[
  {
    "path": ".clang-format",
    "content": "---\nBasedOnStyle: LLVM\nIndentWidth: 4\nTabWidth: 4\nAlwaysBreakTemplateDeclarations: true\nAllowShortFunctionsOnASingleLine: InlineOnly\n#AllowShortLambdasOnASingleLine: Inline\nBreakAfterJavaFieldAnnotations: true\n#BreakBeforeBraces: Linux\nSpaceAfterCStyleCast: true\nIndentCaseLabels: true\nAccessModifierOffset: -4\nBreakBeforeBraces: Custom\nBraceWrapping:\n    AfterNamespace: false\n    AfterClass: false\n    AfterFunction: false\n\nBreakConstructorInitializersBeforeComma: true\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nBinPackParameters: false\nReflowComments: false\nObjCBlockIndentWidth: 4\n---\nLanguage: Cpp\nColumnLimit: 120\nIndentPPDirectives: AfterHash\n---\nLanguage: ObjC\nColumnLimit: 0\n#UseTab: ForIndentation\n---\nLanguage: Java\nColumnLimit: 120\nAllowShortFunctionsOnASingleLine: None\nBreakBeforeBinaryOperators: NonAssignment\n"
  },
  {
    "path": ".dockerignore",
    "content": "# 排除特定文件\noutput/\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [lingol]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n---\n\n## Note:\nAn crash issue will be **ignored & closed** in a week **without logs**.\n\n### The language of MMKV\n\n> e.g. Objective-C, Swift, Java, or Kotlin\n\n\n\n### The version of MMKV\n\n> e.g. v1.2.2  \n> Note: For versions older than the latest version, please upgrade before posting any issue.  \n> We don't have much time for old version tech support.\n\n\n\n### The platform of MMKV\n\n> e.g. iOS or Android\n\n\n\n### The installation of MMKV\n\n> e.g. Cocoapods, Maven, or Git clone\n\n\n\n### What's the issue?\n\n> Post the outputs or screenshots for errors.\n> \n> Explain what you want by example or code **in English**.\n> If you have a crash/OOM/ANR on Android, please provide **symbolize stack traces**. https://developer.android.com/ndk/guides/ndk-stack.html\n\n### What's the log of MMKV when that happened?\n> Your **detail logs**. As much as possible.\n> For how to forward MMKV's log, you can checkout the wiki on each platform.\n> An crash issue will be **ignored & closed** in a week **without logs**.\n"
  },
  {
    "path": ".gitignore",
    "content": "## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n\n## Other\n*.xccheckout\n*.moved-aside\n*.xcuserstate\n*.xcscmblueprint\n\n#python\nvenv\n\n## OS X\n.DS_Store\n\n## Xcode\nDerivedData\n\n## Visual Studio\n.vs\n[Dd]ebug/\n[Rr]elease/\n*.user\n*.VC.opendb\n*.VC.db\nipch/\n\n#cmake\nCMakeCache.txt\nCMakeFiles/\ncmake_install.cmake\nLinux/Makefile\ncmake-build-debug/\ncmake-build-release/\nbuild/\nbuild_*/\ndist/\n*.egg-info/\n.cmake\n\n#CLion\n.idea\n\n#Android Studio\n.cxx\n\npubspec.lock\nPodfile.lock\n\n#docker\noutput/\n\n#swiftpm\n.swiftpm\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"Python/pybind11\"]\n\tpath = Python/pybind11\n\turl = https://github.com/pybind/pybind11.git\n"
  },
  {
    "path": "Android/MMKV/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.idea/caches/build_file_checksums.ser\n.idea/\n"
  },
  {
    "path": "Android/MMKV/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '2.2.20'\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.13.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n\next {\n    minSdkVersion = 23\n    compileSdk = 35\n    targetSdkVersion = compileSdk\n    supportLibVersion = \"25.4.0\"\n    javaVersion = JavaVersion.VERSION_11\n\n    defaultsToPrivateOnly = true\n\n    GROUP = 'com.tencent'\n    VERSION_NAME = \"${VERSION_NAME_PREFIX}${VERSION_NAME_SUFFIX}\"\n\n    POM_PACKAGING = \"pom\"\n    POM_DESCRIPTION = \"MMKV for Android\"\n\n    POM_URL = \"https://github.com/Tencent/MMKV\"\n    POM_SCM_URL = \"https://github.com/Tencent/MMKV\"\n    POM_SCM_CONNECTION=\"scm:git:github.com/Tencent/MMKV.git\"\n    POM_SCM_DEV_CONNECTION=\"scm:git:ssh://git@github.com/Tencent/MMKV.git\"\n\n    POM_ISSUE_URL = 'https://github.com/Tencent/MMKV/issues'\n\n    POM_LICENCE_NAME = \"BSD License\"\n    POM_LICENCE_URL = \"https://opensource.org/licenses/BSD-3-Clause\"\n\n    POM_DEVELOPER_ID = \"Tencent Wechat\"\n    POM_DEVELOPER_NAME = \"Tencent Wechat, Inc.\"\n}\n"
  },
  {
    "path": "Android/MMKV/checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n  Copyright (C) 2016 THL A29 Limited, a Tencent company.\n  Copyright (C) 2014 Square, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<!DOCTYPE module PUBLIC\n    \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n    \"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">\n\n<module name=\"Checker\">\n  <!--module name=\"NewlineAtEndOfFile\"/-->\n  <module name=\"FileLength\"/>\n  <module name=\"FileTabCharacter\"/>\n  <module name=\"SuppressWarningsFilter\"/>\n\n  <!-- Trailing spaces -->\n  <module name=\"RegexpSingleline\">\n    <property name=\"format\" value=\"\\s+$\"/>\n    <property name=\"message\" value=\"Line has trailing spaces.\"/>\n  </module>\n\n  <!-- Space after 'for' and 'if' -->\n  <module name=\"RegexpSingleline\">\n    <property name=\"format\" value=\"^\\s*(for|if)[^ ]\\(\"/>\n    <property name=\"message\" value=\"Space needed before opening parenthesis.\"/>\n  </module>\n\n  <!-- For each spacing -->\n  <module name=\"RegexpSingleline\">\n    <property name=\"format\" value=\"^\\s*for \\(.*?([^ ]:|:[^ ])\"/>\n    <property name=\"message\" value=\"Space needed around ':' character.\"/>\n  </module>\n\n  <module name=\"TreeWalker\">\n    <!-- Checks for Javadoc comments.                     -->\n    <!-- See http://checkstyle.sf.net/config_javadoc.html -->\n    <!--module name=\"JavadocMethod\"/-->\n    <!--module name=\"JavadocType\"/-->\n    <!--module name=\"JavadocVariable\"/-->\n    <!--module name=\"JavadocStyle\"/-->\n\n\n    <!-- Checks for Naming Conventions.                  -->\n    <!-- See http://checkstyle.sf.net/config_naming.html -->\n    <!--<module name=\"ConstantName\"/>-->\n    <module name=\"LocalFinalVariableName\"/>\n    <module name=\"LocalVariableName\"/>\n    <module name=\"MemberName\"/>\n    <module name=\"MethodName\">\n      <property name=\"format\" value=\"^[a-z][a-zA-Z0-9_]*$\"/>\n    </module>\n    <module name=\"PackageName\"/>\n    <module name=\"ParameterName\"/>\n    <module name=\"StaticVariableName\"/>\n    <module name=\"TypeName\">\n      <property name=\"format\" value=\"^[A-Z][a-zA-Z0-9_]*$\"/>\n    </module>\n\n\n    <!-- Checks for imports                              -->\n    <!-- See http://checkstyle.sf.net/config_import.html -->\n    <module name=\"AvoidStarImport\"/>\n    <module name=\"IllegalImport\"/>\n    <module name=\"RedundantImport\"/>\n    <module name=\"UnusedImports\"/>\n\n\n    <!-- Checks for Size Violations.                    -->\n    <!-- See http://checkstyle.sf.net/config_sizes.html -->\n    <!--module name=\"LineLength\"-->\n      <!--property name=\"max\" value=\"100\"/-->\n    <!--/module-->\n    <!--module name=\"MethodLength\"/-->\n    <!--<module name=\"ParameterNumber\"/>-->\n\n    <!-- Checks for whitespace                               -->\n    <!-- See http://checkstyle.sf.net/config_whitespace.html -->\n    <module name=\"GenericWhitespace\"/>\n    <module name=\"EmptyForIteratorPad\"/>\n    <module name=\"MethodParamPad\"/>\n\n\n    <module name=\"NoWhitespaceAfter\"/>\n    <module name=\"NoWhitespaceBefore\"/>\n    <module name=\"OperatorWrap\"/>\n    <module name=\"ParenPad\"/>\n    <module name=\"TypecastParenPad\"/>\n    <module name=\"WhitespaceAfter\"/>\n    <module name=\"WhitespaceAround\"/>\n\n\n    <!-- Modifier Checks                                    -->\n    <!-- See http://checkstyle.sf.net/config_modifiers.html -->\n    <!--module name=\"ModifierOrder\"/-->\n    <module name=\"RedundantModifier\"/>\n\n\n    <!-- Checks for blocks. You know, those {}'s         -->\n    <!-- See http://checkstyle.sf.net/config_blocks.html -->\n    <!--module name=\"AvoidNestedBlocks\"/-->\n    <!--module name=\"EmptyBlock\"/-->\n    <module name=\"LeftCurly\"/>\n    <!--allow single line-->\n    <module name=\"NeedBraces\">\n      <property name=\"allowSingleLineStatement\" value=\"true\"/>\n    </module>\n\n    <module name=\"RightCurly\"/>\n\n\n    <!-- Checks for common coding problems               -->\n    <!-- See http://checkstyle.sf.net/config_coding.html -->\n    <!--module name=\"AvoidInlineConditionals\"/-->\n    <module name=\"CovariantEquals\"/>\n    <module name=\"EmptyStatement\"/>\n    <!--module name=\"EqualsAvoidNull\"/-->\n    <!--<module name=\"EqualsHashCode\"/>-->\n    <!--module name=\"HiddenField\"/-->\n    <module name=\"IllegalInstantiation\"/>\n    <!--<module name=\"InnerAssignment\"/>-->\n    <!--module name=\"MagicNumber\"/-->\n    <!--<module name=\"MissingSwitchDefault\"/>-->\n    <!--<module name=\"RedundantThrows\"/>-->\n    <module name=\"SimplifyBooleanExpression\"/>\n    <module name=\"SimplifyBooleanReturn\"/>\n\n    <!-- Checks for class design                         -->\n    <!-- See http://checkstyle.sf.net/config_design.html -->\n    <!--module name=\"DesignForExtension\"/-->\n    <!--module name=\"FinalClass\"/-->\n    <!--<module name=\"HideUtilityClassConstructor\"/>-->\n    <!--module name=\"InterfaceIsType\"/-->\n    <!--module name=\"VisibilityModifier\"/-->\n\n\n    <!-- Miscellaneous other checks.                   -->\n    <!-- See http://checkstyle.sf.net/config_misc.html -->\n    <module name=\"ArrayTypeStyle\"/>\n    <!--module name=\"FinalParameters\"/-->\n    <!--<module name=\"TodoComment\"/>-->\n    <module name=\"UpperEll\"/>\n    <module name=\"SuppressWarningsHolder\"/>\n  </module>\n</module>"
  },
  {
    "path": "Android/MMKV/gradle/android-publish-private.gradle",
    "content": "// OSS_ANDROID_TEMPLATE_FILE_HEADER\n/**\n * New android-publish gradle script (for private use only, without bintray support)\n *\n * 1. Multiple flavors support.\n * 2. Provide PRIVATE_RELEASE_REPOSITORY_URL and PRIVATE_SNAPSHOT_REPOSITORY_URL with gradle.properties or local.properties.\n * 3. Provide PRIVATE_REPOSITORY_USERNAME and PRIVATE_REPOSITORY_PASSWORD if needed.\n */\napply plugin: 'digital.wup.android-maven-publish'\napply plugin: 'signing'\n\ndef isReleaseBuild() {\n    return version.contains(\"SNAPSHOT\") == false\n}\n\ndef getReleaseRepositoryUrl() {\n    return hasProperty('PRIVATE_RELEASE_REPOSITORY_URL') ? PRIVATE_RELEASE_REPOSITORY_URL : readPropertyFromLocalProperties('PRIVATE_RELEASE_REPOSITORY_URL')\n}\n\ndef getSnapshotRepositoryUrl() {\n    return hasProperty('PRIVATE_SNAPSHOT_REPOSITORY_URL') ? PRIVATE_SNAPSHOT_REPOSITORY_URL : readPropertyFromLocalProperties('PRIVATE_SNAPSHOT_REPOSITORY_URL')\n}\n\ndef readPropertyFromLocalProperties(String key) {\n    Properties properties = new Properties()\n    try {\n        properties.load(project.rootProject.file('local.properties').newDataInputStream())\n    } catch (Exception e) {\n        println(\"load local properties failed msg:${e.message}\")\n    }\n    return properties.getProperty(key)\n}\n\ndef getRepositoryUsername() {\n    return hasProperty('PRIVATE_REPOSITORY_USERNAME') ? PRIVATE_REPOSITORY_USERNAME : readPropertyFromLocalProperties('PRIVATE_REPOSITORY_USERNAME')\n}\n\ndef getRepositoryPassword() {\n    return hasProperty('PRIVATE_REPOSITORY_PASSWORD') ? PRIVATE_REPOSITORY_PASSWORD : readPropertyFromLocalProperties('PRIVATE_REPOSITORY_PASSWORD')\n}\n\ndef pomConfig = {\n    scm {\n        url POM_SCM_URL\n    }\n\n    licenses {\n        license {\n            name POM_LICENCE_NAME\n            url POM_LICENCE_URL\n            distribution POM_LICENCE_DIST\n        }\n    }\n\n    developers {\n        developer {\n            id POM_DEVELOPER_ID\n            name POM_DEVELOPER_NAME\n        }\n    }\n}\n\nandroid.libraryVariants.all { variant ->\n    // Skipped debug variants\n    if (variant.buildType.name == \"debug\") {\n        return\n    }\n\n    def hasFlavors = !variant.flavorName.isEmpty()\n\n    def artifactIdSuffix = hasFlavors ? variant.flavorName.replace('_', '-').capitalize() : ''\n    variant.productFlavors.each { flavor ->\n        def flavorArtifactIdSuffix = flavor.ext.has('artifactIdSuffix') ? flavor.ext.artifactIdSuffix : flavor.name\n        if (!flavorArtifactIdSuffix.isEmpty()) {\n            artifactIdSuffix = artifactIdSuffix.replace(flavor.name.capitalize(), \"-${flavorArtifactIdSuffix}\")\n        } else {\n            artifactIdSuffix = artifactIdSuffix.replace(flavor.name.capitalize(), \"\")\n        }\n    }\n    if (!artifactIdSuffix.isEmpty() && !artifactIdSuffix.startsWith('-')) {\n        artifactIdSuffix = '-' + artifactIdSuffix\n    }\n\n    def curArtifactId = \"${POM_ARTIFACT_ID}${artifactIdSuffix}\"\n\n    /**\n     * Includes\n     */\n    def sourceDirs = variant.sourceSets.collect {\n        it.javaDirectories // TODO: kotlin sources\n    }\n    def javadoc = task(\"${variant.name}Javadoc\", type: Javadoc) {\n        source = variant.javaCompile.source // TODO: deprecated\n        options.encoding = 'utf-8'\n        destinationDir = file(\"${buildDir}/docs/javadoc${hasFlavors ? artifactIdSuffix : \"\"}\")\n        ext.androidJar = \"${android.sdkDirectory}/platforms/${android.compileSdk}/android.jar\"\n        classpath += files(ext.androidJar)\n        classpath += files(configurations.compile)\n        exclude '**/BuildConfig.java'\n        exclude '**/R.java'\n        failOnError false\n    }\n    def javadocJar = task(\"${variant.name}JavadocJar\", type: Jar, dependsOn: javadoc) {\n        classifier = 'javadoc'\n        from javadoc.destinationDir\n    }\n    def sourcesJar = task(\"${variant.name}SourcesJar\", type: Jar) {\n        classifier = 'sources'\n        from sourceDirs\n    }\n    def jniSymbolsJar = task(\"${variant.name}SymbolJar\", type: Jar, dependsOn: 'build') {\n        classifier = \"so-symbols\"\n        boolean hasNativeBuildTask = false\n        tasks.each { task ->\n            if (task.getName().startsWith(\"externalNativeBuild\")) {\n                hasNativeBuildTask = true\n            }\n        }\n\n        if (!hasNativeBuildTask) {\n            return\n        }\n\n        if (hasFlavors) {\n            variant.productFlavors.each { flavor ->\n                from file(\"build/intermediates/cmake/${flavor.name}/release/obj/\")\n            }\n        } else {\n            from file(\"build/intermediates/cmake/release/obj/\")\n        }\n\n    }\n\n    // require gradle 4.x +\n    if (GradleVersion.current() < GradleVersion.version('4.0')) {\n        throw new GradleException('android-publish.gradle need Gradle 4.0 or newer')\n    }\n\n    def publicationName = \"component${variant.name.capitalize()}\"\n\n    // Declare publications\n    publishing.publications {\n        \"$publicationName\"(MavenPublication) {\n            artifactId curArtifactId\n            groupId groupId\n            version version\n\n            from components.findByName(\"android${variant.name.capitalize()}\")\n\n            artifact javadocJar\n            artifact jniSymbolsJar\n        }\n    }\n}\n\n/**\n * Setup custom maven repo\n */\npublishing.repositories {\n    maven {\n        url \"${isReleaseBuild() ? getReleaseRepositoryUrl() : getSnapshotRepositoryUrl()}\"\n        credentials {\n            username \"${getRepositoryUsername()}\"\n            password \"${getRepositoryPassword()}\"\n        }\n    }\n}\n\nif (JavaVersion.current().isJava8Compatible()) {\n    allprojects {\n        tasks.withType(Javadoc) {\n            options.addStringOption('Xdoclint:none', '-quiet')\n        }\n    }\n}\n\ntask buildAndPublishToLocalMaven(type: Copy, dependsOn: ['build', 'publishToMavenLocal']) {\n    group = 'publishing'\n\n    // save artifacts files to artifacts folder\n    from configurations.archives.allArtifacts.files\n    into \"${rootProject.buildDir}/outputs/artifacts/\"\n    rename { String fileName ->\n        fileName.replace(\"release.aar\", \"${version}.aar\")\n    }\n\n    doLast {\n        println \"* published to maven local: ${project.group}:${project.name}:${project.version}\"\n    }\n}\n\ntask buildAndPublishRepo(type: Copy, dependsOn: ['build', 'publish']) {\n    group = \"publishing\"\n\n    // save artifacts files to artifacts folder\n    from configurations.archives.allArtifacts.files\n    into \"${rootProject.buildDir}/outputs/artifacts/\"\n    rename { String fileName ->\n        fileName.replace(\"release.aar\", \"${version}.aar\")\n    }\n\n    doLast {\n        println \"* published to repo: ${project.group}:${project.name}:${project.version}\"\n    }\n}\n\napply from: rootProject.file('gradle/check.gradle')"
  },
  {
    "path": "Android/MMKV/gradle/android-publish.gradle",
    "content": "import java.util.regex.Matcher\nimport java.util.regex.Pattern\n\n// OSS_ANDROID_TEMPLATE_FILE_HEADER\n/**\n * New android-publish gradle script\n *\n * 1. Multiple flavors support.\n * 2. Provide RELEASE_REPOSITORY_URL and SNAPSHOT_REPOSITORY_URL with gradle.properties or local.properties.\n * 3. Provide REPOSITORY_USERNAME and REPOSITORY_PASSWORD if needed.\n */\napply plugin: 'maven-publish'\napply plugin: 'signing'\n\ndef isReleaseBuild() {\n    return version.contains(\"SNAPSHOT\") == false\n}\n\ndef getReleaseRepositoryUrl() {\n    return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : readPropertyFromLocalProperties('RELEASE_REPOSITORY_URL')\n}\n\ndef getSnapshotRepositoryUrl() {\n    return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : readPropertyFromLocalProperties('SNAPSHOT_REPOSITORY_URL')\n}\n\ndef readPropertyFromLocalProperties(String key) {\n    Properties properties = new Properties()\n    try {\n        properties.load(project.rootProject.file('local.properties').newDataInputStream())\n    } catch (Exception e) {\n        println(\"load local properties failed msg:${e.message}\")\n    }\n    return properties.getProperty(key)\n}\n\ndef getRepositoryUsername() {\n    return hasProperty('REPOSITORY_USERNAME') ? REPOSITORY_USERNAME : readPropertyFromLocalProperties('REPOSITORY_USERNAME')\n}\n\ndef getRepositoryPassword() {\n    return hasProperty('REPOSITORY_PASSWORD') ? REPOSITORY_PASSWORD : readPropertyFromLocalProperties('REPOSITORY_PASSWORD')\n}\n\ndef curArtifactId\n\nandroid.libraryVariants.all { variant ->\n    // Skipped debug variants\n    if (variant.buildType.name == \"debug\") {\n        return\n    }\n    // Skipped non publishing variants\n//    println('android.defaultPublishConfig=' + android.defaultPublishConfig)\n//    println('variant.flavorName=' + variant.flavorName)\n//    if (!android.defaultPublishConfig.startsWith(variant.flavorName)) {\n//        return\n//    }\n\n    def hasFlavors = !variant.flavorName.isEmpty()\n\n    def artifactIdSuffix = hasFlavors ? variant.flavorName.replace('_', '-').capitalize() : ''\n    variant.productFlavors.each { flavor ->\n        def flavorArtifactIdSuffix = flavor.ext.has('artifactIdSuffix') ? flavor.ext.artifactIdSuffix : flavor.name\n        if (!flavorArtifactIdSuffix.isEmpty()) {\n            artifactIdSuffix = artifactIdSuffix.replace(flavor.name.capitalize(), \"-${flavorArtifactIdSuffix}\")\n        } else {\n            artifactIdSuffix = artifactIdSuffix.replace(flavor.name.capitalize(), \"\")\n        }\n    }\n    if (!artifactIdSuffix.isEmpty() && !artifactIdSuffix.startsWith('-')) {\n        artifactIdSuffix = '-' + artifactIdSuffix\n    }\n\n    curArtifactId = \"${POM_ARTIFACT_ID}${artifactIdSuffix}\"\n\n    def jniJar = task(\"${variant.name}JniJar\", type: Jar, dependsOn: 'build') {\n    }\n    def javadoc = task(\"${variant.name}Javadoc\", type: Javadoc) {\n        source = variant.javaCompileProvider.get().source\n        options.encoding = 'utf-8'\n        destinationDir = file(\"${buildDir}/docs/javadoc${hasFlavors ? artifactIdSuffix : \"\"}\")\n        ext.androidJar = \"${android.sdkDirectory}/platforms/${android.compileSdk}/android.jar\"\n        classpath += files(ext.androidJar)\n        classpath += files(variant.javaCompileProvider.get().classpath)\n        classpath += files(configurations.javadocDeps)\n        exclude '**/BuildConfig.java'\n        exclude '**/R.java'\n        failOnError = false\n    }\n    def javadocJar = task(\"${variant.name}JavadocJar\", type: Jar, dependsOn: javadoc) {\n        archiveClassifier = 'javadoc'\n        from javadoc.destinationDir\n    }\n    def jniSymbolsJar = task(\"${variant.name}SymbolJar\", type: Jar, dependsOn: 'build') {\n        archiveClassifier = \"so-symbols\"\n        boolean hasNativeBuildTask = false\n        tasks.each { task ->\n            if (task.getName().startsWith(\"externalNativeBuild\")) {\n                hasNativeBuildTask = true\n            }\n        }\n\n        if (!hasNativeBuildTask) {\n            return\n        }\n\n        if (hasFlavors) {\n            variant.productFlavors.each { flavor ->\n                from file(\"build/intermediates/cmake/${flavor.name}Release/obj/\")\n            }\n        } else {\n            from file(\"build/intermediates/cmake/release/obj/\")\n        }\n    }\n    // Custom task to package headers into a ZIP file\n    def headerZip = task(\"${variant.name}Header\", type: Zip, dependsOn: 'build') {\n        archiveClassifier = \"header\"\n        from('../../../Core/include') {\n            exclude '*/MemoryFile.h'\n            exclude '*/MMKVLog.h'\n        }\n        archiveFileName = 'headers.zip'\n        destinationDirectory = file(\"$buildDir/outputs/headers\")\n    }\n\n    def publicationName = \"component${variant.name.capitalize()}\"\n\n    // Declare publications\n    publishing.publications {\n        \"$publicationName\"(MavenPublication) {\n            groupId groupId\n            artifactId curArtifactId\n            version version\n\n            from components.findByName(\"android${variant.name.capitalize()}\")\n\n            if (hasFlavors) {\n                variant.productFlavors.each { flavor ->\n                    artifact(\"build/outputs/aar/${project.getName()}-${flavor.name}-release.aar\")\n                }\n            } else {\n                artifact(\"build/outputs/aar/${project.getName()}-release.aar\")\n            }\n            artifact javadocJar\n            artifact jniSymbolsJar\n            artifact headerZip\n\n            pom {\n                name = 'MMKV'\n                description = POM_DESCRIPTION\n                url = POM_URL\n                licenses {\n                    license {\n                        name = POM_LICENCE_NAME\n                        url = POM_LICENCE_URL\n                    }\n                }\n\n                developers {\n                    developer {\n                        id = POM_DEVELOPER_ID\n                        name = POM_DEVELOPER_NAME\n                    }\n                }\n\n                scm {\n                    url = POM_SCM_URL\n                    connection = POM_SCM_CONNECTION\n                    developerConnection = POM_SCM_DEV_CONNECTION\n                }\n\n                // write the dependency of the library into pom\n                withXml {\n                    def dependenciesNode = asNode().appendNode('dependencies')\n                    project.configurations.all { configuration ->\n                        def name = configuration.name\n                        if (name != \"implementation\" && name != \"compile\" && name != \"api\") {\n                            return\n                        }\n                        configuration.dependencies.each {\n                            if (it.name == \"unspecified\") {\n                                return\n                            }\n                            def dependencyNode = dependenciesNode.appendNode('dependency')\n                            dependencyNode.appendNode('groupId', it.group)\n                            dependencyNode.appendNode('artifactId', it.name)\n                            dependencyNode.appendNode('version', it.version)\n                            if (name == \"api\" || name == \"compile\") {\n                                dependencyNode.appendNode(\"scope\", \"compile\")\n                            } else {\n                                dependencyNode.appendNode(\"scope\", \"runtime\")\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nsigning {\n    sign publishing.publications\n}\n\npublishing.repositories {\n    maven {\n        url \"${isReleaseBuild() ? getReleaseRepositoryUrl() : getSnapshotRepositoryUrl()}\"\n        credentials {\n            username \"${getRepositoryUsername()}\"\n            password \"${getRepositoryPassword()}\"\n        }\n    }\n}\n\nif (JavaVersion.current().isJava8Compatible()) {\n    allprojects {\n        tasks.withType(Javadoc) {\n            options.addStringOption('Xdoclint:none', '-quiet')\n        }\n    }\n}\n\ntask buildAndPublishToLocalMaven(type: Copy, dependsOn: ['build', 'publishToMavenLocal']) {\n    group = 'publishing'\n\n    // save artifacts files to artifacts folder\n    from \"$buildDir/outputs/aar\"\n    include '*.aar'\n    into \"${rootProject.buildDir}/outputs/artifacts\"\n\n    rename { String fileName ->\n        fileName.replace(\"release.aar\", \"${version}.aar\")\n    }\n\n    doLast {\n        println \"* published to maven local: ${project.group}:${project.name}:${project.version}\"\n    }\n}\n\ntask buildAndPublishRepo(type: Copy, dependsOn: ['build', 'publish']) {\n    group = \"publishing\"\n\n    // save artifacts files to artifacts folder\n    from configurations.archives.allArtifacts.files\n    into \"${rootProject.buildDir}/outputs/artifacts/\"\n    rename { String fileName ->\n        fileName.replace(\"release.aar\", \"${version}.aar\")\n    }\n\n    doLast {\n        println \"* published to repo: ${project.group}:${project.name}:${project.version}\"\n    }\n}\n\n//tasks.register('closeCentralRepository', Exec) {\n//    // This task will only run if the 'centralUsername' property exists\n////    onlyIf { project.hasProperty('centralUsername') && project.hasProperty('centralToken') }\n//\n//    // Define your namespace here\n//    def namespace = \"com.tencent\" // <-- IMPORTANT: Replace with your actual namespace\n//\n//    group = \"publishing\"\n//    description = \"Closes the repository on the Central Publisher Portal.\"\n//\n//    commandLine \"curl\", \"-X\", \"POST\",\n//            \"-u\", \"${getRepositoryUsername()}:${getRepositoryPassword()}\",\n//            \"https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/${namespace}\"\n//}\n//\n//// Find your specific publish task and make it trigger the new task\n//// Replace 'publish' with the actual name of your main publishing task if it's different.\n//tasks.named('publish') {\n//    finalizedBy 'closeCentralRepository'\n//}\n\napply from: rootProject.file('gradle/check.gradle')"
  },
  {
    "path": "Android/MMKV/gradle/build_library.gradle",
    "content": "// OSS_ANDROID_TEMPLATE_FILE_HEADER\n/**\n * Optional android libraries gradle file\n *\n * Simply apply from: rootProject.file('gradle/build_library.gradle') if needed\n *\n */\napply plugin: 'com.android.library'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n\n    defaultConfig {\n        minSdkVersion rootProject.ext.minSdkVersion\n        targetSdkVersion rootProject.ext.targetSdkVersion\n    }\n\n    signingConfigs {\n        configDebug {\n            keyAlias 'key0'\n            keyPassword 'mmkv.wxg'\n            storeFile rootProject.file('debug.keystore')\n            storePassword 'mmkv.wxg'\n        }\n\n        configRelease {\n            keyAlias 'key0'\n            keyPassword 'mmkv.wxg'\n            storeFile rootProject.file('debug.keystore')\n            storePassword 'mmkv.wxg'\n        }\n    }\n\n    buildTypes {\n        debug {\n            debuggable true\n            signingConfig signingConfigs.configDebug\n            minifyEnabled false\n        }\n        release {\n            signingConfig signingConfigs.configRelease\n            minifyEnabled true\n            proguardFile getDefaultProguardFile('proguard-android.txt')\n            proguardFile project.file('proguard-rules-android-lib.pro') // NOTE: default rules for android library if this file exists\n        }\n    }\n}\n\n// Publication\nversion = rootProject.ext.VERSION_NAME\ngroup = rootProject.ext.GROUP\n\n//def check_private_only = project.hasProperty('PRIVATE_ONLY') ? PRIVATE_ONLY.toBoolean() : rootProject.ext.defaultsToPrivateOnly // Dangerous, turn on for default\n//\n//if (check_private_only) {\n//    // Publish to private only repositories\n//    apply from: rootProject.file('gradle/android-publish-private.gradle')\n//\n//} else {\n//    // Publish to public repositories\n//    apply from: rootProject.file('gradle/android-publish.gradle')\n//}\n"
  },
  {
    "path": "Android/MMKV/gradle/check.gradle",
    "content": "// OSS_ANDROID_TEMPLATE_FILE_HEADER\n\napply plugin: 'checkstyle'\n\n\ncheckstyle {\n    configFile rootProject.file('checkstyle.xml')\n    toolVersion '6.19'\n    ignoreFailures false\n    showViolations true\n}\n\ntask('checkstyle', type: Checkstyle) {\n    source 'src/main/java'\n    include '**/*.java'\n    classpath = files()\n}\n\ncheck.dependsOn('checkstyle')\n\n\n//apply plugin: 'pmd'\n//\n//pmd {\n//    toolVersion '5.4.0'\n//}\n//\n//task pmd(type: Pmd) {\n//    targetJdk = TargetJdk.VERSION_1_7\n//\n//    description 'Run pmd'\n//    group 'verification'\n//\n//    // If ruleSets is not empty, it seems to contain some\n//    // defaults which override rules in the ruleset file...\n//    ruleSets = []\n//    ruleSetFiles = rootProject.files('pmd-ruleset.xml')\n//    source = fileTree('src/main/java')\n//    ignoreFailures = false\n//\n//    reports {\n//        xml.enabled = false\n//        html.enabled = true\n//    }\n//}\n//\n//check.dependsOn('pmd')\n\n//apply plugin: 'findbugs'\n//\n//def classTree = 'build/intermediates/classes/debug'\n//\n//if (project.plugins.hasPlugin('java')) {\n//    classTree = 'build/classes'\n//}\n//task findbugs(type: FindBugs) {\n//\n//    description 'Run findbugs'\n//    group 'verification'\n//\n//    classes = fileTree(classTree)\n//    source = fileTree('src/main/java/')\n//    classpath = files()\n//\n//    effort = 'default'\n//\n//    excludeFilter = rootProject.file(\"findbugs-exclude.xml\")\n//\n//    reports {\n//        xml.enabled = false\n//        html.enabled = true\n//    }\n//    ignoreFailures = true\n//}\n\n//check.dependsOn('findbugs')"
  },
  {
    "path": "Android/MMKV/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Oct 16 21:16:39 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-bin.zip\n"
  },
  {
    "path": "Android/MMKV/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\nandroid.enableJetifier=true\nandroid.useAndroidX=true\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\nVERSION_NAME_PREFIX=2.4.0\n#VERSION_NAME_SUFFIX=-SNAPSHOT\nVERSION_NAME_SUFFIX=\n\nRELEASE_REPOSITORY_URL=https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2\nSNAPSHOT_REPOSITORY_URL=https://ossrh-staging-api.central.sonatype.com/content/repositories/snapshots\nandroid.defaults.buildfeatures.buildconfig=true\nandroid.nonTransitiveRClass=false\nandroid.nonFinalResIds=false\n"
  },
  {
    "path": "Android/MMKV/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "Android/MMKV/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "Android/MMKV/mmkv/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "Android/MMKV/mmkv/CMakeLists.txt",
    "content": "# For more information about using CMake with Android Studio, read the\n# documentation: https://d.android.com/studio/projects/add-native-code.html\n\nproject(mmkv)\n\n# Sets the minimum version of CMake required to build the native library.\ncmake_minimum_required(VERSION 3.10.0)\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\nadd_subdirectory(../../../Core Core)\n\nadd_library( mmkv\n\n             # Sets the library as a shared library.\n             SHARED\n\n             # Provides a relative path to your source file(s).\n             src/main/cpp/native-bridge.cpp\n             src/main/cpp/flutter-bridge.cpp\n        )\n\nset_target_properties(mmkv PROPERTIES\n        CXX_STANDARD 20\n        CXX_EXTENSIONS OFF\n        POSITION_INDEPENDENT_CODE ON\n        )\n\n# Searches for a specified prebuilt library and stores the path as a\n# variable. Because CMake includes system libraries in the search path by\n# default, you only need to specify the name of the public NDK library\n# you want to add. CMake verifies that the library exists before\n# completing its build.\n\nfind_library( # Sets the name of the path variable.\n              log-lib\n\n              # Specifies the name of the NDK library that\n              # you want CMake to locate.\n              log )\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\ntarget_link_libraries( mmkv\n\n                       # Links the target library to the log library\n                       # included in the NDK.\n                       ${log-lib}\n                       core\n#                       aes\n)\n"
  },
  {
    "path": "Android/MMKV/mmkv/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'maven-publish'\n    id 'signing'\n//    id 'org.jetbrains.dokka' version '1.4.32' // For Kotlin projects\n}\napply from: rootProject.file('gradle/build_library.gradle')\n\nandroid {\n    namespace \"com.tencent.mmkv\"\n    compileSdk rootProject.ext.compileSdk\n    publishing {\n        multipleVariants(\"DefaultCppRelease\") {\n            includeBuildTypeValues('release')\n            includeFlavorDimensionAndValues('stl_mode', 'DefaultCpp')\n            withJavadocJar()\n        }\n        multipleVariants(\"StaticCppRelease\") {\n            includeBuildTypeValues('release')\n            includeFlavorDimensionAndValues('stl_mode', 'StaticCpp')\n            withJavadocJar()\n        }\n        multipleVariants(\"SharedCppRelease\") {\n            includeBuildTypeValues('release')\n            includeFlavorDimensionAndValues('stl_mode', 'SharedCpp')\n            withJavadocJar()\n        }\n    }\n\n    defaultConfig {\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        externalNativeBuild {\n            cmake {\n                abiFilters 'arm64-v8a', 'x86_64'\n            }\n        }\n        ndkVersion \"28.1.13356709\"\n        minSdkVersion rootProject.ext.minSdkVersion\n        targetSdkVersion rootProject.ext.targetSdkVersion\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            externalNativeBuild {\n                cmake {\n                    cppFlags \"-fvisibility=hidden\", \"-funwind-tables\", \"-fasynchronous-unwind-tables\", \"-O2\"\n                }\n            }\n            consumerProguardFiles 'proguard-rules.pro'\n//            debuggable true\n//            jniDebuggable true\n        }\n        debug {\n            jniDebuggable true\n        }\n    }\n    externalNativeBuild {\n        cmake {\n            path \"CMakeLists.txt\"\n            version \"3.18.0+\"\n        }\n    }\n    packagingOptions {\n        jniLibs {\n            useLegacyPackaging true\n        }\n    }\n    flavorDimensions = [\"stl_mode\"]\n    productFlavors {\n        DefaultCpp {\n            dimension \"stl_mode\"\n            ext.artifactIdSuffix = ''\n            externalNativeBuild {\n                cmake {\n                    arguments = [\"-DANDROID_STL=c++_static\"]\n                    cppFlags \"-DMMKV_STL_SHARED=0\"\n                }\n            }\n        }\n        StaticCpp {\n            dimension \"stl_mode\"\n            ext.artifactIdSuffix = 'static'\n            externalNativeBuild {\n                cmake {\n                    arguments = [\"-DANDROID_STL=c++_static\"]\n                    cppFlags \"-DMMKV_STL_SHARED=0\"\n                }\n            }\n            // not working\n//            packagingOptions {\n//                exclude \"prefab/modules/**\"\n//            }\n        }\n        SharedCpp {\n            dimension \"stl_mode\"\n            ext.artifactIdSuffix = 'shared'\n            externalNativeBuild {\n                cmake {\n                    arguments = [\"-DANDROID_STL=c++_shared\"]\n                    cppFlags \"-DMMKV_STL_SHARED=1\"\n                }\n            }\n        }\n    }\n    buildFeatures {\n        prefab true\n        prefabPublishing true\n        aidl true\n    }\n    prefab {\n        mmkv {\n            headers \"../../../Core/include\"\n        }\n    }\n}\n\nafterEvaluate {\n    android.libraryVariants.configureEach { variant ->\n        if (variant.buildType.name == \"release\") {\n            def abiFilters = ['arm64-v8a', 'x86_64']\n\n            def strippedLibsDir = \"$buildDir/intermediates/stripped_native_libs/${variant.name}/strip${variant.name}DebugSymbols/out/lib\"\n            // Create per-variant copy task\n            tasks.register(\"syncStrippedLibsToPrefab${variant.name.capitalize()}\", Copy) {\n                from(strippedLibsDir) {\n                    include \"**/libmmkv.so\"\n                }\n                into \"$buildDir/intermediates/prefab_package/${variant.name.capitalize()}/prefab/modules/mmkv/libs\"\n                eachFile {\n//                    println('hhh before:' + path)\n                    for (abi in abiFilters) {\n                        if (path.contains(\"${abi}/\")) {\n                            path = path.replace(\"${abi}/\", \"android.${abi}/\")\n                            break\n                        }\n                    }\n//                    println('hhh after:' + path)\n                }\n\n                // Hook into build process\n                dependsOn(\"strip${variant.name.capitalize()}DebugSymbols\")\n            }\n\n            // Hook into build process\n            tasks.named(\"externalNativeBuild${variant.name.capitalize()}\") {\n                finalizedBy(\"syncStrippedLibsToPrefab${variant.name.capitalize()}\")\n            }\n            tasks.named(\"bundle${variant.name.capitalize()}Aar\") {\n                dependsOn(\"syncStrippedLibsToPrefab${variant.name.capitalize()}\")\n            }\n            tasks.named(\"bundle${variant.name.capitalize()}LocalLintAar\") {\n                dependsOn(\"syncStrippedLibsToPrefab${variant.name.capitalize()}\")\n            }\n\n            // Generate combined artifact suffix from all flavor dimensions\n            def flavorSuffixes = variant.productFlavors.collect { it.ext.artifactIdSuffix }\n            def artifactIdSuffix = flavorSuffixes.join(\"-\")\n            def fullArtifactId = \"${POM_ARTIFACT_ID}${artifactIdSuffix ? \"-$artifactIdSuffix\" : \"\"}\"\n\n            // 1. Find the task that merges native libs (unstripped).\n            //    Name pattern is typically: mergeReleaseNativeLibs or mergeFlavorReleaseNativeLibs\n            def mergeTaskName = \"merge${variant.name.capitalize()}NativeLibs\"\n            def mergeTask = tasks.findByName(mergeTaskName)\n\n            if (!mergeTask) {\n                println \"Warning: Could not find Native Merge task: $mergeTaskName\"\n                return // Skip creating the symbols jar if we can't find the source\n            }\n\n            // Create debug symbols task\n            def symbolsTask = tasks.register(\"bundleSymbols${variant.name.capitalize()}\", Jar) {\n                // 2. FIX: Don't use a hardcoded path string.\n                //    Use the output files of the merge task directly.\n                from(mergeTask.outputs.files)\n\n                // 3. Ensure we only grab the .so files (the merge task might output a tree structure like /lib/arm64/...)\n                include '**/*.so'\n\n                archiveClassifier.set(\"so-symbols\")\n                destinationDirectory.set(file(\"$buildDir/intermediates/so-symbols/${variant.name}\"))\n                dependsOn mergeTask\n            }\n\n            // Create publication per variant\n            publishing.publications.create(variant.name, MavenPublication) {\n                from components.findByName(variant.name)\n\n                groupId = group\n                artifactId = fullArtifactId\n                version = version\n\n                // Add debug symbols\n                artifact symbolsTask\n\n                // Configure POM\n                pom {\n                    name = 'MMKV'\n                    description = POM_DESCRIPTION\n                    url = POM_URL\n                    licenses {\n                        license {\n                            name = POM_LICENCE_NAME\n                            url = POM_LICENCE_URL\n                        }\n                    }\n\n                    developers {\n                        developer {\n                            id = POM_DEVELOPER_ID\n                            name = POM_DEVELOPER_NAME\n                        }\n                    }\n\n                    scm {\n                        url = POM_SCM_URL\n                        connection = POM_SCM_CONNECTION\n                        developerConnection = POM_SCM_DEV_CONNECTION\n                    }\n                }\n            }\n        }\n    }\n\n    // Configure repositories and signing\n    publishing.repositories {\n        maven {\n            name = \"MavenCentral\"\n            url = RELEASE_REPOSITORY_URL\n            credentials {\n                username = REPOSITORY_USERNAME\n                password = REPOSITORY_PASSWORD\n            }\n        }\n    }\n\n    signing {\n        // Ensure signing only targets publications that were successfully created\n        sign publishing.publications.matching {\n            it.name in android.libraryVariants.collect { v -> v.name }\n        }\n    }\n}\n\nconfigurations {\n    javadocDeps\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'androidx.annotation:annotation:1.9.1'\n    javadocDeps 'androidx.annotation:annotation:1.9.1'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.7.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'\n\n    compileOnly project(':mmkvannotation')\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/gradle.properties",
    "content": "POM_ARTIFACT_ID=mmkv\nPRIVATE_ONLY=false\n"
  },
  {
    "path": "Android/MMKV/mmkv/proguard-rules.pro",
    "content": "# Keep all native methods, their classes and any classes in their descriptors\n-keepclasseswithmembers,includedescriptorclasses class com.tencent.mmkv.** {\n    native <methods>;\n    long nativeHandle;\n    private static *** onMMKVCRCCheckFail(***);\n    private static *** onMMKVFileLengthError(***);\n    private static *** mmkvLogImp(...);\n    private static *** onContentChangedByOuterProcess(***);\n    private static *** onMMKVContentLoadSuccessfully(***);\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/androidTest/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application>\n        <service\n            android:name=\".MMKVTestService\"\n            android:enabled=\"true\"\n            android:exported=\"false\"\n            android:process=\":mmkvtest_svr\">\n        </service>\n    </application>\n</manifest>\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTest.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport static org.junit.Assert.*;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.SystemClock;\nimport androidx.test.InstrumentationRegistry;\nimport java.util.HashSet;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\npublic class MMKVTest {\n\n    static MMKV mmkv;\n    static final String KeyNotExist = \"Key_Not_Exist\";\n    static final float Delta = 0.000001f;\n\n    @BeforeClass\n    public static void setUp() throws Exception {\n        Context appContext = InstrumentationRegistry.getTargetContext();\n        MMKV.initialize(appContext);\n\n        mmkv = MMKV.mmkvWithID(\"unitTest\", MMKV.SINGLE_PROCESS_MODE, \"UnitTestCryptKey\");\n    }\n\n    @AfterClass\n    public static void tearDown() throws Exception {\n        mmkv.clearAll();\n    }\n\n    @Test\n    public void testBool() {\n        boolean ret = mmkv.encode(\"bool\", true);\n        assertEquals(ret, true);\n\n        boolean value = mmkv.decodeBool(\"bool\");\n        assertEquals(value, true);\n\n        value = mmkv.decodeBool(KeyNotExist);\n        assertEquals(value, false);\n\n        value = mmkv.decodeBool(KeyNotExist, true);\n        assertEquals(value, true);\n    }\n\n    @Test\n    public void testInt() {\n        boolean ret = mmkv.encode(\"int\", Integer.MAX_VALUE);\n        assertEquals(ret, true);\n\n        int value = mmkv.decodeInt(\"int\");\n        assertEquals(value, Integer.MAX_VALUE);\n\n        value = mmkv.decodeInt(KeyNotExist);\n        assertEquals(value, 0);\n\n        value = mmkv.decodeInt(KeyNotExist, -1);\n        assertEquals(value, -1);\n    }\n\n    @Test\n    public void testLong() {\n        boolean ret = mmkv.encode(\"long\", Long.MAX_VALUE);\n        assertEquals(ret, true);\n\n        long value = mmkv.decodeLong(\"long\");\n        assertEquals(value, Long.MAX_VALUE);\n\n        value = mmkv.decodeLong(KeyNotExist);\n        assertEquals(value, 0);\n\n        value = mmkv.decodeLong(KeyNotExist, -1);\n        assertEquals(value, -1);\n    }\n\n    @Test\n    public void testFloat() {\n        boolean ret = mmkv.encode(\"float\", Float.MAX_VALUE);\n        assertEquals(ret, true);\n\n        float value = mmkv.decodeFloat(\"float\");\n        assertEquals(value, Float.MAX_VALUE, Delta);\n\n        value = mmkv.decodeFloat(KeyNotExist);\n        assertEquals(value, 0, Delta);\n\n        value = mmkv.decodeFloat(KeyNotExist, -1);\n        assertEquals(value, -1, Delta);\n    }\n\n    @Test\n    public void testDouble() {\n        boolean ret = mmkv.encode(\"double\", Double.MAX_VALUE);\n        assertEquals(ret, true);\n\n        double value = mmkv.decodeDouble(\"double\");\n        assertEquals(value, Double.MAX_VALUE, Delta);\n\n        value = mmkv.decodeDouble(KeyNotExist);\n        assertEquals(value, 0, Delta);\n\n        value = mmkv.decodeDouble(KeyNotExist, -1);\n        assertEquals(value, -1, Delta);\n    }\n\n    @Test\n    public void testString() {\n        String str = \"Hello 2018 world cup 世界杯\";\n        boolean ret = mmkv.encode(\"string\", str);\n        assertEquals(ret, true);\n\n        String value = mmkv.decodeString(\"string\");\n        assertEquals(value, str);\n\n        value = mmkv.decodeString(KeyNotExist);\n        assertEquals(value, null);\n\n        value = mmkv.decodeString(KeyNotExist, \"Empty\");\n        assertEquals(value, \"Empty\");\n    }\n\n    @Test\n    public void testStringSet() {\n        HashSet<String> set = new HashSet<String>();\n        set.add(\"W\");\n        set.add(\"e\");\n        set.add(\"C\");\n        set.add(\"h\");\n        set.add(\"a\");\n        set.add(\"t\");\n        boolean ret = mmkv.encode(\"string_set\", set);\n        assertEquals(ret, true);\n\n        HashSet<String> value = (HashSet<String>) mmkv.decodeStringSet(\"string_set\");\n        assertEquals(value, set);\n\n        value = (HashSet<String>) mmkv.decodeStringSet(KeyNotExist);\n        assertEquals(value, null);\n\n        set = new HashSet<String>();\n        set.add(\"W\");\n        value = (HashSet<String>) mmkv.decodeStringSet(KeyNotExist, set);\n        assertEquals(value, set);\n    }\n\n    @Test\n    public void testBytes() {\n        byte[] bytes = {'m', 'm', 'k', 'v'};\n        boolean ret = mmkv.encode(\"bytes\", bytes);\n        assertEquals(ret, true);\n\n        byte[] value = mmkv.decodeBytes(\"bytes\");\n        assertArrayEquals(value, bytes);\n    }\n\n    @Test\n    public void testRemove() {\n        boolean ret = mmkv.encode(\"bool_1\", true);\n        ret &= mmkv.encode(\"int_1\", Integer.MIN_VALUE);\n        ret &= mmkv.encode(\"long_1\", Long.MIN_VALUE);\n        ret &= mmkv.encode(\"float_1\", Float.MIN_VALUE);\n        ret &= mmkv.encode(\"double_1\", Double.MIN_VALUE);\n        ret &= mmkv.encode(\"string_1\", \"hello\");\n\n        HashSet<String> set = new HashSet<String>();\n        set.add(\"W\");\n        set.add(\"e\");\n        set.add(\"C\");\n        set.add(\"h\");\n        set.add(\"a\");\n        set.add(\"t\");\n        ret &= mmkv.encode(\"string_set_1\", set);\n\n        byte[] bytes = {'m', 'm', 'k', 'v'};\n        ret &= mmkv.encode(\"bytes_1\", bytes);\n        assertEquals(ret, true);\n\n        {\n            long count = mmkv.count();\n\n            mmkv.removeValueForKey(\"bool_1\");\n            mmkv.removeValuesForKeys(new String[] {\"int_1\", \"long_1\"});\n\n            long newCount = mmkv.count();\n            assertEquals(count, newCount + 3);\n        }\n\n        boolean bValue = mmkv.decodeBool(\"bool_1\");\n        assertEquals(bValue, false);\n\n        int iValue = mmkv.decodeInt(\"int_1\");\n        assertEquals(iValue, 0);\n\n        long lValue = mmkv.decodeLong(\"long_1\");\n        assertEquals(lValue, 0);\n\n        float fValue = mmkv.decodeFloat(\"float_1\");\n        assertEquals(fValue, Float.MIN_VALUE, Delta);\n\n        double dValue = mmkv.decodeDouble(\"double_1\");\n        assertEquals(dValue, Double.MIN_VALUE, Delta);\n\n        String sValue = mmkv.decodeString(\"string_1\");\n        assertEquals(sValue, \"hello\");\n\n        HashSet<String> hashSet = (HashSet<String>) mmkv.decodeStringSet(\"string_set_1\");\n        assertEquals(hashSet, set);\n\n        byte[] byteValue = mmkv.decodeBytes(\"bytes_1\");\n        assertArrayEquals(bytes, byteValue);\n    }\n\n    @Test\n    public void testIPCUpdateInt() {\n        MMKV mmkv = MMKV.mmkvWithID(MMKVTestService.SharedMMKVID, MMKV.MULTI_PROCESS_MODE);\n        mmkv.encode(MMKVTestService.SharedMMKVKey, 1024);\n\n        Context appContext = InstrumentationRegistry.getTargetContext();\n        Intent intent = new Intent(appContext, MMKVTestService.class);\n        intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Update);\n        appContext.startService(intent);\n\n        SystemClock.sleep(1000 * 3);\n        int value = mmkv.decodeInt(MMKVTestService.SharedMMKVKey);\n        assertEquals(value, 1024 + 1);\n    }\n\n    @Test\n    public void testIPCLock() {\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        Intent intent = new Intent(appContext, MMKVTestService.class);\n        intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Lock);\n        appContext.startService(intent);\n\n        SystemClock.sleep(1000 * 3);\n        MMKV mmkv = MMKV.mmkvWithID(MMKVTestService.SharedMMKVID, MMKV.MULTI_PROCESS_MODE);\n        boolean ret = mmkv.tryLock();\n        assertEquals(ret, false);\n\n        intent.putExtra(MMKVTestService.CMD_Key, MMKVTestService.CMD_Kill);\n        appContext.startService(intent);\n\n        SystemClock.sleep(1000 * 3);\n        ret = mmkv.tryLock();\n        assertEquals(ret, true);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\nimport android.os.Process;\nimport androidx.annotation.Nullable;\n\npublic class MMKVTestService extends Service {\n    public static final String SharedMMKVID = \"SharedMMKVID\";\n    public static final String SharedMMKVKey = \"SharedMMKVKey\";\n\n    public static final String CMD_Key = \"CMD_Key\";\n    public static final String CMD_Update = \"CMD_Update\";\n    public static final String CMD_Lock = \"CMD_Lock\";\n    public static final String CMD_Kill = \"CMD_Kill\";\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        MMKV.initialize(this);\n    }\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        MMKV mmkv = MMKV.mmkvWithID(SharedMMKVID, MMKV.MULTI_PROCESS_MODE);\n\n        String cmd = intent.getStringExtra(CMD_Key);\n        switch (cmd) {\n            case CMD_Update:\n                int value = mmkv.decodeInt(SharedMMKVKey);\n                value += 1;\n                mmkv.encode(SharedMMKVKey, value);\n                break;\n            case CMD_Lock:\n                mmkv.lock();\n                break;\n            case CMD_Kill:\n                stopSelf();\n                break;\n        }\n\n        return START_NOT_STICKY;\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n\n        Process.killProcess(Process.myPid());\n    }\n\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n</manifest>"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/aidl/com/tencent/mmkv/ParcelableMMKV.aidl",
    "content": "// MMKV.aidl\npackage com.tencent.mmkv;\n\nparcelable ParcelableMMKV;\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/cpp/flutter-bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKVPredef.h>\n\n#ifndef MMKV_DISABLE_FLUTTER\n\n#    include <MMKV/MMKV.h>\n#    include <MMKV/MMKVLog.h>\n#    include <cstdint>\n#    include <string>\n\nusing namespace mmkv;\nusing namespace std;\n\nnamespace mmkv {\nextern int g_android_api;\nextern string g_android_tmpDir;\n}\n\n#    ifdef MMKV_EXPORT\n#       undef MMKV_EXPORT\n#    endif\n#    define MMKV_EXPORT extern \"C\" __attribute__((visibility(\"default\"))) __attribute__((used))\n\nusing LogCallback_t = void (*)(uint32_t level, const char *file, int32_t line, const char *funcname, const char *message);\nusing ErrorCallback_t = int (*)(const char *mmapID, int32_t errorType);\nusing ContenctChangeCallback_t = void (*)(const char *mmapID);\n\nclass FlutterMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    LogCallback_t logCallback = nullptr;\n    ErrorCallback_t errorCallback = nullptr;\n    ContenctChangeCallback_t contentChangeCallback = nullptr;\n    ContenctChangeCallback_t contentLoadedCallback = nullptr;\n\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) override {\n        if (logCallback) {\n            logCallback(level, file, line, function, message.c_str());\n        }\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVCRCCheckFail);\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVFileLength);\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (contentChangeCallback) {\n            contentChangeCallback(mmapID.c_str());\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if (contentLoadedCallback) {\n            contentLoadedCallback(mmapID.c_str());\n        }\n    }\n};\n\nstatic FlutterMMKVHandler g_flutterHandler;\n\nMMKV_EXPORT void *mmkvInitialize_v2(const char *rootDir, const char *cacheDir, int32_t sdkInt, int32_t logLevel, LogCallback_t callback) {\n    if (!rootDir) {\n        return nullptr;\n    }\n    if (cacheDir) {\n        g_android_tmpDir = string(cacheDir);\n    }\n\n    g_android_api = sdkInt;\n#ifdef MMKV_STL_SHARED\n    MMKVInfo(\"current API level = %d, libc++_shared=%d\", g_android_api, MMKV_STL_SHARED);\n#else\n    MMKVInfo(\"current API level = %d, libc++_shared=?\", g_android_api);\n#endif\n\n    if (callback) {\n        g_flutterHandler.logCallback = callback;\n        MMKV::initializeMMKV(rootDir, (MMKVLogLevel) logLevel, &g_flutterHandler);\n    } else {\n        MMKV::initializeMMKV(rootDir, (MMKVLogLevel) logLevel);\n    }\n    return (void *) MMKV::getRootDir().c_str();\n}\n\nMMKV_EXPORT void mmkvInitialize_v1(const char *rootDir, const char *cacheDir, int32_t sdkInt, int32_t logLevel) {\n    mmkvInitialize_v2(rootDir, cacheDir, sdkInt, logLevel, nullptr);\n}\n\nMMKV_EXPORT void mmkvInitialize(const char *rootDir, int32_t logLevel) {\n    mmkvInitialize_v2(rootDir, nullptr, 0, logLevel, nullptr);\n}\n\nMMKV_EXPORT void *getMMKVWithID(const char *mmapID, int32_t mode, const char *cryptKey, const char *rootPath,\n                                size_t expectedCapacity, bool fromNameSpace, bool aes256, int32_t enableKeyExpire,\n                                int32_t expiredInSeconds, bool enableCompareBeforeSet, int32_t recover,\n                                uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n    if (!mmapID) {\n        return kv;\n    }\n    string str = mmapID;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    bool done = false;\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (!crypt.empty()) {\n            config.cryptKey = &crypt;\n            if (rootPath) {\n                string path = rootPath;\n                if (fromNameSpace) {\n                    auto ns = MMKV::nameSpace(path);\n                    config.rootPath = &ns.getRootDir();\n                    kv = ns.mmkvWithID(str, config);\n                } else {\n                    config.rootPath = &path;\n                    kv = MMKV::mmkvWithID(str, config);\n                }\n            } else {\n                kv = MMKV::mmkvWithID(str, config);\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath) {\n            string path = rootPath;\n            if (fromNameSpace) {\n                auto ns = MMKV::nameSpace(path);\n                config.rootPath = &ns.getRootDir();\n                kv = ns.mmkvWithID(str, config);\n            } else {\n                config.rootPath = &path;\n                kv = MMKV::mmkvWithID(str, config);\n            }\n        } else {\n            kv = MMKV::mmkvWithID(str, config);\n        }\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT void *getDefaultMMKV(int32_t mode, const char *cryptKey, bool aes256, size_t expectedCapacity,\n                                 int32_t enableKeyExpire, int32_t expiredInSeconds, bool enableCompareBeforeSet,\n                                 int32_t recover, uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            kv = MMKV::defaultMMKV(config);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::defaultMMKV(config);\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT const char *mmapID(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->mmapID().c_str();\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool encodeBool(void *handle, const char *oKey, bool value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBool_v2(void *handle, const char *oKey, bool value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool decodeBool(void *handle, const char *oKey, bool defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getBool(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt32(void *handle, const char *oKey, int32_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt32_v2(void *handle, const char *oKey, int32_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int32_t decodeInt32(void *handle, const char *oKey, int32_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt64(void *handle, const char *oKey, int64_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt64_v2(void *handle, const char *oKey, int64_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int64_t decodeInt64(void *handle, const char *oKey, int64_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeDouble(void *handle, const char *oKey, double value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeDouble_v2(void *handle, const char *oKey, double value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT double decodeDouble(void *handle, const char *oKey, double defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getDouble(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeBytes(void *handle, const char *oKey, void *oValue, uint64_t length) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBytes_v2(void *handle, const char *oKey, void *oValue, uint64_t length, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key, expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *decodeBytes(void *handle, const char *oKey, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        mmkv::MMBuffer value;\n        auto hasValue = kv->getBytes(key, value);\n        if (hasValue) {\n            if (value.length() > 0) {\n                if (value.isStoredOnStack()) {\n                    auto result = malloc(value.length());\n                    if (result) {\n                        memcpy(result, value.getPtr(), value.length());\n                        *lengthPtr = value.length();\n                    }\n                    return result;\n                }\n                void *result = value.getPtr();\n                *lengthPtr = value.length();\n                value.detach();\n                return result;\n            }\n            *lengthPtr = 0;\n            // this ptr is intended for checking existence of the value\n            // don't free this ptr\n            return value.getPtr();\n        }\n    }\n    return nullptr;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT bool reKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            return kv->reKey(key, aes256);\n        } else {\n            return kv->reKey(string(), aes256);\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *cryptKey(void *handle, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && lengthPtr) {\n        auto cryptKey = kv->cryptKey();\n        if (cryptKey.length() > 0) {\n            auto ptr = malloc(cryptKey.length());\n            if (ptr) {\n                memcpy(ptr, cryptKey.data(), cryptKey.length());\n                *lengthPtr = cryptKey.length();\n                return ptr;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT void checkReSetCryptKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            kv->checkReSetCryptKey(&key, aes256);\n        } else {\n            kv->checkReSetCryptKey(nullptr, aes256);\n        }\n    }\n}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT uint32_t valueSize(void *handle, char *oKey, bool actualSize) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        auto ret = kv->getValueSize(key, actualSize);\n        return static_cast<uint32_t>(ret);\n    }\n    return 0;\n}\n\nMMKV_EXPORT int32_t writeValueToNB(void *handle, char *oKey, void *pointer, uint32_t size) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->writeValueToBuffer(key, pointer, size);\n    }\n    return -1;\n}\n\nMMKV_EXPORT uint64_t allKeys(void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        auto keys = kv->allKeys(filterExpire);\n        if (!keys.empty()) {\n            auto keyArray = (char **) malloc(keys.size() * sizeof(void *));\n            auto sizeArray = (uint32_t *) malloc(keys.size() * sizeof(uint32_t *));\n            if (!keyArray || !sizeArray) {\n                free(keyArray);\n                free(sizeArray);\n                return 0;\n            }\n            *keyArrayPtr = keyArray;\n            *sizeArrayPtr = sizeArray;\n\n            for (size_t index = 0; index < keys.size(); index++) {\n                auto &key = keys[index];\n                sizeArray[index] = static_cast<uint32_t>(key.length());\n                keyArray[index] = (char *) malloc(key.length());\n                if (keyArray[index]) {\n                    memcpy(keyArray[index], key.data(), key.length());\n                }\n            }\n        }\n        return keys.size();\n    }\n    return 0;\n}\n\nMMKV_EXPORT bool containsKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->containsKey(key);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t count(void *handle, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->count(filterExpire);\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t totalSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->totalSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t actualSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->actualSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT void removeValueForKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        kv->removeValueForKey(key);\n    }\n}\n\nMMKV_EXPORT void removeValuesForKeys(void *handle, char **keyArray, uint32_t *sizeArray, uint64_t count) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && keyArray && sizeArray && count > 0) {\n        vector<string> arrKeys;\n        arrKeys.reserve(count);\n        for (uint64_t index = 0; index < count; index++) {\n            if (sizeArray[index] > 0 && keyArray[index]) {\n                arrKeys.emplace_back(keyArray[index], sizeArray[index]);\n            }\n        }\n        if (!arrKeys.empty()) {\n            kv->removeValuesForKeys(arrKeys);\n        }\n    }\n}\n\nMMKV_EXPORT void clearAll(void *handle, bool keepSpace) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearAll(keepSpace);\n    }\n}\n\nMMKV_EXPORT void mmkvSync(void *handle, bool sync) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->sync((SyncFlag) sync);\n    }\n}\n\nMMKV_EXPORT void clearMemoryCache(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n}\n\nMMKV_EXPORT int32_t pageSize() {\n    return static_cast<int32_t>(DEFAULT_MMAP_SIZE);\n}\n\nMMKV_EXPORT const char *version() {\n    return MMKV_VERSION;\n}\n\nMMKV_EXPORT void trim(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->trim();\n    }\n}\n\nMMKV_EXPORT void mmkvClose(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->close();\n    }\n}\n\nMMKV_EXPORT void mmkvMemcpy(void *dst, const void *src, uint64_t size) {\n    memcpy(dst, src, size);\n}\n\nMMKV_EXPORT bool backupOne(const char *mmapID, const char *dstDir, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupOneToDirectory(mmapID, dstDir, &root);\n        }\n    }\n    return MMKV::backupOneToDirectory(mmapID, dstDir);\n}\n\nMMKV_EXPORT bool restoreOne(const char *mmapID, const char *srcDir, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreOneFromDirectory(mmapID, srcDir, &root);\n        }\n    }\n    return MMKV::restoreOneFromDirectory(mmapID, srcDir);\n}\n\nMMKV_EXPORT uint64_t backupAll(const char *dstDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupAllToDirectory(dstDir, &root);\n        }\n    }*/\n    return MMKV::backupAllToDirectory(dstDir);\n}\n\nMMKV_EXPORT uint64_t restoreAll(const char *srcDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreAllFromDirectory(srcDir, &root);\n        }\n    }*/\n    return MMKV::restoreAllFromDirectory(srcDir);\n}\n\nMMKV_EXPORT bool enableAutoExpire(void *handle, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableAutoKeyExpire(expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableAutoExpire(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableAutoKeyExpire();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool enableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isFileValid(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::isFileValid(mmapID, &root);\n        }\n    }\n    return MMKV::isFileValid(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool removeStorage(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::removeStorage(mmapID, &root);\n        }\n    }\n    return MMKV::removeStorage(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool isMultiProcess(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isMultiProcess();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isReadOnly(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isReadOnly();\n    }\n    return false;\n}\n\nMMKV_EXPORT void registerErrorHandler(ErrorCallback_t callback) {\n    g_flutterHandler.errorCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.contentChangeCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentChangeNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentChangeCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentLoadedNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentLoadedCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentChangeCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void checkContentChanged(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->checkContentChanged();\n    }\n}\n\nMMKV_EXPORT bool getNameSpace(const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (!root.empty()) {\n            MMKV::nameSpace(root);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool checkExist(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::checkExist(mmapID, &root);\n        }\n    }\n    return MMKV::checkExist(mmapID, nullptr);\n}\n\nMMKV_EXPORT size_t importFrom(void *handle, void *srcHandle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    MMKV *kvSrc = static_cast<MMKV *>(srcHandle);\n    if (kv && kvSrc) {\n        return kv->importFrom(kvSrc);\n    }\n    return 0;\n}\n\n#endif // MMKV_DISABLE_FLUTTER\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/cpp/native-bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKVPredef.h>\n\n#ifdef MMKV_ANDROID\n\n#    include <MMKV/MMBuffer.h>\n#    include <MMKV/MMKV.h>\n#    include <MMKV/MMKVLog.h>\n#    include <MMKV/MemoryFile.h>\n#    include <cstdint>\n#    include <jni.h>\n#    include <string>\n#    include <android/api-level.h>\n\nusing namespace std;\nusing namespace mmkv;\n\nstatic jclass g_cls = nullptr;\nstatic jfieldID g_fileID = nullptr;\nstatic jmethodID g_callbackOnCRCFailID = nullptr;\nstatic jmethodID g_callbackOnFileLengthErrorID = nullptr;\nstatic jmethodID g_mmkvLogID = nullptr;\nstatic jmethodID g_callbackOnContentChange = nullptr;\nstatic jmethodID g_callbackOnContentLoaded = nullptr;\nstatic JavaVM *g_currentJVM = nullptr;\n\nstatic int registerNativeMethods(JNIEnv *env, jclass cls);\nextern \"C\" void internalLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...);\nextern MMKVLogLevel g_currentLogLevel;\n\nnamespace mmkv {\n    static void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message);\n\n    typedef void (*AndroidLogHandler)(MMKVLogLevel level, const char *file, int line, const char *function, const char *message);\n    static AndroidLogHandler g_androidLogHandler = nullptr;\n    static void androidLogWrapper(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message);\n\n    static JNIEnv *getCurrentEnv();\n    static jstring string2jstring(JNIEnv *env, const string &str);\n\n// C++ adapter class that bridges mmkv::MMKVHandler to JNI\nclass JNIMMKVHandler : public mmkv::MMKVHandler {\n    bool m_logRedirecting = false;\n    bool m_hasCallback = false;\n    bool m_wantsContentChange = false;\npublic:\n    void setLogRedirecting(bool logRedirecting) { m_logRedirecting = logRedirecting; }\n    void setHasCallback(bool hasCallback) { m_hasCallback = hasCallback; }\n    void setWantsContentChange(bool wantsContentChange) { m_wantsContentChange = wantsContentChange; }\n    bool isLogRedirecting() const { return m_logRedirecting; }\n\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message) override {\n        if (m_logRedirecting) {\n            if (g_androidLogHandler) {\n                g_androidLogHandler(level, file, line, function, message.c_str());\n            } else {\n                mmkv::mmkvLog(level, file, line, function, message);\n            }\n        }\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (!m_hasCallback) {\n            return OnErrorDiscard;\n        }\n        auto currentEnv = getCurrentEnv();\n        if (currentEnv && g_callbackOnCRCFailID) {\n            jstring str = string2jstring(currentEnv, mmapID);\n            auto strategic = currentEnv->CallStaticIntMethod(g_cls, g_callbackOnCRCFailID, str);\n            currentEnv->DeleteLocalRef(str);\n            return static_cast<MMKVRecoverStrategic>(strategic);\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (!m_hasCallback) {\n            return OnErrorDiscard;\n        }\n        auto currentEnv = getCurrentEnv();\n        if (currentEnv && g_callbackOnFileLengthErrorID) {\n            jstring str = string2jstring(currentEnv, mmapID);\n            auto strategic = currentEnv->CallStaticIntMethod(g_cls, g_callbackOnFileLengthErrorID, str);\n            currentEnv->DeleteLocalRef(str);\n            return static_cast<MMKVRecoverStrategic>(strategic);\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        auto currentEnv = getCurrentEnv();\n        if (currentEnv && g_callbackOnContentChange) {\n            jstring str = string2jstring(currentEnv, mmapID);\n            currentEnv->CallStaticVoidMethod(g_cls, g_callbackOnContentChange, str);\n            currentEnv->DeleteLocalRef(str);\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        auto currentEnv = getCurrentEnv();\n        if (currentEnv && g_callbackOnContentLoaded) {\n            jstring str = string2jstring(currentEnv, mmapID);\n            currentEnv->CallStaticVoidMethod(g_cls, g_callbackOnContentLoaded, str);\n            currentEnv->DeleteLocalRef(str);\n        }\n    }\n};\n\nstatic JNIMMKVHandler g_jniHandler;\n}\n\n#define InternalLogError(format, ...) \\\n    internalLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogError, __MMKV_FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n\n#define InternalLogInfo(format, ...) \\\n    internalLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogInfo, __MMKV_FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n\nextern \"C\" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) {\n    g_currentJVM = vm;\n    JNIEnv *env;\n    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {\n        return -1;\n    }\n\n    if (g_cls) {\n        env->DeleteGlobalRef(g_cls);\n    }\n    static const char *clsName = \"com/tencent/mmkv/MMKV\";\n    jclass instance = env->FindClass(clsName);\n    if (!instance) {\n        MMKVError(\"fail to locate class: %s\", clsName);\n        return -2;\n    }\n    g_cls = reinterpret_cast<jclass>(env->NewGlobalRef(instance));\n    if (!g_cls) {\n        MMKVError(\"fail to create global reference for %s\", clsName);\n        return -3;\n    }\n    g_mmkvLogID =\n        env->GetStaticMethodID(g_cls, \"mmkvLogImp\", \"(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;)V\");\n    if (!g_mmkvLogID) {\n        MMKVError(\"fail to get method id for mmkvLogImp\");\n    }\n    // every code from now on can use InternalLogXXX()\n\n    int ret = registerNativeMethods(env, g_cls);\n    if (ret != 0) {\n        InternalLogError(\"fail to register native methods for class %s, ret = %d\", clsName, ret);\n        return -4;\n    }\n    g_fileID = env->GetFieldID(g_cls, \"nativeHandle\", \"J\");\n    if (!g_fileID) {\n        InternalLogError(\"fail to locate fileID\");\n        return -5;\n    }\n\n    g_callbackOnCRCFailID = env->GetStaticMethodID(g_cls, \"onMMKVCRCCheckFail\", \"(Ljava/lang/String;)I\");\n    if (!g_callbackOnCRCFailID) {\n        InternalLogError(\"fail to get method id for onMMKVCRCCheckFail\");\n    }\n    g_callbackOnFileLengthErrorID = env->GetStaticMethodID(g_cls, \"onMMKVFileLengthError\", \"(Ljava/lang/String;)I\");\n    if (!g_callbackOnFileLengthErrorID) {\n        InternalLogError(\"fail to get method id for onMMKVFileLengthError\");\n    }\n    g_callbackOnContentChange =\n        env->GetStaticMethodID(g_cls, \"onContentChangedByOuterProcess\", \"(Ljava/lang/String;)V\");\n    if (!g_callbackOnContentChange) {\n        InternalLogError(\"fail to get method id for onContentChangedByOuterProcess()\");\n    }\n    g_callbackOnContentLoaded =\n        env->GetStaticMethodID(g_cls, \"onMMKVContentLoadSuccessfully\", \"(Ljava/lang/String;)V\");\n    if (!g_callbackOnContentLoaded) {\n        InternalLogError(\"fail to get method id for onMMKVContentLoadSuccessfully()\");\n    }\n\n    // Note: If you use NDK r23 or older, you can get API level by accessing android.os.Build.VERSION.SDK_INT\n    g_android_api = android_get_device_api_level();\n#ifdef MMKV_STL_SHARED\n    InternalLogInfo(\"current API level = %d, libc++_shared=%d\", g_android_api, MMKV_STL_SHARED);\n#else\n    InternalLogInfo(\"current API level = %d, libc++_shared=?\", g_android_api);\n#endif\n\n    return JNI_VERSION_1_6;\n}\n\n//#define MMKV_JNI extern \"C\" JNIEXPORT JNICALL\n#    define MMKV_JNI static\n\nnamespace mmkv {\n\nstatic string jstring2string(JNIEnv *env, jstring str);\n\nMMKV_JNI void jniInitialize(JNIEnv *env, jobject obj, jstring rootDir, jstring cacheDir, jint logLevel, jboolean logReDirecting, jboolean hasCallback, jlong nativeLogHandler) {\n    if (!rootDir) {\n        return;\n    }\n    const char *kstr = env->GetStringUTFChars(rootDir, nullptr);\n    if (kstr) {\n        g_jniHandler.setLogRedirecting(logReDirecting == JNI_TRUE);\n        g_jniHandler.setHasCallback(hasCallback == JNI_TRUE);\n        if (logReDirecting && nativeLogHandler != 0) {\n            g_androidLogHandler = (AndroidLogHandler) nativeLogHandler;\n        } else {\n            g_androidLogHandler = nullptr;\n        }\n        mmkv::MMKVHandler *handler = (logReDirecting || hasCallback) ? &g_jniHandler : nullptr;\n        MMKV::initializeMMKV(kstr, (MMKVLogLevel) logLevel, handler);\n        env->ReleaseStringUTFChars(rootDir, kstr);\n\n        g_android_tmpDir = jstring2string(env, cacheDir);\n\n        if (hasCallback == JNI_TRUE || logReDirecting == JNI_TRUE) {\n            MMKV::registerHandler(&g_jniHandler);\n        } else {\n            MMKV::unRegisterHandler();\n        }\n    }\n}\n\nMMKV_JNI void onExit(JNIEnv *env, jobject obj) {\n    MMKV::onExit();\n}\n\nstatic MMKV *getMMKV(JNIEnv *env, jobject obj) {\n    jlong handle = env->GetLongField(obj, g_fileID);\n    return reinterpret_cast<MMKV *>(handle);\n}\n\nstatic string jstring2string(JNIEnv *env, jstring str) {\n    if (str) {\n        const char *kstr = env->GetStringUTFChars(str, nullptr);\n        if (kstr) {\n            string result(kstr);\n            env->ReleaseStringUTFChars(str, kstr);\n            return result;\n        }\n    }\n    return \"\";\n}\n\nstatic jstring string2jstring(JNIEnv *env, const string &str) {\n    return env->NewStringUTF(str.c_str());\n}\n\nstatic vector<string> jarray2vector(JNIEnv *env, jobjectArray array) {\n    vector<string> keys;\n    if (array) {\n        jsize size = env->GetArrayLength(array);\n        keys.reserve(size);\n        for (jsize i = 0; i < size; i++) {\n            jstring str = (jstring) env->GetObjectArrayElement(array, i);\n            if (str) {\n                keys.push_back(jstring2string(env, str));\n                env->DeleteLocalRef(str);\n            }\n        }\n    }\n    return keys;\n}\n\nstatic jobjectArray vector2jarray(JNIEnv *env, const vector<string> &arr) {\n    jobjectArray result = env->NewObjectArray(arr.size(), env->FindClass(\"java/lang/String\"), nullptr);\n    if (result) {\n        for (size_t index = 0; index < arr.size(); index++) {\n            jstring value = string2jstring(env, arr[index]);\n            env->SetObjectArrayElement(result, index, value);\n            env->DeleteLocalRef(value);\n        }\n    }\n    return result;\n}\n\nstatic JNIEnv *getCurrentEnv() {\n    if (g_currentJVM) {\n        JNIEnv *currentEnv = nullptr;\n        auto ret = g_currentJVM->GetEnv(reinterpret_cast<void **>(&currentEnv), JNI_VERSION_1_6);\n        if (ret == JNI_OK) {\n            return currentEnv;\n        } else {\n            MMKVError(\"fail to get current JNIEnv: %d\", ret);\n        }\n    }\n    return nullptr;\n}\n\nextern \"C\" void internalLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...) {\n    if (level >= g_currentLogLevel) {\n        std::string message;\n        char buffer[16];\n\n        va_list args;\n        va_start(args, format);\n        auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n        va_end(args);\n\n        if (length < 0) { // something wrong\n            message = {};\n        } else if (length < sizeof(buffer)) {\n            message = std::string(buffer, static_cast<unsigned long>(length));\n        } else {\n            message.resize(static_cast<unsigned long>(length), '\\0');\n            va_start(args, format);\n            std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n            va_end(args);\n        }\n\n        if (g_cls && g_mmkvLogID) {\n            mmkvLog(level, filename, line, func, message);\n        } else {\n            _MMKVLogWithLevel(level, filename, func, line, message.c_str());\n        }\n    }\n}\n\nstatic void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) {\n    auto currentEnv = getCurrentEnv();\n    if (currentEnv && g_mmkvLogID) {\n        jstring oFile = string2jstring(currentEnv, string(file));\n        jstring oFunction = string2jstring(currentEnv, string(function));\n        jstring oMessage = string2jstring(currentEnv, message);\n        int readLevel = level;\n\n        currentEnv->CallStaticVoidMethod(g_cls, g_mmkvLogID, readLevel, oFile, line, oFunction, oMessage);\n\n        currentEnv->DeleteLocalRef(oMessage);\n        currentEnv->DeleteLocalRef(oFunction);\n        currentEnv->DeleteLocalRef(oFile);\n    }\n}\n\nstatic void androidLogWrapper(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) {\n    g_androidLogHandler(level, file, line, function, message.c_str());\n}\n\nMMKV_JNI jlong getMMKVWithID(JNIEnv *env, jobject, jstring mmapID, jint mode, jstring cryptKey, jstring rootPath,\n                             jlong expectedCapacity, jboolean aes256, jint enableKeyExpire, jint expiredInSeconds,\n                             jboolean enableCompareBeforeSet, jint recover, jint itemSizeLimit) {\n    MMKV *kv = nullptr;\n    if (!mmapID) {\n        return (jlong) kv;\n    }\n    string str = jstring2string(env, mmapID);\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    bool done = false;\n    if (cryptKey) {\n        string crypt = jstring2string(env, cryptKey);\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            if (rootPath) {\n                string path = jstring2string(env, rootPath);\n                config.rootPath = &path;\n                kv = MMKV::mmkvWithID(str, config);\n            } else {\n                kv = MMKV::mmkvWithID(str, config);\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath) {\n            string path = jstring2string(env, rootPath);\n            config.rootPath = &path;\n            kv = MMKV::mmkvWithID(str, config);\n        } else {\n            kv = MMKV::mmkvWithID(str, config);\n        }\n    }\n\n    return (jlong) kv;\n}\n\nMMKV_JNI jlong getDefaultMMKV(JNIEnv *env, jobject obj, jint mode, jstring cryptKey, jlong expectedCapacity,\n                              jboolean aes256, jint enableKeyExpire, jint expiredInSeconds,\n                              jboolean enableCompareBeforeSet, jint recover, jint itemSizeLimit) {\n    MMKV *kv = nullptr;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    if (cryptKey) {\n        string crypt = jstring2string(env, cryptKey);\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            kv = MMKV::defaultMMKV(config);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::defaultMMKV(config);\n    }\n\n    return (jlong) kv;\n}\n\nMMKV_JNI jlong getMMKVWithAshmemFD(JNIEnv *env, jobject obj, jstring mmapID, jint fd, jint metaFD, jstring cryptKey,\n                                   jboolean aes256) {\n    MMKV *kv = nullptr;\n    if (!mmapID || fd < 0 || metaFD < 0) {\n        return (jlong) kv;\n    }\n    string id = jstring2string(env, mmapID);\n\n    if (cryptKey) {\n        string crypt = jstring2string(env, cryptKey);\n        if (crypt.length() > 0) {\n            kv = MMKV::mmkvWithAshmemFD(id, fd, metaFD, &crypt, aes256);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::mmkvWithAshmemFD(id, fd, metaFD, nullptr, aes256);\n    }\n\n    return (jlong) kv;\n}\n\nMMKV_JNI jstring mmapID(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return string2jstring(env, kv->mmapID());\n    }\n    return nullptr;\n}\n\nMMKV_JNI jint ashmemFD(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return kv->ashmemFD();\n    }\n    return -1;\n}\n\nMMKV_JNI jint ashmemMetaFD(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return kv->ashmemMetaFD();\n    }\n    return -1;\n}\n\nMMKV_JNI jboolean checkProcessMode(JNIEnv *env, jobject, jlong handle) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->checkProcessMode();\n    }\n    return false;\n}\n\nMMKV_JNI jboolean encodeBool(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean value) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((bool) value, key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeBool_2(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean value, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((bool) value, key, (uint32_t) expiration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean decodeBool(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean defaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->getBool(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_JNI jboolean encodeInt(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint value) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((int32_t) value, key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeInt_2(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint value, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((int32_t) value, key, (uint32_t) expiration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jint decodeInt(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jint defaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jint) kv->getInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_JNI jboolean encodeLong(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong value) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((int64_t) value, key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeLong_2(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong value, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((int64_t) value, key, (uint32_t) expiration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jlong decodeLong(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jlong defaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jlong) kv->getInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_JNI jboolean encodeFloat(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jfloat value) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((float) value, key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeFloat_2(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jfloat value, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((float) value, key, (uint32_t) expiration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jfloat decodeFloat(JNIEnv *env, jobject, jlong handle, jstring oKey, jfloat defaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jfloat) kv->getFloat(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_JNI jboolean encodeDouble(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jdouble value) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((double) value, key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeDouble_2(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jdouble value, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->set((double) value, key, (uint32_t) expiration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jdouble decodeDouble(JNIEnv *env, jobject, jlong handle, jstring oKey, jdouble defaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jdouble) kv->getDouble(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_JNI jboolean encodeString(JNIEnv *env, jobject, jlong handle, jstring oKey, jstring oValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (oValue) {\n            string value = jstring2string(env, oValue);\n            return (jboolean) kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeString_2(JNIEnv *env, jobject, jlong handle, jstring oKey, jstring oValue, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (oValue) {\n            string value = jstring2string(env, oValue);\n            return (jboolean) kv->set(value, key, (uint32_t) expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jstring decodeString(JNIEnv *env, jobject obj, jlong handle, jstring oKey, jstring oDefaultValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        string value;\n        bool hasValue = kv->getString(key, value);\n        if (hasValue) {\n            return string2jstring(env, value);\n        }\n    }\n    return oDefaultValue;\n}\n\nMMKV_JNI jboolean encodeBytes(JNIEnv *env, jobject, jlong handle, jstring oKey, jbyteArray oValue) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (oValue) {\n            MMBuffer value(0);\n            {\n                jsize len = env->GetArrayLength(oValue);\n                void *bufferPtr = env->GetPrimitiveArrayCritical(oValue, nullptr);\n                if (bufferPtr) {\n                    value = MMBuffer(bufferPtr, len);\n                    env->ReleasePrimitiveArrayCritical(oValue, bufferPtr, JNI_ABORT);\n                } else {\n                    MMKVError(\"fail to get array: %s=%p\", key.c_str(), oValue);\n                }\n            }\n            return (jboolean) kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeBytes_2(JNIEnv *env, jobject, jlong handle, jstring oKey, jbyteArray oValue, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (oValue) {\n            MMBuffer value(0);\n            {\n                jsize len = env->GetArrayLength(oValue);\n                void *bufferPtr = env->GetPrimitiveArrayCritical(oValue, nullptr);\n                if (bufferPtr) {\n                    value = MMBuffer(bufferPtr, len);\n                    env->ReleasePrimitiveArrayCritical(oValue, bufferPtr, JNI_ABORT);\n                } else {\n                    MMKVError(\"fail to get array: %s=%p\", key.c_str(), oValue);\n                }\n            }\n            return (jboolean) kv->set(value, key, (uint32_t) expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jbyteArray decodeBytes(JNIEnv *env, jobject obj, jlong handle, jstring oKey) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        mmkv::MMBuffer value;\n        auto hasValue = kv->getBytes(key, value);\n        if (hasValue) {\n            jbyteArray result = env->NewByteArray(value.length());\n            env->SetByteArrayRegion(result, 0, value.length(), (const jbyte *) value.getPtr());\n            return result;\n        }\n    }\n    return nullptr;\n}\n\nMMKV_JNI jobjectArray allKeys(JNIEnv *env, jobject instance, jlong handle, jboolean filterExpire) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        vector<string> keys = kv->allKeys((bool) filterExpire);\n        return vector2jarray(env, keys);\n    }\n    return nullptr;\n}\n\nMMKV_JNI jboolean containsKey(JNIEnv *env, jobject instance, jlong handle, jstring oKey) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return (jboolean) kv->containsKey(key);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jlong count(JNIEnv *env, jobject instance, jlong handle, jboolean filterExpire) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        jlong size = kv->count((bool) filterExpire);\n        return size;\n    }\n    return 0;\n}\n\nMMKV_JNI jlong totalSize(JNIEnv *env, jobject instance, jlong handle) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        jlong size = kv->totalSize();\n        return size;\n    }\n    return 0;\n}\n\nMMKV_JNI jlong actualSize(JNIEnv *env, jobject instance, jlong handle) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        jlong size = kv->actualSize();\n        return size;\n    }\n    return 0;\n}\n\nMMKV_JNI void removeValueForKey(JNIEnv *env, jobject instance, jlong handle, jstring oKey) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        kv->removeValueForKey(key);\n    }\n}\n\nMMKV_JNI void removeValuesForKeys(JNIEnv *env, jobject instance, jobjectArray arrKeys) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv && arrKeys) {\n        vector<string> keys = jarray2vector(env, arrKeys);\n        if (!keys.empty()) {\n            kv->removeValuesForKeys(keys);\n        }\n    }\n}\n\nMMKV_JNI void clearAll(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->clearAll();\n    }\n}\n\nMMKV_JNI void sync(JNIEnv *env, jobject instance, jboolean sync) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->sync((SyncFlag) sync);\n    }\n}\n\nMMKV_JNI jboolean isFileValid(JNIEnv *env, jclass type, jstring oMmapID, jstring rootPath) {\n    if (oMmapID) {\n        string mmapID = jstring2string(env, oMmapID);\n        if (!rootPath) {\n            return (jboolean) MMKV::isFileValid(mmapID, nullptr);\n        } else {\n            auto root = jstring2string(env, rootPath);\n            return (jboolean) MMKV::isFileValid(mmapID, &root);\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean removeStorage(JNIEnv *env, jclass type, jstring oMmapID, jstring rootPath) {\n    if (oMmapID) {\n        string mmapID = jstring2string(env, oMmapID);\n        if (!rootPath) {\n            return (jboolean) MMKV::removeStorage(mmapID, nullptr);\n        } else {\n            auto root = jstring2string(env, rootPath);\n            return (jboolean) MMKV::removeStorage(mmapID, &root);\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeSet(JNIEnv *env, jobject, jlong handle, jstring oKey, jobjectArray arrStr) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (arrStr) {\n            vector<string> value = jarray2vector(env, arrStr);\n            return (jboolean) kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean encodeSet_2(JNIEnv *env, jobject, jlong handle, jstring oKey, jobjectArray arrStr, jint expiration) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        if (arrStr) {\n            vector<string> value = jarray2vector(env, arrStr);\n            return (jboolean) kv->set(value, key, (uint32_t) expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jobjectArray decodeStringSet(JNIEnv *env, jobject, jlong handle, jstring oKey) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        vector<string> value;\n        bool hasValue = kv->getVector(key, value);\n        if (hasValue) {\n            return vector2jarray(env, value);\n        }\n    }\n    return nullptr;\n}\n\nMMKV_JNI void clearMemoryCache(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n}\n\nMMKV_JNI void lock(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->lock();\n    }\n}\n\nMMKV_JNI void unlock(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->unlock();\n    }\n}\n\nMMKV_JNI jboolean tryLock(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return (jboolean) kv->try_lock();\n    }\n    return jboolean(false);\n}\n\nMMKV_JNI jint pageSize(JNIEnv *env, jclass type) {\n    return DEFAULT_MMAP_SIZE;\n}\n\nMMKV_JNI jstring version(JNIEnv *env, jclass type) {\n    return string2jstring(env, MMKV_VERSION);\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nMMKV_JNI jstring cryptKey(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        string cryptKey = kv->cryptKey();\n        if (cryptKey.length() > 0) {\n            return string2jstring(env, cryptKey);\n        }\n    }\n    return nullptr;\n}\n\nMMKV_JNI jboolean doReKey(JNIEnv *env, jobject instance, jstring cryptKey, jboolean aes256) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        string newKey;\n        if (cryptKey) {\n            newKey = jstring2string(env, cryptKey);\n        }\n        return (jboolean) kv->reKey(newKey, aes256);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI void doCheckReSetCryptKey(JNIEnv *env, jobject instance, jstring cryptKey, jboolean aes256) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        string newKey;\n        if (cryptKey) {\n            newKey = jstring2string(env, cryptKey);\n        }\n\n        if (!cryptKey || newKey.empty()) {\n            kv->checkReSetCryptKey(nullptr, aes256);\n        } else {\n            kv->checkReSetCryptKey(&newKey, aes256);\n        }\n    }\n}\n\n#    else\n\nMMKV_JNI jstring cryptKey(JNIEnv *env, jobject instance) {\n    return nullptr;\n}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nMMKV_JNI void trim(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->trim();\n    }\n}\n\nMMKV_JNI void close(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->close();\n        env->SetLongField(instance, g_fileID, 0);\n    }\n}\n\nMMKV_JNI jint valueSize(JNIEnv *env, jobject, jlong handle, jstring oKey, jboolean actualSize) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return static_cast<jint>(kv->getValueSize(key, (bool) actualSize));\n    }\n    return 0;\n}\n\nMMKV_JNI void setLogLevel(JNIEnv *env, jclass type, jint level) {\n    MMKV::setLogLevel((MMKVLogLevel) level);\n}\n\nMMKV_JNI void setCallbackHandler(JNIEnv *env, jclass type, jboolean logReDirecting, jboolean hasCallback, jlong nativeHandler) {\n    g_jniHandler.setLogRedirecting(logReDirecting == JNI_TRUE);\n    g_jniHandler.setHasCallback(hasCallback == JNI_TRUE);\n    if (logReDirecting && nativeHandler != 0) {\n        g_androidLogHandler = (AndroidLogHandler) nativeHandler;\n    } else {\n        g_androidLogHandler = nullptr;\n    }\n\n    if (hasCallback == JNI_TRUE || logReDirecting == JNI_TRUE) {\n        MMKV::registerHandler(&g_jniHandler);\n    } else {\n        MMKV::unRegisterHandler();\n    }\n}\n\nMMKV_JNI jlong createNB(JNIEnv *env, jobject instance, jint size) {\n    auto ptr = malloc(static_cast<size_t>(size));\n    if (!ptr) {\n        MMKVError(\"fail to create NativeBuffer:%s\", strerror(errno));\n        return 0;\n    }\n    return reinterpret_cast<jlong>(ptr);\n}\n\nMMKV_JNI void destroyNB(JNIEnv *env, jobject instance, jlong pointer, jint size) {\n    free(reinterpret_cast<void *>(pointer));\n}\n\nMMKV_JNI jint writeValueToNB(JNIEnv *env, jobject instance, jlong handle, jstring oKey, jlong pointer, jint size) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key = jstring2string(env, oKey);\n        return kv->writeValueToBuffer(key, reinterpret_cast<void *>(pointer), size);\n    }\n    return -1;\n}\n\nMMKV_JNI void setWantsContentChangeNotify(JNIEnv *env, jclass type, jboolean notify) {\n    g_jniHandler.setWantsContentChange(notify == JNI_TRUE);\n    // ensure handler is registered when content change is wanted\n    if (notify == JNI_TRUE) {\n        MMKV::registerHandler(&g_jniHandler);\n    }\n}\n\nMMKV_JNI void checkContentChanged(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->checkContentChanged();\n    }\n}\n\nMMKV_JNI jboolean backupOne(JNIEnv *env, jobject obj, jstring mmapID, jstring dstDir, jstring rootPath) {\n    if (rootPath) {\n        string root = jstring2string(env, rootPath);\n        if (root.length() > 0) {\n            return (jboolean) MMKV::backupOneToDirectory(jstring2string(env, mmapID), jstring2string(env, dstDir), &root);\n        }\n    }\n    return (jboolean) MMKV::backupOneToDirectory(jstring2string(env, mmapID), jstring2string(env, dstDir));\n}\n\nMMKV_JNI jboolean restoreOne(JNIEnv *env, jobject obj, jstring mmapID, jstring srcDir, jstring rootPath) {\n    if (rootPath) {\n        string root = jstring2string(env, rootPath);\n        if (root.length() > 0) {\n            return (jboolean) MMKV::restoreOneFromDirectory(jstring2string(env, mmapID), jstring2string(env, srcDir), &root);\n        }\n    }\n    return (jboolean) MMKV::restoreOneFromDirectory(jstring2string(env, mmapID), jstring2string(env, srcDir));\n}\n\nMMKV_JNI jlong backupAll(JNIEnv *env, jobject obj, jstring dstDir/*, jstring rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        string root = jstring2string(env, rootPath);\n        if (root.length() > 0) {\n            return (jlong) MMKV::backupAllToDirectory(jstring2string(env, dstDir), &root);\n        }\n    }*/\n    return (jlong) MMKV::backupAllToDirectory(jstring2string(env, dstDir));\n}\n\nMMKV_JNI jlong restoreAll(JNIEnv *env, jobject obj, jstring srcDir/*, jstring rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        string root = jstring2string(env, rootPath);\n        if (root.length() > 0) {\n            return (jlong) MMKV::restoreAllFromDirectory(jstring2string(env, srcDir), &root);\n        }\n    }*/\n    return (jlong) MMKV::restoreAllFromDirectory(jstring2string(env, srcDir));\n}\n\nMMKV_JNI jboolean enableAutoExpire(JNIEnv *env, jobject instance, jint expireDuration) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return (jboolean) kv->enableAutoKeyExpire(expireDuration);\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean disableAutoExpire(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return (jboolean) kv->disableAutoKeyExpire();\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI void enableCompareBeforeSet(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->enableCompareBeforeSet();\n    }\n}\n\nMMKV_JNI void disableCompareBeforeSet(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->disableCompareBeforeSet();\n    }\n}\n\nMMKV_JNI bool isCompareBeforeSetEnabled(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return kv->isCompareBeforeSetEnabled();\n    }\n    return false;\n}\n\nMMKV_JNI bool isEncryptionEnabled(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return kv->isEncryptionEnabled();\n    }\n    return false;\n}\n\nMMKV_JNI bool isExpirationEnabled(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return kv->isExpirationEnabled();\n    }\n    return false;\n}\n\nMMKV_JNI void clearAllWithKeepingSpace(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        kv->clearAll(true);\n    }\n}\n\nMMKV_JNI jboolean isMultiProcess(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return (jboolean) kv->isMultiProcess();\n    }\n    return jboolean(false);\n}\n\nMMKV_JNI jboolean isReadOnly(JNIEnv *env, jobject instance) {\n    MMKV *kv = getMMKV(env, instance);\n    if (kv) {\n        return (jboolean) kv->isReadOnly();\n    }\n    return jboolean(false);\n}\n\nMMKV_JNI jboolean getNameSpace(JNIEnv *env, jclass type, jstring rootPath) {\n    if (rootPath) {\n        auto root = jstring2string(env, rootPath);\n        if (!root.empty()) {\n            MMKV::nameSpace(root);\n            return (jboolean) true;\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI jboolean checkExist(JNIEnv *env, jclass type, jstring oMmapID, jstring rootPath) {\n    if (oMmapID) {\n        string mmapID = jstring2string(env, oMmapID);\n        if (!rootPath) {\n            return (jboolean) MMKV::checkExist(mmapID, nullptr);\n        } else {\n            auto root = jstring2string(env, rootPath);\n            return (jboolean) MMKV::checkExist(mmapID, &root);\n        }\n    }\n    return (jboolean) false;\n}\n\nMMKV_JNI void enableDisableProcessMode(JNIEnv *env, jclass type, jboolean notify) {\n    if (notify == JNI_TRUE) {\n        MMKV::enableDisableProcessMode(true);\n    } else {\n        MMKV::enableDisableProcessMode(false);\n    }\n}\n\nMMKV_JNI jlong importFrom(JNIEnv *env, jobject instance, jlong handle, jlong srcHandle) {\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    MMKV *src = reinterpret_cast<MMKV *>(srcHandle);\n    if (kv && src) {\n        jlong size = kv->importFrom(src);\n        return size;\n    }\n    return 0;\n}\n\n} // namespace mmkv\n\nstatic JNINativeMethod g_methods[] = {\n    {\"onExit\", \"()V\", (void *) mmkv::onExit},\n    {\"cryptKey\", \"()Ljava/lang/String;\", (void *) mmkv::cryptKey},\n#    ifndef MMKV_DISABLE_CRYPT\n    {\"doReKey\", \"(Ljava/lang/String;Z)Z\", (void *) mmkv::doReKey},\n    {\"doCheckReSetCryptKey\", \"(Ljava/lang/String;Z)V\", (void *) mmkv::doCheckReSetCryptKey},\n#    endif\n    {\"pageSize\", \"()I\", (void *) mmkv::pageSize},\n    {\"mmapID\", \"()Ljava/lang/String;\", (void *) mmkv::mmapID},\n    {\"version\", \"()Ljava/lang/String;\", (void *) mmkv::version},\n    {\"lock\", \"()V\", (void *) mmkv::lock},\n    {\"unlock\", \"()V\", (void *) mmkv::unlock},\n    {\"tryLock\", \"()Z\", (void *) mmkv::tryLock},\n    {\"allKeys\", \"(JZ)[Ljava/lang/String;\", (void *) mmkv::allKeys},\n    {\"removeValuesForKeys\", \"([Ljava/lang/String;)V\", (void *) mmkv::removeValuesForKeys},\n    {\"clearAll\", \"()V\", (void *) mmkv::clearAll},\n    {\"trim\", \"()V\", (void *) mmkv::trim},\n    {\"close\", \"()V\", (void *) mmkv::close},\n    {\"clearMemoryCache\", \"()V\", (void *) mmkv::clearMemoryCache},\n    {\"sync\", \"(Z)V\", (void *) mmkv::sync},\n    {\"isFileValid\", \"(Ljava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::isFileValid},\n    {\"removeStorage\", \"(Ljava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::removeStorage},\n    {\"ashmemFD\", \"()I\", (void *) mmkv::ashmemFD},\n    {\"ashmemMetaFD\", \"()I\", (void *) mmkv::ashmemMetaFD},\n    {\"jniInitialize\", \"(Ljava/lang/String;Ljava/lang/String;IZZJ)V\", (void *) mmkv::jniInitialize},\n    {\"getMMKVWithID\", \"(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JZIIZII)J\", (void *) mmkv::getMMKVWithID},\n    {\"getDefaultMMKV\", \"(ILjava/lang/String;JZIIZII)J\", (void *) mmkv::getDefaultMMKV},\n    {\"getMMKVWithAshmemFD\", \"(Ljava/lang/String;IILjava/lang/String;Z)J\", (void *) mmkv::getMMKVWithAshmemFD},\n    {\"encodeBool\", \"(JLjava/lang/String;Z)Z\", (void *) mmkv::encodeBool},\n    {\"encodeBool_2\", \"(JLjava/lang/String;ZI)Z\", (void *) mmkv::encodeBool_2},\n    {\"decodeBool\", \"(JLjava/lang/String;Z)Z\", (void *) mmkv::decodeBool},\n    {\"encodeInt\", \"(JLjava/lang/String;I)Z\", (void *) mmkv::encodeInt},\n    {\"encodeInt_2\", \"(JLjava/lang/String;II)Z\", (void *) mmkv::encodeInt_2},\n    {\"decodeInt\", \"(JLjava/lang/String;I)I\", (void *) mmkv::decodeInt},\n    {\"encodeLong\", \"(JLjava/lang/String;J)Z\", (void *) mmkv::encodeLong},\n    {\"encodeLong_2\", \"(JLjava/lang/String;JI)Z\", (void *) mmkv::encodeLong_2},\n    {\"decodeLong\", \"(JLjava/lang/String;J)J\", (void *) mmkv::decodeLong},\n    {\"encodeFloat\", \"(JLjava/lang/String;F)Z\", (void *) mmkv::encodeFloat},\n    {\"encodeFloat_2\", \"(JLjava/lang/String;FI)Z\", (void *) mmkv::encodeFloat_2},\n    {\"decodeFloat\", \"(JLjava/lang/String;F)F\", (void *) mmkv::decodeFloat},\n    {\"encodeDouble\", \"(JLjava/lang/String;D)Z\", (void *) mmkv::encodeDouble},\n    {\"encodeDouble_2\", \"(JLjava/lang/String;DI)Z\", (void *) mmkv::encodeDouble_2},\n    {\"decodeDouble\", \"(JLjava/lang/String;D)D\", (void *) mmkv::decodeDouble},\n    {\"encodeString\", \"(JLjava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::encodeString},\n    {\"encodeString_2\", \"(JLjava/lang/String;Ljava/lang/String;I)Z\", (void *) mmkv::encodeString_2},\n    {\"decodeString\", \"(JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;\", (void *) mmkv::decodeString},\n    {\"encodeSet\", \"(JLjava/lang/String;[Ljava/lang/String;)Z\", (void *) mmkv::encodeSet},\n    {\"encodeSet_2\", \"(JLjava/lang/String;[Ljava/lang/String;I)Z\", (void *) mmkv::encodeSet_2},\n    {\"decodeStringSet\", \"(JLjava/lang/String;)[Ljava/lang/String;\", (void *) mmkv::decodeStringSet},\n    {\"encodeBytes\", \"(JLjava/lang/String;[B)Z\", (void *) mmkv::encodeBytes},\n    {\"encodeBytes_2\", \"(JLjava/lang/String;[BI)Z\", (void *) mmkv::encodeBytes_2},\n    {\"decodeBytes\", \"(JLjava/lang/String;)[B\", (void *) mmkv::decodeBytes},\n    {\"containsKey\", \"(JLjava/lang/String;)Z\", (void *) mmkv::containsKey},\n    {\"count\", \"(JZ)J\", (void *) mmkv::count},\n    {\"totalSize\", \"(J)J\", (void *) mmkv::totalSize},\n    {\"actualSize\", \"(J)J\", (void *) mmkv::actualSize},\n    {\"removeValueForKey\", \"(JLjava/lang/String;)V\", (void *) mmkv::removeValueForKey},\n    {\"valueSize\", \"(JLjava/lang/String;Z)I\", (void *) mmkv::valueSize},\n    {\"setLogLevel\", \"(I)V\", (void *) mmkv::setLogLevel},\n    {\"setCallbackHandler\", \"(ZZJ)V\", (void *) mmkv::setCallbackHandler},\n    {\"createNB\", \"(I)J\", (void *) mmkv::createNB},\n    {\"destroyNB\", \"(JI)V\", (void *) mmkv::destroyNB},\n    {\"writeValueToNB\", \"(JLjava/lang/String;JI)I\", (void *) mmkv::writeValueToNB},\n    {\"setWantsContentChangeNotify\", \"(Z)V\", (void *) mmkv::setWantsContentChangeNotify},\n    {\"checkContentChangedByOuterProcess\", \"()V\", (void *) mmkv::checkContentChanged},\n    {\"checkProcessMode\", \"(J)Z\", (void *) mmkv::checkProcessMode},\n    {\"backupOneToDirectory\", \"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::backupOne},\n    {\"restoreOneMMKVFromDirectory\", \"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::restoreOne},\n    {\"backupAllToDirectory\", \"(Ljava/lang/String;)J\", (void *) mmkv::backupAll},\n    {\"restoreAllFromDirectory\", \"(Ljava/lang/String;)J\", (void *) mmkv::restoreAll},\n    {\"enableAutoKeyExpire\", \"(I)Z\", (void *) mmkv::enableAutoExpire},\n    {\"disableAutoKeyExpire\", \"()Z\", (void *) mmkv::disableAutoExpire},\n    {\"nativeEnableCompareBeforeSet\", \"()V\", (void *) mmkv::enableCompareBeforeSet},\n    {\"disableCompareBeforeSet\", \"()V\", (void *) mmkv::disableCompareBeforeSet},\n    {\"isCompareBeforeSetEnabled\", \"()Z\", (void *) mmkv::isCompareBeforeSetEnabled},\n    {\"isEncryptionEnabled\", \"()Z\", (void *) mmkv::isEncryptionEnabled},\n    {\"isExpirationEnabled\", \"()Z\", (void *) mmkv::isExpirationEnabled},\n    {\"clearAllWithKeepingSpace\", \"()V\", (void *) mmkv::clearAllWithKeepingSpace},\n    {\"isMultiProcess\", \"()Z\", (void *) mmkv::isMultiProcess},\n    {\"isReadOnly\", \"()Z\", (void *) mmkv::isReadOnly},\n    {\"getNameSpace\", \"(Ljava/lang/String;)Z\", (void *)mmkv::getNameSpace},\n    {\"checkExist\", \"(Ljava/lang/String;Ljava/lang/String;)Z\", (void *) mmkv::checkExist},\n    {\"enableDisableProcessMode\", \"(Z)V\", (void *) mmkv::enableDisableProcessMode},\n    {\"importFrom\", \"(JJ)J\", (void *) mmkv::importFrom},\n};\n\nstatic int registerNativeMethods(JNIEnv *env, jclass cls) {\n    return env->RegisterNatives(cls, g_methods, sizeof(g_methods) / sizeof(g_methods[0]));\n}\n\n#endif // MMKV_ANDROID\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKV.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.Process;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.jetbrains.annotations.Contract;\n\nimport dalvik.annotation.optimization.FastNative;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * An highly efficient, reliable, multi-process key-value storage framework.\n * THE PERFECT drop-in replacement for SharedPreferences and MultiProcessSharedPreferences.\n */\npublic class MMKV implements SharedPreferences, SharedPreferences.Editor {\n\n    private static final EnumMap<MMKVRecoverStrategic, Integer> recoverIndex;\n    private static final EnumMap<MMKVLogLevel, Integer> logLevel2Index;\n    private static final MMKVLogLevel[] index2LogLevel;\n    private static final Set<Long> checkedHandleSet;\n\n    static {\n        recoverIndex = new EnumMap<>(MMKVRecoverStrategic.class);\n        recoverIndex.put(MMKVRecoverStrategic.OnErrorDiscard, 0);\n        recoverIndex.put(MMKVRecoverStrategic.OnErrorRecover, 1);\n\n        logLevel2Index = new EnumMap<>(MMKVLogLevel.class);\n        logLevel2Index.put(MMKVLogLevel.LevelDebug, 0);\n        logLevel2Index.put(MMKVLogLevel.LevelInfo, 1);\n        logLevel2Index.put(MMKVLogLevel.LevelWarning, 2);\n        logLevel2Index.put(MMKVLogLevel.LevelError, 3);\n        logLevel2Index.put(MMKVLogLevel.LevelNone, 4);\n\n        index2LogLevel = new MMKVLogLevel[]{MMKVLogLevel.LevelDebug, MMKVLogLevel.LevelInfo, MMKVLogLevel.LevelWarning,\n                MMKVLogLevel.LevelError, MMKVLogLevel.LevelNone};\n\n        checkedHandleSet = new HashSet<Long>();\n    }\n\n    /**\n     * The interface for providing a 3rd library loader (the ReLinker https://github.com/KeepSafe/ReLinker, etc).\n     */\n    public interface LibLoader {\n        void loadLibrary(String libName);\n    }\n\n    /**\n     * Initialize MMKV with default configuration.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context The context of Android App, usually from Application.\n     * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static String initialize(@NonNull Context context) {\n        String root = context.getFilesDir().getAbsolutePath() + \"/mmkv\";\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return initialize(context, root, null, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with customize log level.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.\n     * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static String initialize(@NonNull Context context, MMKVLogLevel logLevel) {\n        String root = context.getFilesDir().getAbsolutePath() + \"/mmkv\";\n        return initialize(context, root, null, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with a 3rd library loader.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context The context of Android App, usually from Application.\n     * @param loader  The 3rd library loader (for example, the <a href=\"https://github.com/KeepSafe/ReLinker\">ReLinker</a> .\n     * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static String initialize(@NonNull Context context, LibLoader loader) {\n        String root = context.getFilesDir().getAbsolutePath() + \"/mmkv\";\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return initialize(context, root, loader, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with a 3rd library loader, and customize log level.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param loader   The 3rd library loader (for example, the <a href=\"https://github.com/KeepSafe/ReLinker\">ReLinker</a> .\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.\n     * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static String initialize(@NonNull Context context, LibLoader loader, MMKVLogLevel logLevel) {\n        String root = context.getFilesDir().getAbsolutePath() + \"/mmkv\";\n        return initialize(context, root, loader, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with customize root folder.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context The context of Android App, usually from Application.\n     * @param rootDir The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     * @return The root folder of MMKV.\n     */\n    public static String initialize(Context context, String rootDir) {\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return initialize(context, rootDir, null, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with customize root folder, and log level.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param rootDir  The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.\n     * @return The root folder of MMKV.\n     */\n    public static String initialize(Context context, String rootDir, MMKVLogLevel logLevel) {\n        return initialize(context, rootDir, null, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with customize root folder, and a 3rd library loader.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context The context of Android App, usually from Application.\n     * @param rootDir The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     * @param loader  The 3rd library loader (for example, the <a href=\"https://github.com/KeepSafe/ReLinker\">ReLinker</a> .\n     * @return The root folder of MMKV.\n     */\n    public static String initialize(Context context, String rootDir, LibLoader loader) {\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return initialize(context, rootDir, loader, logLevel, null);\n    }\n\n    /**\n     * Initialize MMKV with customize settings.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param rootDir  The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     * @param loader   The 3rd library loader (for example, the <a href=\"https://github.com/KeepSafe/ReLinker\">ReLinker</a> .\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.\n     * @return The root folder of MMKV.\n     */\n    public static String initialize(Context context, String rootDir, LibLoader loader, MMKVLogLevel logLevel) {\n        return initialize(context, rootDir, loader, logLevel, null);\n    }\n\n    public static String initialize(@NonNull Context context, String rootDir, LibLoader loader, MMKVLogLevel logLevel, MMKVHandler handler) {\n        if (!Process.is64Bit()) {\n            throw new UnsupportedArchitectureException(\"MMKV 2.0+ requires 64-bit App, use 1.3.x instead.\");\n        }\n        String cacheDir = context.getCacheDir().getAbsolutePath();\n\n        gCallbackHandler = handler;\n        boolean hasCallback = false;\n        long nativeLogHandler = 0;\n        if (handler != null) {\n            hasCallback = true;\n            if (handler.wantLogRedirecting()) {\n                gWantLogReDirecting = true;\n                nativeLogHandler = handler.getNativeLogHandler();\n            }\n        }\n\n        String ret = doInitialize(rootDir, cacheDir, loader, logLevel, gWantLogReDirecting, hasCallback, nativeLogHandler);\n\n        if (handler != null && handler.wantContentChangeNotification()) {\n            setWantsContentChangeNotify(true);\n        }\n\n        // disable process mode in release build\n        // FIXME: Find a better way to getApplicationInfo() without using context.\n        //  If any one knows how, you're welcome to make a contribution.\n        if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {\n            disableProcessModeChecker();\n        } else {\n            enableProcessModeChecker();\n        }\n\n        return ret;\n    }\n\n    private static String doInitialize(String rootDir, String cacheDir, LibLoader loader, MMKVLogLevel logLevel, boolean wantLogReDirecting, boolean hasCallback, long nativeHandler) {\n        tryLoadNativeLib(loader);\n        jniInitialize(rootDir, cacheDir, logLevel2Int(logLevel), wantLogReDirecting, hasCallback, nativeHandler);\n        MMKV.rootDir = rootDir;\n        return MMKV.rootDir;\n    }\n\n    private static boolean isNativeLibLoaded = false;\n\n    private static void tryLoadNativeLib(@Nullable LibLoader loader) {\n        if (isNativeLibLoaded) {\n            return;\n        }\n        if (loader != null) {\n            if (BuildConfig.FLAVOR.equals(\"SharedCpp\")) {\n                loader.loadLibrary(\"c++_shared\");\n            }\n            loader.loadLibrary(\"mmkv\");\n        } else {\n            if (BuildConfig.FLAVOR.equals(\"SharedCpp\")) {\n                System.loadLibrary(\"c++_shared\");\n            }\n            System.loadLibrary(\"mmkv\");\n        }\n        isNativeLibLoaded = true;\n    }\n\n    /**\n     * @deprecated This method is deprecated due to failing to automatically disable checkProcessMode() without Context.\n     * Use the {@link #initialize(Context, String)} method instead.\n     */\n    @Deprecated\n    public static String initialize(String rootDir) {\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return doInitialize(rootDir, rootDir + \"/.tmp\", null, logLevel, false, false, 0);\n    }\n\n    /**\n     * @deprecated This method is deprecated due to failing to automatically disable checkProcessMode() without Context.\n     * Use the {@link #initialize(Context, String, MMKVLogLevel)} method instead.\n     */\n    @Deprecated\n    public static String initialize(String rootDir, MMKVLogLevel logLevel) {\n        return doInitialize(rootDir, rootDir + \"/.tmp\", null, logLevel, false, false, 0);\n    }\n\n    /**\n     * @deprecated This method is deprecated due to failing to automatically disable checkProcessMode() without Context.\n     * Use the {@link #initialize(Context, String, LibLoader)} method instead.\n     */\n    @Deprecated\n    public static String initialize(String rootDir, LibLoader loader) {\n        MMKVLogLevel logLevel = BuildConfig.DEBUG ? MMKVLogLevel.LevelDebug : MMKVLogLevel.LevelInfo;\n        return doInitialize(rootDir, rootDir + \"/.tmp\", loader, logLevel, false, false, 0);\n    }\n\n    /**\n     * @deprecated This method is deprecated due to failing to automatically disable checkProcessMode() without Context.\n     * Use the {@link #initialize(Context, String, LibLoader, MMKVLogLevel)} method instead.\n     */\n    @Deprecated\n    public static String initialize(String rootDir, LibLoader loader, MMKVLogLevel logLevel) {\n        return doInitialize(rootDir, rootDir + \"/.tmp\", loader, logLevel, false, false, 0);\n    }\n\n    /**\n     * @param dir the customize root directory of a NameSpace\n     * @return a NameSpace with custom root dir\n     * @throws RuntimeException if there's an runtime error.\n     */\n    public static NameSpace nameSpace(String dir) throws RuntimeException {\n        tryLoadNativeLib(null);\n        if (getNameSpace(dir)) {\n            return new NameSpace(dir);\n        }\n        throw new RuntimeException(\"Fail to get NameSpace[\" + dir + \"] in JNI.\");\n    }\n\n    /**\n     * identical with the original MMKV with the global root dir\n     * @throws RuntimeException if there's an runtime error.\n     */\n    public static NameSpace defaultNameSpace() throws RuntimeException {\n        if (rootDir == null) {\n            throw new IllegalStateException(\"You should Call MMKV.initialize() first.\");\n        }\n        return new NameSpace(rootDir);\n    }\n\n    static private String rootDir = null;\n\n    /**\n     * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static String getRootDir() {\n        return rootDir;\n    }\n\n    @Contract(pure = true)\n    private static int logLevel2Int(@NonNull MMKVLogLevel level) {\n        int realLevel;\n        switch (level) {\n            case LevelDebug:\n                realLevel = 0;\n                break;\n            case LevelWarning:\n                realLevel = 2;\n                break;\n            case LevelError:\n                realLevel = 3;\n                break;\n            case LevelNone:\n                realLevel = 4;\n                break;\n            case LevelInfo:\n            default:\n                realLevel = 1;\n                break;\n        }\n        return realLevel;\n    }\n\n    /**\n     * Set the log level of MMKV.\n     *\n     * @param level Defaults to {@link MMKVLogLevel#LevelInfo}.\n     */\n    public static void setLogLevel(MMKVLogLevel level) {\n        int realLevel = logLevel2Int(level);\n        setLogLevel(realLevel);\n    }\n\n    /**\n     * Notify MMKV that App is about to exit. It's totally fine not calling it at all.\n     */\n    public static native void onExit();\n\n    /**\n     * Single-process mode. The default mode on an MMKV instance.\n     */\n    static public final int SINGLE_PROCESS_MODE = 1 << 0;\n\n    /**\n     * Multi-process mode.\n     * To enable multi-process accessing of an MMKV instance, you must set this mode whenever you getting that instance.\n     */\n    static public final int MULTI_PROCESS_MODE = 1 << 1;\n\n    // in case someone mistakenly pass Context.MODE_MULTI_PROCESS\n    static private final int CONTEXT_MODE_MULTI_PROCESS = 1 << 2;\n\n    static private final int ASHMEM_MODE = 1 << 3;\n\n    static private final int BACKUP_MODE = 1 << 4;\n\n    /**\n     * Read-only mode.\n     */\n    static public final int READ_ONLY_MODE = 1 << 5;\n\n    /**\n     * Create an MMKV instance with an unique ID (in single-process mode).\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID) throws RuntimeException {\n        return mmkvWithID(mmapID, new MMKVConfig());\n    }\n\n    /**\n     * Create an MMKV instance with an unique ID (in single-process mode).\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param config The all-in-one configuration for the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, MMKVConfig config) throws RuntimeException {\n        if (rootDir == null) {\n            throw new IllegalStateException(\"You should Call MMKV.initialize() first.\");\n        }\n\n        int enableKeyExpire = (config.enableKeyExpire != null) ? (config.enableKeyExpire ? 1 : 0) : -1;\n        Integer value = recoverIndex.get(config.recover);\n        int recover = (value == null) ? -1 : value;\n\n        long handle = getMMKVWithID(mmapID, config.mode, config.cryptKey, config.rootPath, config.expectedCapacity,\n                config.aes256, enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet,\n                recover, config.itemSizeLimit);\n\n        return checkProcessMode(handle, mmapID, config.mode);\n    }\n\n    /**\n     * Create an MMKV instance in single-process or multi-process mode.\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode   The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in single-process or multi-process mode.\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode   The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, long expectedCapacity) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256 Use AES 256 key length\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize folder.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, String rootPath) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.rootPath = rootPath;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize folder.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, String rootPath, long expectedCapacity) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.rootPath = rootPath;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, String rootPath, long expectedCapacity)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256, String rootPath,\n                                  long expectedCapacity) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, String rootPath)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256, String rootPath)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Get an backed-up MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param rootPath The backup folder of the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV backedUpMMKVWithID(String mmapID, int mode, @Nullable String cryptKey, String rootPath)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Get an backed-up MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @param rootPath The backup folder of the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV backedUpMMKVWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256, String rootPath)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        mode |= BACKUP_MODE;\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        config.rootPath = rootPath;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance base on Anonymous Shared Memory, aka not synced to any disk files.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param size     The maximum size of the underlying Anonymous Shared Memory.\n     *                 Anonymous Shared Memory on Android can't grow dynamically, must set an appropriate size on creation.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, @Nullable String cryptKey)\n            throws RuntimeException {\n        return mmkvWithAshmemID(context, mmapID, size, mode, cryptKey, false);\n    }\n\n    /**\n     * Create an MMKV instance base on Anonymous Shared Memory, aka not synced to any disk files.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param size     The maximum size of the underlying Anonymous Shared Memory.\n     *                 Anonymous Shared Memory on Android can't grow dynamically, must set an appropriate size on creation.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, @Nullable String cryptKey,\n                                        boolean aes256) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode | ASHMEM_MODE;\n        config.expectedCapacity = size;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        return mmkvWithAshmemID(context, mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance base on Anonymous Shared Memory, aka not synced to any disk files.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param config   The all-in-one configuration for the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV mmkvWithAshmemID(Context context, String mmapID, MMKVConfig config) throws RuntimeException {\n        if (rootDir == null) {\n            throw new IllegalStateException(\"You should Call MMKV.initialize() first.\");\n        }\n\n        String processName = MMKVContentProvider.getProcessNameByPID(context, Process.myPid());\n        if (processName == null || processName.isEmpty()) {\n            String message = \"process name detect fail, try again later\";\n            simpleLog(MMKVLogLevel.LevelError, message);\n            throw new IllegalStateException(message);\n        }\n        if (processName.contains(\":\")) {\n            Uri uri = MMKVContentProvider.contentUri(context);\n            if (uri == null) {\n                String message = \"MMKVContentProvider has invalid authority\";\n                simpleLog(MMKVLogLevel.LevelError, message);\n                throw new IllegalStateException(message);\n            }\n            simpleLog(MMKVLogLevel.LevelInfo, \"getting parcelable mmkv in process, Uri = \" + uri);\n\n            Bundle extras = new Bundle();\n            extras.putInt(MMKVContentProvider.KEY_SIZE, (int) config.expectedCapacity);\n            extras.putInt(MMKVContentProvider.KEY_MODE, config.mode);\n            if (config.cryptKey != null) {\n                extras.putString(MMKVContentProvider.KEY_CRYPT, config.cryptKey);\n            }\n            ContentResolver resolver = context.getContentResolver();\n            Bundle result = resolver.call(uri, MMKVContentProvider.FUNCTION_NAME, mmapID, extras);\n            if (result != null) {\n                result.setClassLoader(ParcelableMMKV.class.getClassLoader());\n                ParcelableMMKV parcelableMMKV = result.getParcelable(MMKVContentProvider.KEY);\n                if (parcelableMMKV != null) {\n                    MMKV mmkv = parcelableMMKV.toMMKV();\n                    if (mmkv != null) {\n                        simpleLog(MMKVLogLevel.LevelInfo,\n                                mmkv.mmapID() + \" fd = \" + mmkv.ashmemFD() + \", meta fd = \" + mmkv.ashmemMetaFD());\n                        return mmkv;\n                    }\n                }\n            }\n        }\n        simpleLog(MMKVLogLevel.LevelInfo, \"getting mmkv in main process\");\n\n        config.mode = config.mode | ASHMEM_MODE;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create the default MMKV instance in single-process mode.\n     *\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV defaultMMKV() throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        return defaultMMKV(config);\n    }\n\n    /**\n     * Create the default MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV defaultMMKV(int mode, @Nullable String cryptKey) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        return defaultMMKV(config);\n    }\n\n    /**\n     * Create the default MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV defaultMMKV(int mode, @Nullable String cryptKey, boolean aes256) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        return defaultMMKV(config);\n    }\n\n    /**\n     * Create the default MMKV instance in customize configuration.\n     *\n     * @param config     The all-in-one configuration for the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public static MMKV defaultMMKV(MMKVConfig config) throws RuntimeException {\n        if (rootDir == null) {\n            throw new IllegalStateException(\"You should Call MMKV.initialize() first.\");\n        }\n\n        int enableKeyExpire = (config.enableKeyExpire != null) ? (config.enableKeyExpire ? 1 : 0) : -1;\n        Integer value = recoverIndex.get(config.recover);\n        int recover = (value == null) ? -1 : value;\n\n        long handle = getDefaultMMKV(config.mode, config.cryptKey, config.expectedCapacity, config.aes256,\n                enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet, recover, config.itemSizeLimit);\n\n        return checkProcessMode(handle, \"DefaultMMKV\", config.mode);\n    }\n\n    @NonNull\n    @Contract(\"_, _, _ -> new\")\n    static MMKV checkProcessMode(long handle, String mmapID, int mode) throws RuntimeException {\n        if (handle == 0) {\n            throw new RuntimeException(\"Fail to create an MMKV instance [\" + mmapID + \"] in JNI\");\n        }\n        if (!isProcessModeCheckerEnabled) {\n            return new MMKV(handle);\n        }\n        synchronized (checkedHandleSet) {\n            if (!checkedHandleSet.contains(handle)) {\n                if (!checkProcessMode(handle)) {\n                    String message;\n                    if (mode == SINGLE_PROCESS_MODE) {\n                        message = \"Opening a multi-process MMKV instance [\" + mmapID + \"] with SINGLE_PROCESS_MODE!\";\n                    } else {\n                        message = \"Opening an MMKV instance [\" + mmapID + \"] with MULTI_PROCESS_MODE, \";\n                        message += \"while it's already been opened with SINGLE_PROCESS_MODE by someone somewhere else!\";\n                    }\n                    throw new IllegalArgumentException(message);\n                }\n                checkedHandleSet.add(handle);\n            }\n        }\n        return new MMKV(handle);\n    }\n\n    // Enable checkProcessMode() when initializing an MMKV instance, it's automatically enabled on debug build.\n    private static boolean isProcessModeCheckerEnabled = true;\n\n    /**\n     * Manually enable the process mode checker.\n     * By default, it's automatically enabled in DEBUG build, and disabled in RELEASE build.\n     * If it's enabled, MMKV will throw exceptions when an MMKV instance is created with mismatch process mode.\n     */\n    public static void enableProcessModeChecker() {\n        synchronized (checkedHandleSet) {\n            isProcessModeCheckerEnabled = true;\n        }\n        enableDisableProcessMode(true);\n        Log.i(\"MMKV\", \"Enable checkProcessMode()\");\n    }\n\n    /**\n     * Manually disable the process mode checker.\n     * By default, it's automatically enabled in DEBUG build, and disabled in RELEASE build.\n     * If it's enabled, MMKV will throw exceptions when an MMKV instance is created with mismatch process mode.\n     */\n    public static void disableProcessModeChecker() {\n        synchronized (checkedHandleSet) {\n            isProcessModeCheckerEnabled = false;\n        }\n        enableDisableProcessMode(false);\n        Log.i(\"MMKV\", \"Disable checkProcessMode()\");\n    }\n\n    /**\n     * @return The encryption key (no more than 16 bytes).\n     */\n    @Nullable\n    public native String cryptKey();\n\n    /**\n     * Transform plain text into encrypted text, or vice versa by passing a null encryption key.\n     * You can also change existing crypt key with a different cryptKey.\n     *\n     * @param cryptKey The new encryption key (no more than 16 bytes).\n     * @return True if success, otherwise False.\n     */\n    public boolean reKey(@Nullable String cryptKey) {\n        return doReKey(cryptKey, false);\n    }\n\n    /**\n     * Transform plain text into encrypted text, or vice versa by passing a null encryption key.\n     * You can also change existing crypt key with a different cryptKey.\n     *\n     * @param cryptKey The new encryption key (no more than 32 bytes).\n     * @param aes256 Use AES 256 key length\n     * @return True if success, otherwise False.\n     */\n    public boolean reKey(@Nullable String cryptKey, boolean aes256) {\n        return doReKey(cryptKey, aes256);\n    }\n    private native boolean doReKey(@Nullable String cryptKey, boolean aes256);\n\n    /**\n     * Just reset the encryption key (will not encrypt or decrypt anything).\n     * Usually you should call this method after another process has {@link #reKey(String)} the multi-process MMKV instance.\n     *\n     * @param cryptKey The new encryption key (no more than 16 bytes).\n     */\n    public void checkReSetCryptKey(@Nullable String cryptKey) {\n        doCheckReSetCryptKey(cryptKey, false);\n    }\n\n    /**\n     * Just reset the encryption key (will not encrypt or decrypt anything).\n     * Usually you should call this method after another process has {@link #reKey(String)} the multi-process MMKV instance.\n     *\n     * @param cryptKey The new encryption key (no more than 16 bytes).\n     * @param aes256 Use AES 256 key length\n     */\n    public void checkReSetCryptKey(@Nullable String cryptKey, boolean aes256) {\n        doCheckReSetCryptKey(cryptKey, aes256);\n    }\n    private native void doCheckReSetCryptKey(@Nullable String cryptKey, boolean aes256);\n\n    /**\n     * @return The device's memory page size.\n     */\n    public static native int pageSize();\n\n    /**\n     * @return The version of MMKV.\n     */\n    public static native String version();\n\n    /**\n     * @return The unique ID of the MMKV instance.\n     */\n    public native String mmapID();\n\n    /**\n     * Exclusively inter-process lock the MMKV instance.\n     * It will block and wait until it successfully locks the file.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     */\n    public native void lock();\n\n    /**\n     * Exclusively inter-process unlock the MMKV instance.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     */\n    public native void unlock();\n\n    /**\n     * Try exclusively inter-process lock the MMKV instance.\n     * It will not block if the file has already been locked by another process.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     *\n     * @return True if successfully locked, otherwise return immediately with False.\n     */\n    public native boolean tryLock();\n\n    public boolean encode(String key, boolean value) {\n        return encodeBool(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, boolean value, int expireDurationInSecond) {\n        return encodeBool_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    public boolean decodeBool(String key) {\n        return decodeBool(nativeHandle, key, false);\n    }\n\n    public boolean decodeBool(String key, boolean defaultValue) {\n        return decodeBool(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, int value) {\n        return encodeInt(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, int value, int expireDurationInSecond) {\n        return encodeInt_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    public int decodeInt(String key) {\n        return decodeInt(nativeHandle, key, 0);\n    }\n\n    public int decodeInt(String key, int defaultValue) {\n        return decodeInt(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, long value) {\n        return encodeLong(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, long value, int expireDurationInSecond) {\n        return encodeLong_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    public long decodeLong(String key) {\n        return decodeLong(nativeHandle, key, 0);\n    }\n\n    public long decodeLong(String key, long defaultValue) {\n        return decodeLong(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, float value) {\n        return encodeFloat(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, float value, int expireDurationInSecond) {\n        return encodeFloat_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    public float decodeFloat(String key) {\n        return decodeFloat(nativeHandle, key, 0);\n    }\n\n    public float decodeFloat(String key, float defaultValue) {\n        return decodeFloat(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, double value) {\n        return encodeDouble(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, double value, int expireDurationInSecond) {\n        return encodeDouble_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    public double decodeDouble(String key) {\n        return decodeDouble(nativeHandle, key, 0);\n    }\n\n    public double decodeDouble(String key, double defaultValue) {\n        return decodeDouble(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, @Nullable String value) {\n        return encodeString(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, @Nullable String value, int expireDurationInSecond) {\n        return encodeString_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    @Nullable\n    public String decodeString(String key) {\n        return decodeString(nativeHandle, key, null);\n    }\n\n    @Nullable\n    public String decodeString(String key, @Nullable String defaultValue) {\n        return decodeString(nativeHandle, key, defaultValue);\n    }\n\n    public boolean encode(String key, @Nullable Set<String> value) {\n        return encodeSet(nativeHandle, key, (value == null) ? null : value.toArray(new String[0]));\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, @Nullable Set<String> value, int expireDurationInSecond) {\n        return encodeSet_2(nativeHandle, key, (value == null) ? null : value.toArray(new String[0]), expireDurationInSecond);\n    }\n\n    @Nullable\n    public Set<String> decodeStringSet(String key) {\n        return decodeStringSet(key, null);\n    }\n\n    @Nullable\n    public Set<String> decodeStringSet(String key, @Nullable Set<String> defaultValue) {\n        return decodeStringSet(key, defaultValue, HashSet.class);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Nullable\n    public Set<String> decodeStringSet(String key, @Nullable Set<String> defaultValue, Class<? extends Set> cls) {\n        String[] result = decodeStringSet(nativeHandle, key);\n        if (result == null) {\n            return defaultValue;\n        }\n        Set<String> a;\n        try {\n            a = cls.newInstance();\n        } catch (IllegalAccessException | InstantiationException e) {\n            return defaultValue;\n        }\n        a.addAll(Arrays.asList(result));\n        return a;\n    }\n\n    public boolean encode(String key, @Nullable byte[] value) {\n        return encodeBytes(nativeHandle, key, value);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, @Nullable byte[] value, int expireDurationInSecond) {\n        return encodeBytes_2(nativeHandle, key, value, expireDurationInSecond);\n    }\n\n    @Nullable\n    public byte[] decodeBytes(String key) {\n        return decodeBytes(key, null);\n    }\n\n    @Nullable\n    public byte[] decodeBytes(String key, @Nullable byte[] defaultValue) {\n        byte[] ret = decodeBytes(nativeHandle, key);\n        return (ret != null) ? ret : defaultValue;\n    }\n\n    private static final HashMap<String, Parcelable.Creator<?>> mCreators = new HashMap<>();\n\n    private byte[] getParcelableByte(@NonNull Parcelable value) {\n        Parcel source = Parcel.obtain();\n        value.writeToParcel(source, 0);\n        byte[] bytes = source.marshall();\n        source.recycle();\n        return bytes;\n    }\n\n    public boolean encode(String key, @Nullable Parcelable value) {\n        if (value == null) {\n            return encodeBytes(nativeHandle, key, null);\n        }\n        byte[] bytes = getParcelableByte(value);\n        return encodeBytes(nativeHandle, key, bytes);\n    }\n\n    /**\n     * Set value with customize expiration in seconds.\n     *\n     * @param expireDurationInSecond override the default duration, {@link #ExpireNever} (0) means never expire.\n     */\n    public boolean encode(String key, @Nullable Parcelable value, int expireDurationInSecond) {\n        if (value == null) {\n            return encodeBytes_2(nativeHandle, key, null, expireDurationInSecond);\n        }\n        byte[] bytes = getParcelableByte(value);\n        return encodeBytes_2(nativeHandle, key, bytes, expireDurationInSecond);\n    }\n\n    @Nullable\n    public <T extends Parcelable> T decodeParcelable(String key, Class<T> tClass) {\n        return decodeParcelable(key, tClass, null);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Nullable\n    public <T extends Parcelable> T decodeParcelable(String key, Class<T> tClass, @Nullable T defaultValue) {\n        if (tClass == null) {\n            return defaultValue;\n        }\n\n        byte[] bytes = decodeBytes(nativeHandle, key);\n        if (bytes == null) {\n            return defaultValue;\n        }\n\n        Parcel source = Parcel.obtain();\n        source.unmarshall(bytes, 0, bytes.length);\n        source.setDataPosition(0);\n\n        try {\n            String name = tClass.toString();\n            Parcelable.Creator<T> creator;\n            synchronized (mCreators) {\n                creator = (Parcelable.Creator<T>) mCreators.get(name);\n                if (creator == null) {\n                    Field f = tClass.getField(\"CREATOR\");\n                    creator = (Parcelable.Creator<T>) f.get(null);\n                    if (creator != null) {\n                        mCreators.put(name, creator);\n                    }\n                }\n            }\n            if (creator != null) {\n                return creator.createFromParcel(source);\n            } else {\n                throw new Exception(\"Parcelable protocol requires a \"\n                        + \"non-null static Parcelable.Creator object called \"\n                        + \"CREATOR on class \" + name);\n            }\n        } catch (Exception e) {\n            simpleLog(MMKVLogLevel.LevelError, e.toString());\n        } finally {\n            source.recycle();\n        }\n        return defaultValue;\n    }\n\n    /**\n     * Get the actual size consumption of the key's value.\n     * Note: might be a little bigger than value's length.\n     *\n     * @param key The key of the value.\n     */\n    public int getValueSize(String key) {\n        return valueSize(nativeHandle, key, false);\n    }\n\n    /**\n     * Get the actual size of the key's value. String's length or byte[]'s length, etc.\n     *\n     * @param key The key of the value.\n     */\n    public int getValueActualSize(String key) {\n        return valueSize(nativeHandle, key, true);\n    }\n\n    /**\n     * Check whether or not MMKV contains the key.\n     *\n     * @param key The key of the value.\n     */\n    public boolean containsKey(String key) {\n        return containsKey(nativeHandle, key);\n    }\n\n    /**\n     * @return All the keys.\n     */\n    @Nullable\n    public String[] allKeys() {\n        return allKeys(nativeHandle, false);\n    }\n\n    /**\n     * @return All non-expired keys. Note that this call has costs.\n     */\n    @Nullable\n    public String[] allNonExpireKeys() {\n        return allKeys(nativeHandle, true);\n    }\n\n    /**\n     * @return The total count of all the keys.\n     */\n    public long count() {\n        return count(nativeHandle, false);\n    }\n\n    /**\n     * @return The total count of all non-expired keys. Note that this call has costs.\n     */\n    public long countNonExpiredKeys() {\n        return count(nativeHandle, true);\n    }\n\n    /**\n     * Get the size of the underlying file. Align to the disk block size, typically 4K for an Android device.\n     */\n    public long totalSize() {\n        return totalSize(nativeHandle);\n    }\n\n    /**\n     * Get the actual used size of the MMKV instance.\n     * This size might increase and decrease as MMKV doing insertion and full write back.\n     */\n    public long actualSize() {\n        return actualSize(nativeHandle);\n    }\n\n    public void removeValueForKey(String key) {\n        removeValueForKey(nativeHandle, key);\n    }\n\n    /**\n     * Batch remove some keys from the MMKV instance.\n     *\n     * @param arrKeys The keys to be removed.\n     */\n    public native void removeValuesForKeys(String[] arrKeys);\n\n    /**\n     * Clear all the key-values inside the MMKV instance.\n     * The data file will be trimmed down to `pageSize`, and some sync operations will be called\n     * If you do not want to trim the file, use {@link #clearAllWithKeepingSpace()} instead for better performance\n     */\n    public native void clearAll();\n\n    /**\n     * Faster {@link #clearAll()} implementation\n     * The file size is kept as previous for later use\n     */\n    public native void clearAllWithKeepingSpace();\n\n    /**\n     * The {@link #totalSize()} of an MMKV instance won't reduce after deleting key-values,\n     * call this method after lots of deleting if you care about disk usage.\n     * Note that {@link #clearAll()}  has a similar effect.\n     */\n    public native void trim();\n\n    /**\n     * import all key-value items from src\n     * @return count of items imported\n     */\n    public long importFrom(MMKV src) {\n        return importFrom(nativeHandle, src.nativeHandle);\n    }\n\n    /**\n     * Call this method if the MMKV instance is no longer needed in the near future.\n     * Any subsequent call to any MMKV instances with the same ID is undefined behavior.\n     */\n    public native void close();\n\n    /**\n     * Clear memory cache of the MMKV instance.\n     * You can call it on memory warning.\n     * Any subsequent call to the MMKV instance will trigger all key-values loading from the file again.\n     */\n    public native void clearMemoryCache();\n\n    /**\n     * Save all mmap memory to file synchronously.\n     * You don't need to call this, really, I mean it.\n     * Unless you worry about the device running out of battery.\n     */\n    public void sync() {\n        sync(true);\n    }\n\n    /**\n     * Save all mmap memory to file asynchronously.\n     * No need to call this unless you worry about the device running out of battery.\n     */\n    public void async() {\n        sync(false);\n    }\n\n    private native void sync(boolean sync);\n\n    /**\n     * Check whether the MMKV file is valid or not.\n     * Note: Don't use this to check the existence of the instance, the result is undefined on nonexistent files.\n     */\n    public static boolean isFileValid(String mmapID) {\n        return isFileValid(mmapID, null);\n    }\n\n    /**\n     * Check whether the MMKV file is valid or not on customize folder.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     */\n    public static native boolean isFileValid(String mmapID, @Nullable String rootPath);\n\n    /**\n     * remove the storage of the MMKV, including the data file & meta file (.crc)\n     * Note: the existing instance (if any) will be closed & destroyed\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public static boolean removeStorage(String mmapID) {\n        return removeStorage(mmapID, null);\n    }\n\n    /**\n     * remove the storage of the MMKV, including the data file & meta file (.crc)\n     * Note: the existing instance (if any) will be closed & destroyed\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     */\n    public static native boolean removeStorage(String mmapID, @Nullable String rootPath);\n\n    /**\n     * check existence of the MMKV file\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public static boolean checkExist(String mmapID) {\n        return checkExist(mmapID, null);\n    }\n\n    /**\n     * check existence of the MMKV file\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     */\n    public static native boolean checkExist(String mmapID, @Nullable String rootPath);\n\n    /**\n     * Atomically migrate all key-values from an existent SharedPreferences to the MMKV instance.\n     *\n     * @param preferences The SharedPreferences to import from.\n     * @return The total count of key-values imported.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public int importFromSharedPreferences(@NonNull SharedPreferences preferences) {\n        Map<String, ?> kvs = preferences.getAll();\n        if (kvs == null || kvs.size() <= 0) {\n            return 0;\n        }\n\n        for (Map.Entry<String, ?> entry : kvs.entrySet()) {\n            String key = entry.getKey();\n            Object value = entry.getValue();\n            if (key == null || value == null) {\n                continue;\n            }\n\n            if (value instanceof Boolean) {\n                encodeBool(nativeHandle, key, (boolean) value);\n            } else if (value instanceof Integer) {\n                encodeInt(nativeHandle, key, (int) value);\n            } else if (value instanceof Long) {\n                encodeLong(nativeHandle, key, (long) value);\n            } else if (value instanceof Float) {\n                encodeFloat(nativeHandle, key, (float) value);\n            } else if (value instanceof Double) {\n                encodeDouble(nativeHandle, key, (double) value);\n            } else if (value instanceof String) {\n                encodeString(nativeHandle, key, (String) value);\n            } else if (value instanceof Set) {\n                encode(key, (Set<String>) value);\n            } else {\n                simpleLog(MMKVLogLevel.LevelError, \"unknown type: \" + value.getClass());\n            }\n        }\n        return kvs.size();\n    }\n\n    /**\n     * backup one MMKV instance to dstDir\n     *\n     * @param mmapID   the MMKV ID to backup\n     * @param rootPath the customize root path of the MMKV, if null then backup from the root dir of MMKV\n     * @param dstDir   the backup destination directory\n     */\n    public static native boolean backupOneToDirectory(String mmapID, String dstDir, @Nullable String rootPath);\n\n    /**\n     * restore one MMKV instance from srcDir\n     *\n     * @param mmapID   the MMKV ID to restore\n     * @param srcDir   the restore source directory\n     * @param rootPath the customize root path of the MMKV, if null then restore to the root dir of MMKV\n     */\n    public static native boolean restoreOneMMKVFromDirectory(String mmapID, String srcDir, @Nullable String rootPath);\n\n    /**\n     * backup all MMKV instance from default root dir to dstDir\n     *\n     * @param dstDir the backup destination directory\n     * @return count of MMKV successfully backuped\n     */\n    public static native long backupAllToDirectory(String dstDir);\n\n    /**\n     * restore all MMKV instance from srcDir to default root dir\n     *\n     * @param srcDir the restore source directory\n     * @return count of MMKV successfully restored\n     */\n    public static native long restoreAllFromDirectory(String srcDir);\n\n    // TODO: we can't have these functionality because we can't know for sure\n    //  that each instance inside NameSpace has been upgraded successfully or not.\n    //  The workaround is to manually call backup/restore on each instance of NameSpace.\n//    /**\n//     * backup all MMKV instance from srcDir to dstDir\n//     *\n//     * @param dstDir the backup destination directory\n//     * @param srcDir the backup source directory\n//     * @return count of MMKV successfully backuped\n//     */\n//    public static native long backupAllToDirectory(String dstDir, String srcDir);\n//\n//    /**\n//     * restore all MMKV instance from srcDir to dstDir\n//     *\n//     * @param srcDir the restore source directory\n//     * @param dstDir the restore destination directory\n//     * @return count of MMKV successfully restored\n//     */\n//    public static native long restoreAllFromDirectory(String srcDir, String dstDir);\n\n    public static final int ExpireNever = 0;\n    public static final int ExpireInMinute = 60;\n    public static final int ExpireInHour = 60 * 60;\n    public static final int ExpireInDay = 24 * 60 * 60;\n    public static final int ExpireInMonth = 30 * 24 * 60 * 60;\n    public static final int ExpireInYear = 365 * 30 * 24 * 60 * 60;\n\n    /**\n     * Enable auto key expiration. This is a upgrade operation, the file format will change.\n     * And the file won't be accessed correctly by older version (v1.2.16) of MMKV.\n     * NOTICE: enableCompareBeforeSet will be invalid when Expiration is on\n     * @param expireDurationInSecond the expire duration for all keys, {@link #ExpireNever} (0) means no default duration (aka each key will have it's own expire date)\n     */\n    public native boolean enableAutoKeyExpire(int expireDurationInSecond);\n\n    /**\n     * Disable auto key expiration. This is a downgrade operation.\n     */\n    public native boolean disableAutoKeyExpire();\n\n    /**\n     * Enable data compare before set, for better performance.\n     * If data for key seldom changes, use it.\n     * When encryption or expiration is on, compare-before-set will be invalid.\n     * For encryption, compare operation must decrypt data which is time consuming.\n     * For expiration, compare is useless because in most cases the expiration time changes every time.\n     */\n    public void enableCompareBeforeSet() {\n        if (isExpirationEnabled()) {\n            Log.e(\"MMKV\", \"enableCompareBeforeSet is invalid when Expiration is on\");\n            if (BuildConfig.DEBUG) {\n                throw new RuntimeException(\"enableCompareBeforeSet is invalid when Expiration is on\");\n            }\n        }\n        if (isEncryptionEnabled()) {\n            Log.e(\"MMKV\", \"enableCompareBeforeSet is invalid when key encryption is on\");\n            if (BuildConfig.DEBUG) {\n                throw new RuntimeException(\"enableCompareBeforeSet is invalid when Expiration is on\");\n            }\n        }\n        nativeEnableCompareBeforeSet();\n    }\n\n    @FastNative\n    private native void nativeEnableCompareBeforeSet();\n\n    /**\n     * Disable data compare before set\n     * disabled at default\n     */\n    public native void disableCompareBeforeSet();\n\n    /**\n     * Intentionally Not Supported. Because MMKV does type-eraser inside to get better performance.\n     */\n    @Override\n    public Map<String, ?> getAll() {\n        throw new UnsupportedOperationException(\n                \"Intentionally Not Supported. Use allKeys() instead, getAll() not implement because type-erasure inside mmkv\");\n    }\n\n    @Nullable\n    @Override\n    public String getString(String key, @Nullable String defValue) {\n        return decodeString(nativeHandle, key, defValue);\n    }\n\n    @Override\n    public Editor putString(String key, @Nullable String value) {\n        encodeString(nativeHandle, key, value);\n        return this;\n    }\n\n    public Editor putString(String key, @Nullable String value, int expireDurationInSecond) {\n        encodeString_2(nativeHandle, key, value, expireDurationInSecond);\n        return this;\n    }\n\n    @Nullable\n    @Override\n    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {\n        return decodeStringSet(key, defValues);\n    }\n\n    @Override\n    public Editor putStringSet(String key, @Nullable Set<String> values) {\n        encode(key, values);\n        return this;\n    }\n\n    public Editor putStringSet(String key, @Nullable Set<String> values, int expireDurationInSecond) {\n        encode(key, values, expireDurationInSecond);\n        return this;\n    }\n\n    public Editor putBytes(String key, @Nullable byte[] bytes) {\n        encode(key, bytes);\n        return this;\n    }\n\n    public Editor putBytes(String key, @Nullable byte[] bytes, int expireDurationInSecond) {\n        encode(key, bytes, expireDurationInSecond);\n        return this;\n    }\n\n    public byte[] getBytes(String key, @Nullable byte[] defValue) {\n        return decodeBytes(key, defValue);\n    }\n\n    @Override\n    public int getInt(String key, int defValue) {\n        return decodeInt(nativeHandle, key, defValue);\n    }\n\n    @Override\n    public Editor putInt(String key, int value) {\n        encodeInt(nativeHandle, key, value);\n        return this;\n    }\n\n    public Editor putInt(String key, int value, int expireDurationInSecond) {\n        encodeInt_2(nativeHandle, key, value, expireDurationInSecond);\n        return this;\n    }\n\n    @Override\n    public long getLong(String key, long defValue) {\n        return decodeLong(nativeHandle, key, defValue);\n    }\n\n    @Override\n    public Editor putLong(String key, long value) {\n        encodeLong(nativeHandle, key, value);\n        return this;\n    }\n\n    public Editor putLong(String key, long value, int expireDurationInSecond) {\n        encodeLong_2(nativeHandle, key, value, expireDurationInSecond);\n        return this;\n    }\n\n    @Override\n    public float getFloat(String key, float defValue) {\n        return decodeFloat(nativeHandle, key, defValue);\n    }\n\n    @Override\n    public Editor putFloat(String key, float value) {\n        encodeFloat(nativeHandle, key, value);\n        return this;\n    }\n\n    public Editor putFloat(String key, float value, int expireDurationInSecond) {\n        encodeFloat_2(nativeHandle, key, value, expireDurationInSecond);\n        return this;\n    }\n\n    @Override\n    public boolean getBoolean(String key, boolean defValue) {\n        return decodeBool(nativeHandle, key, defValue);\n    }\n\n    @Override\n    public Editor putBoolean(String key, boolean value) {\n        encodeBool(nativeHandle, key, value);\n        return this;\n    }\n\n    public Editor putBoolean(String key, boolean value, int expireDurationInSecond) {\n        encodeBool_2(nativeHandle, key, value, expireDurationInSecond);\n        return this;\n    }\n\n    @Override\n    public Editor remove(String key) {\n        removeValueForKey(key);\n        return this;\n    }\n\n    /**\n     * {@link #clearAll()}\n     */\n    @Override\n    public Editor clear() {\n        clearAll();\n        return this;\n    }\n\n    /**\n     * @deprecated This method is only for compatibility purpose. You should remove all the calls after migration to MMKV.\n     * MMKV doesn't rely on commit() to save data to file.\n     * If you really worry about losing battery and data corruption, call {@link #async()} or {@link #sync()} instead.\n     */\n    @Override\n    @Deprecated\n    public boolean commit() {\n        sync(true);\n        return true;\n    }\n\n    /**\n     * @deprecated This method is only for compatibility purpose. You should remove all the calls after migration to MMKV.\n     * MMKV doesn't rely on apply() to save data to file.\n     * If you really worry about losing battery and data corruption, call {@link #async()} instead.\n     */\n    @Override\n    @Deprecated\n    public void apply() {\n        sync(false);\n    }\n\n    @Override\n    public boolean contains(String key) {\n        return containsKey(key);\n    }\n\n    @Override\n    public Editor edit() {\n        return this;\n    }\n\n    /**\n     * Intentionally Not Supported by MMKV. We believe it's better not for a storage framework to notify the change of data.\n     * Check {@link #registerContentChangeNotify} for a potential replacement on inter-process scene.\n     */\n    @Override\n    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        throw new UnsupportedOperationException(\"Intentionally Not implement in MMKV\");\n    }\n\n    /**\n     * Intentionally Not Supported by MMKV. We believe it's better not for a storage framework to notify the change of data.\n     */\n    @Override\n    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        throw new UnsupportedOperationException(\"Intentionally Not implement in MMKV\");\n    }\n\n    /**\n     * Get an ashmem MMKV instance that has been initiated by another process.\n     * Normally you should just call {@link #mmkvWithAshmemID(Context, String, int, int, String)} instead.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param fd       The file descriptor of the ashmem of the MMKV file, transferred from another process by binder.\n     * @param metaFD   The file descriptor of the ashmem of the MMKV crc file, transferred from another process by binder.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @throws RuntimeException If any failure in JNI or runtime.\n     */\n    // Parcelable\n    @NonNull\n    @Contract(\"_, _, _, _ -> new\")\n    public static MMKV mmkvWithAshmemFD(String mmapID, int fd, int metaFD, String cryptKey) throws RuntimeException {\n        return mmkvWithAshmemFD(mmapID, fd, metaFD, cryptKey, false);\n    }\n\n    /**\n     * Get an ashmem MMKV instance that has been initiated by another process.\n     * Normally you should just call {@link #mmkvWithAshmemID(Context, String, int, int, String)} instead.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param fd       The file descriptor of the ashmem of the MMKV file, transferred from another process by binder.\n     * @param metaFD   The file descriptor of the ashmem of the MMKV crc file, transferred from another process by binder.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @throws RuntimeException If any failure in JNI or runtime.\n     */\n    // Parcelable\n    @NonNull\n    @Contract(\"_, _, _, _, _ -> new\")\n    public static MMKV mmkvWithAshmemFD(String mmapID, int fd, int metaFD, String cryptKey, boolean aes256) throws RuntimeException {\n        long handle = getMMKVWithAshmemFD(mmapID, fd, metaFD, cryptKey, aes256);\n        if (handle == 0) {\n            throw new RuntimeException(\"Fail to create an ashmem MMKV instance [\" + mmapID + \"] in JNI\");\n        }\n        return new MMKV(handle);\n    }\n\n    /**\n     * @return The file descriptor of the ashmem of the MMKV file.\n     */\n    public native int ashmemFD();\n\n    /**\n     * @return The file descriptor of the ashmem of the MMKV crc file.\n     */\n    public native int ashmemMetaFD();\n\n    /**\n     * Create an native buffer, whose underlying memory can be directly transferred to another JNI method.\n     * Avoiding unnecessary JNI boxing and unboxing.\n     * An NativeBuffer must be manually {@link #destroyNativeBuffer} to avoid memory leak.\n     *\n     * @param size The size of the underlying memory.\n     */\n    @Nullable\n    public static NativeBuffer createNativeBuffer(int size) {\n        long pointer = createNB(size);\n        if (pointer == 0) {\n            return null;\n        }\n        return new NativeBuffer(pointer, size);\n    }\n\n    /**\n     * Destroy the native buffer. An NativeBuffer must be manually destroy to avoid memory leak.\n     */\n    public static void destroyNativeBuffer(@NonNull NativeBuffer buffer) {\n        destroyNB(buffer.pointer, buffer.size);\n    }\n\n    /**\n     * Write the value of the key to the native buffer.\n     *\n     * @return The size written. Return -1 on any error.\n     */\n    public int writeValueToNativeBuffer(String key, @NonNull NativeBuffer buffer) {\n        return writeValueToNB(nativeHandle, key, buffer.pointer, buffer.size);\n    }\n\n    // callback handler\n    private static MMKVHandler gCallbackHandler = null;\n    private static boolean gWantLogReDirecting = false;\n\n    /**\n     * Register a handler for MMKV log redirecting, and error handling.\n     *\n     * @deprecated This method is deprecated.\n     * Use the {@link #initialize(Context, String, LibLoader, MMKVLogLevel, MMKVHandler)} method instead.\n     */\n    public static void registerHandler(MMKVHandler handler) {\n        gCallbackHandler = handler;\n        gWantLogReDirecting = gCallbackHandler.wantLogRedirecting();\n        long nativeLogHandler = gCallbackHandler.getNativeLogHandler();\n        setCallbackHandler(gWantLogReDirecting, true, nativeLogHandler);\n        if (gCallbackHandler.wantContentChangeNotification()) {\n            setWantsContentChangeNotify(true);\n        }\n    }\n\n    /**\n     * Unregister the handler for MMKV.\n     */\n    public static void unregisterHandler() {\n        gCallbackHandler = null;\n\n        setCallbackHandler(false, false, 0);\n        gWantLogReDirecting = false;\n        setWantsContentChangeNotify(gContentChangeNotify != null);\n    }\n\n    private static int onMMKVCRCCheckFail(String mmapID) {\n        MMKVRecoverStrategic strategic = MMKVRecoverStrategic.OnErrorDiscard;\n        MMKVHandler handler = gCallbackHandler;\n        if (handler != null) {\n            strategic = handler.onMMKVCRCCheckFail(mmapID);\n        }\n        simpleLog(MMKVLogLevel.LevelInfo, \"Recover strategic for \" + mmapID + \" is \" + strategic);\n        Integer value = recoverIndex.get(strategic);\n        return (value == null) ? 0 : value;\n    }\n\n    private static int onMMKVFileLengthError(String mmapID) {\n        MMKVRecoverStrategic strategic = MMKVRecoverStrategic.OnErrorDiscard;\n        MMKVHandler handler = gCallbackHandler;\n        if (handler != null) {\n            strategic = handler.onMMKVFileLengthError(mmapID);\n        }\n        simpleLog(MMKVLogLevel.LevelInfo, \"Recover strategic for \" + mmapID + \" is \" + strategic);\n        Integer value = recoverIndex.get(strategic);\n        return (value == null) ? 0 : value;\n    }\n\n    private static void mmkvLogImp(int level, String file, int line, String function, String message) {\n        MMKVHandler handler = gCallbackHandler;\n        if (handler != null && gWantLogReDirecting) {\n            handler.mmkvLog(index2LogLevel[level], file, line, function, message);\n        } else {\n            switch (index2LogLevel[level]) {\n                case LevelDebug:\n                    Log.d(\"MMKV\", message);\n                    break;\n                case LevelInfo:\n                    Log.i(\"MMKV\", message);\n                    break;\n                case LevelWarning:\n                    Log.w(\"MMKV\", message);\n                    break;\n                case LevelError:\n                    Log.e(\"MMKV\", message);\n                    break;\n                case LevelNone:\n                    break;\n            }\n        }\n    }\n\n    private static void simpleLog(MMKVLogLevel level, String message) {\n        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();\n        StackTraceElement e = stacktrace[stacktrace.length - 1];\n        Integer i = logLevel2Index.get(level);\n        int intLevel = (i == null) ? 0 : i;\n        mmkvLogImp(intLevel, e.getFileName(), e.getLineNumber(), e.getMethodName(), message);\n    }\n\n    // content change notification of other process\n    // trigger by getXXX() or setXXX() or checkContentChangedByOuterProcess()\n    private static MMKVContentChangeNotification gContentChangeNotify;\n\n    /**\n     * Register for MMKV inter-process content change notification.\n     * The notification will trigger only when any method is manually called on the MMKV instance.\n     * For example {@link #checkContentChangedByOuterProcess()}.\n     *\n     * @param notify The notification handler.\n     * @deprecated Use {@link MMKVHandler#onContentChangedByOuterProcess(String)} instead.\n     */\n    @Deprecated\n    public static void registerContentChangeNotify(MMKVContentChangeNotification notify) {\n        gContentChangeNotify = notify;\n        setWantsContentChangeNotify(gContentChangeNotify != null || (gCallbackHandler != null && gCallbackHandler.wantContentChangeNotification()));\n    }\n\n    /**\n     * Unregister for MMKV inter-process content change notification.\n     * @deprecated Use {@link MMKVHandler#onContentChangedByOuterProcess(String)} instead.\n     */\n    @Deprecated\n    public static void unregisterContentChangeNotify() {\n        gContentChangeNotify = null;\n        setWantsContentChangeNotify(gCallbackHandler != null && gCallbackHandler.wantContentChangeNotification());\n    }\n\n    private static void onContentChangedByOuterProcess(String mmapID) {\n        MMKVHandler handler = gCallbackHandler;\n        if (handler != null && handler.wantContentChangeNotification()) {\n            handler.onContentChangedByOuterProcess(mmapID);\n        } else if (gContentChangeNotify != null) {\n            gContentChangeNotify.onContentChangedByOuterProcess(mmapID);\n        }\n    }\n\n    private static void onMMKVContentLoadSuccessfully(String mmapID) {\n        MMKVHandler handler = gCallbackHandler;\n        if (handler != null) {\n            handler.onMMKVContentLoadSuccessfully(mmapID);\n        }\n    }\n\n    private static native void setWantsContentChangeNotify(boolean needsNotify);\n\n    /**\n     * Check inter-process content change manually.\n     */\n    public native void checkContentChangedByOuterProcess();\n\n    /**\n     * Check if this instance is in multi-process mode.\n     */\n    public native boolean isMultiProcess();\n\n    /**\n     * Check if this instance is in read-only mode.\n     */\n    public native boolean isReadOnly();\n\n    // jni\n    private final long nativeHandle;\n\n    private MMKV(long handle) {\n        nativeHandle = handle;\n    }\n\n    private static native void jniInitialize(String rootDir, String cacheDir, int level, boolean wantLogReDirecting, boolean hasCallback, long nativeHandler);\n\n    native static long\n    getMMKVWithID(String mmapID, int mode, @Nullable String cryptKey, @Nullable String rootPath,\n                  long expectedCapacity, boolean aes256, int enableKeyExpire, int expiredInSeconds,\n                  boolean enableCompareBeforeSet, int recover, int itemSizeLimit);\n\n    private native static long getDefaultMMKV(int mode, @Nullable String cryptKey, long expectedCapacity,\n                                              boolean aes256, int enableKeyExpire, int expiredInSeconds,\n                                              boolean enableCompareBeforeSet, int recover, int itemSizeLimit);\n\n    private native static long getMMKVWithAshmemFD(String mmapID, int fd, int metaFD, @Nullable String cryptKey, boolean aes256);\n\n    private native boolean encodeBool(long handle, String key, boolean value);\n\n    private native boolean encodeBool_2(long handle, String key, boolean value, int expireDurationInSecond);\n\n    private native boolean decodeBool(long handle, String key, boolean defaultValue);\n\n    private native boolean encodeInt(long handle, String key, int value);\n\n    private native boolean encodeInt_2(long handle, String key, int value, int expireDurationInSecond);\n\n    private native int decodeInt(long handle, String key, int defaultValue);\n\n    private native boolean encodeLong(long handle, String key, long value);\n\n    private native boolean encodeLong_2(long handle, String key, long value, int expireDurationInSecond);\n\n    private native long decodeLong(long handle, String key, long defaultValue);\n\n    private native boolean encodeFloat(long handle, String key, float value);\n\n    private native boolean encodeFloat_2(long handle, String key, float value, int expireDurationInSecond);\n\n    private native float decodeFloat(long handle, String key, float defaultValue);\n\n    private native boolean encodeDouble(long handle, String key, double value);\n\n    private native boolean encodeDouble_2(long handle, String key, double value, int expireDurationInSecond);\n\n    private native double decodeDouble(long handle, String key, double defaultValue);\n\n    private native boolean encodeString(long handle, String key, @Nullable String value);\n\n    private native boolean encodeString_2(long handle, String key, @Nullable String value, int expireDurationInSecond);\n\n    @Nullable\n    private native String decodeString(long handle, String key, @Nullable String defaultValue);\n\n    private native boolean encodeSet(long handle, String key, @Nullable String[] value);\n\n    private native boolean encodeSet_2(long handle, String key, @Nullable String[] value, int expireDurationInSecond);\n\n    @Nullable\n    private native String[] decodeStringSet(long handle, String key);\n\n    private native boolean encodeBytes(long handle, String key, @Nullable byte[] value);\n\n    private native boolean encodeBytes_2(long handle, String key, @Nullable byte[] value, int expireDurationInSecond);\n\n    @Nullable\n    private native byte[] decodeBytes(long handle, String key);\n\n    private native boolean containsKey(long handle, String key);\n\n    private native String[] allKeys(long handle, boolean filterExpire);\n\n    private native long count(long handle, boolean filterExpire);\n\n    private native long totalSize(long handle);\n\n    private native long actualSize(long handle);\n\n    private native void removeValueForKey(long handle, String key);\n\n    private native int valueSize(long handle, String key, boolean actualSize);\n\n    private static native void setLogLevel(int level);\n\n    private static native void setCallbackHandler(boolean logReDirecting, boolean hasCallback, long nativeHandle);\n\n    private static native long createNB(int size);\n\n    private static native void destroyNB(long pointer, int size);\n\n    private native int writeValueToNB(long handle, String key, long pointer, int size);\n\n    private native boolean isCompareBeforeSetEnabled();\n\n    @FastNative\n    private native boolean isEncryptionEnabled();\n\n    @FastNative\n    private native boolean isExpirationEnabled();\n\n    private static native void enableDisableProcessMode(boolean enable);\n\n    private static native boolean checkProcessMode(long handle);\n\n    private static native boolean getNameSpace(String rootPath);\n\n    private native long importFrom(long handle, long srcHandle);\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVConfig.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * The all-in-one configuration for creating an MMKV instance.\n */\npublic class MMKVConfig {\n    public int mode = MMKV.SINGLE_PROCESS_MODE;\n\n    // using AES-256 key length\n    public boolean aes256 = false;\n\n    public String cryptKey = null;\n\n    public String rootPath = null;\n\n    public long expectedCapacity = 0; // the initial file size\n\n    public Boolean enableKeyExpire = null;\n    public int expiredInSeconds = 0; // ExpireNever = 0\n\n    public boolean enableCompareBeforeSet = false;\n\n    // if not set, use the old style callback\n    public MMKVRecoverStrategic recover = null;\n\n    // the size limit of a key-value pair, reject insert if pass limit\n    public int itemSizeLimit = 0;\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentChangeNotification.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * Inter-process content change notification.\n * Triggered by any method call, such as getXXX() or setXXX() or {@link MMKV#checkContentChangedByOuterProcess()}.\n * @deprecated Use {@link MMKVHandler#onContentChangedByOuterProcess(String)} instead.\n */\n@Deprecated\npublic interface MMKVContentChangeNotification {\n    /**\n     * Inter-process content change notification.\n     * Triggered by any method call, such as getXXX() or setXXX() or {@link MMKV#checkContentChangedByOuterProcess()}.\n     * @param mmapID The unique ID of the changed MMKV instance.\n     */\n    void onContentChangedByOuterProcess(String mmapID);\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVContentProvider.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.app.ActivityManager;\nimport android.content.ComponentName;\nimport android.content.ContentProvider;\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.Log;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * A helper class for MMKV based on Anonymous Shared Memory. {@link MMKV#mmkvWithAshmemID}\n */\npublic class MMKVContentProvider extends ContentProvider {\n\n    static protected final String KEY = \"KEY\";\n    static protected final String KEY_SIZE = \"KEY_SIZE\";\n    static protected final String KEY_MODE = \"KEY_MODE\";\n    static protected final String KEY_CRYPT = \"KEY_CRYPT\";\n    static protected final String FUNCTION_NAME = \"mmkvFromAshmemID\";\n\n    static private Uri gUri;\n    @Nullable\n    static protected Uri contentUri(Context context) {\n        if (MMKVContentProvider.gUri != null) {\n            return MMKVContentProvider.gUri;\n        }\n        if (context == null) {\n            return null;\n        }\n        String authority = queryAuthority(context);\n        if (authority == null) {\n            return null;\n        }\n        MMKVContentProvider.gUri = Uri.parse(ContentResolver.SCHEME_CONTENT + \"://\" + authority);\n        return MMKVContentProvider.gUri;\n    }\n\n    @NonNull\n    private Bundle mmkvFromAshmemID(String ashmemID, int size, int mode, String cryptKey) throws RuntimeException {\n        MMKV mmkv = MMKV.mmkvWithAshmemID(getContext(), ashmemID, size, mode, cryptKey);\n        ParcelableMMKV parcelableMMKV = new ParcelableMMKV(mmkv);\n        Log.i(\"MMKV\", ashmemID + \" fd = \" + mmkv.ashmemFD() + \", meta fd = \" + mmkv.ashmemMetaFD());\n        Bundle result = new Bundle();\n        result.putParcelable(MMKVContentProvider.KEY, parcelableMMKV);\n        return result;\n    }\n\n    @Nullable\n    private static String queryAuthority(Context context) {\n        try {\n            ComponentName componentName = new ComponentName(context, MMKVContentProvider.class.getName());\n            PackageManager mgr = context.getPackageManager();\n            if (mgr != null) {\n                ProviderInfo providerInfo = mgr.getProviderInfo(componentName, 0);\n                if (providerInfo != null) {\n                    return providerInfo.authority;\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    @Override\n    public boolean onCreate() {\n        Context context = getContext();\n        if (context == null) {\n            return false;\n        }\n\n        return true;\n    }\n\n    protected static String getProcessNameByPID(@NonNull Context context, int pid) {\n        if (pid == android.os.Process.myPid()) {\n            return MMKVProcessUtil.getCurrentProcessName(context);\n        }\n        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (manager != null) {\n            // clang-format off\n            for (ActivityManager.RunningAppProcessInfo processInfo\n                    : manager.getRunningAppProcesses()) {\n                if (processInfo.pid == pid) {\n                    return processInfo.processName;\n                }\n            }\n            // clang-format on\n        }\n        return \"\";\n    }\n\n    @Nullable\n    @Override\n    public Bundle call(@NonNull String method, @Nullable String mmapID, @Nullable Bundle extras) {\n        if (method.equals(MMKVContentProvider.FUNCTION_NAME)) {\n            if (extras != null) {\n                int size = extras.getInt(MMKVContentProvider.KEY_SIZE);\n                int mode = extras.getInt(MMKVContentProvider.KEY_MODE);\n                String cryptKey = extras.getString(MMKVContentProvider.KEY_CRYPT);\n                try {\n                    return mmkvFromAshmemID(mmapID, size, mode, cryptKey);\n                } catch (Exception e) {\n                    Log.e(\"MMKV\", e.getMessage());\n                    return null;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Nullable\n    @Override\n    public String getType(@NonNull Uri uri) {\n        return null;\n    }\n\n    @Nullable\n    @Override\n    public Cursor query(@NonNull Uri uri,\n                        @Nullable String[] projection,\n                        @Nullable String selection,\n                        @Nullable String[] selectionArgs,\n                        @Nullable String sortOrder) {\n        throw new java.lang.UnsupportedOperationException(\"Not implement in MMKV\");\n    }\n\n    @Override\n    public int update(@NonNull Uri uri,\n                      @Nullable ContentValues values,\n                      @Nullable String selection,\n                      @Nullable String[] selectionArgs) {\n        throw new java.lang.UnsupportedOperationException(\"Not implement in MMKV\");\n    }\n\n    @Override\n    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {\n        throw new java.lang.UnsupportedOperationException(\"Not implement in MMKV\");\n    }\n\n    @Nullable\n    @Override\n    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {\n        throw new java.lang.UnsupportedOperationException(\"Not implement in MMKV\");\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVHandler.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * Unified callback handler for MMKV.\n * Callback is called on the operating thread of the MMKV instance.\n */\npublic interface MMKVHandler {\n    /**\n     * By default MMKV will discard all data on crc32-check failure. {@link MMKVRecoverStrategic#OnErrorDiscard}\n     * @param mmapID The unique ID of the MMKV instance.\n     * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.\n     */\n    MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID);\n\n    /**\n     * By default MMKV will discard all data on file length mismatch. {@link MMKVRecoverStrategic#OnErrorDiscard}\n     * @param mmapID The unique ID of the MMKV instance.\n     * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.\n     */\n    MMKVRecoverStrategic onMMKVFileLengthError(String mmapID);\n\n    /**\n     * @return Return False if you don't want log redirecting.\n     */\n    boolean wantLogRedirecting();\n\n    /**\n     * Log Redirecting.\n     * @param level The level of this log.\n     * @param file The file name of this log.\n     * @param line The line of code of this log.\n     * @param function The function name of this log.\n     * @param message The content of this log.\n     */\n    void mmkvLog(MMKVLogLevel level, String file, int line, String function, String message);\n\n    /**\n     * handle the log in NDK native code\n     * @return a native log handler with signature of void log(int level, const char *file, int line, const char *function, const char *message)\n     * return 0 to indicate no native handler\n     */\n    default long getNativeLogHandler() { return 0; }\n\n    /**\n     * @return Return true if you want inter-process content change notification.\n     */\n    default boolean wantContentChangeNotification() { return false; }\n\n    /**\n     * Inter-process content change notification.\n     * Triggered by any method call, such as getXXX() or setXXX() or {@link MMKV#checkContentChangedByOuterProcess()}.\n     * @param mmapID The unique ID of the changed MMKV instance.\n     */\n    default void onContentChangedByOuterProcess(String mmapID) {}\n\n    /**\n     * Called when an MMKV file is loaded successfully.\n     * @param mmapID The unique ID of the loaded MMKV instance.\n     */\n    default void onMMKVContentLoadSuccessfully(String mmapID) {}\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVLogLevel.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * The levels of MMKV log.\n */\npublic enum MMKVLogLevel {\n    /**\n     * Debug level. Not available for release/production build.\n     */\n    LevelDebug,\n\n    /**\n     * Info level. The default level.\n     */\n    LevelInfo,\n\n    /**\n     * Warning level.\n     */\n    LevelWarning,\n\n    /**\n     * Error level.\n     */\n    LevelError,\n\n    /**\n     * Special level for disabling all logging.\n     * It's highly NOT suggested to turn off logging. Makes it hard to diagnose online/production bugs.\n     */\n    LevelNone\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVProcessUtil.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.content.Context;\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport androidx.annotation.NonNull;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\n/**\n * Get current process name for AppStore review\n */\nclass MMKVProcessUtil {\n\n    private static String currentProcessName = \"\";\n\n    public static String getCurrentProcessName(@NonNull Context context) {\n        if (!TextUtils.isEmpty(currentProcessName)) {\n            return currentProcessName;\n        }\n\n        currentProcessName = getCurrentProcessNameByApplication();\n        if (!TextUtils.isEmpty(currentProcessName)) {\n            return currentProcessName;\n        }\n\n        currentProcessName = getCurrentProcessNameByActivityThread();\n        if (!TextUtils.isEmpty(currentProcessName)) {\n            return currentProcessName;\n        }\n\n        currentProcessName = getCurrentProcessNameByActivityManager(context);\n        return currentProcessName;\n    }\n\n    @NonNull\n    private static String getCurrentProcessNameByApplication() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            return Application.getProcessName();\n        }\n        return \"\";\n    }\n\n    @NonNull\n    private static String getCurrentProcessNameByActivityThread() {\n        String processName = \"\";\n        try {\n            Method declaredMethod = Class.forName(\"android.app.ActivityThread\").\n                    getDeclaredMethod(\"currentProcessName\");\n            declaredMethod.setAccessible(true);\n            final Object invoke = declaredMethod.invoke(null);\n            if (invoke instanceof String) {\n                processName = (String) invoke;\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        return processName;\n    }\n\n    private static String getCurrentProcessNameByActivityManager(@NonNull Context context) {\n        int pid = android.os.Process.myPid();\n        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (am != null) {\n            List<ActivityManager.RunningAppProcessInfo> runningAppList = am.getRunningAppProcesses();\n            if (runningAppList != null) {\n                for (ActivityManager.RunningAppProcessInfo processInfo : runningAppList) {\n                    if (processInfo.pid == pid) {\n                        return processInfo.processName;\n                    }\n                }\n            }\n        }\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/MMKVRecoverStrategic.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * The recover strategic of MMKV on errors. {@link MMKV#registerHandler}\n */\npublic enum MMKVRecoverStrategic {\n    /**\n     * The default strategic is to discard everything on errors.\n     */\n    OnErrorDiscard,\n\n    /**\n     * The recover strategic will try to recover as much data as possible.\n     */\n    OnErrorRecover,\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/NameSpace.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport static com.tencent.mmkv.MMKV.SINGLE_PROCESS_MODE;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * a facade wraps custom root directory\n */\npublic final class NameSpace {\n    private final String rootDir;\n\n    // make it internal\n    NameSpace(String dir) {\n        rootDir = dir;\n    }\n\n    /**\n     * @return The root folder of this NameSpace.\n     */\n    public String getRootDir() {\n        return rootDir;\n    }\n\n    /**\n     * Create an MMKV instance with an unique ID (in single-process mode).\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID) throws RuntimeException {\n        return mmkvWithID(mmapID, new MMKVConfig());\n    }\n\n    /**\n     * Create an MMKV instance with an unique ID (in single-process mode).\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param config The all-in-one configuration for the MMKV instance.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, MMKVConfig config) throws RuntimeException {\n        int enableKeyExpire = (config.enableKeyExpire != null) ? (config.enableKeyExpire ? 1 : 0) : -1;\n        int recover = (config.recover == null) ? -1 : ((config.recover == MMKVRecoverStrategic.OnErrorDiscard) ? 0 : 1);\n\n        long handle = MMKV.getMMKVWithID(mmapID, config.mode, config.cryptKey, rootDir, config.expectedCapacity,\n                config.aes256, enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet,\n                recover, config.itemSizeLimit);\n\n        return MMKV.checkProcessMode(handle, mmapID, config.mode);\n    }\n\n    /**\n     * Create an MMKV instance in single-process or multi-process mode.\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode   The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in single-process or multi-process mode.\n     *\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode   The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode, long expectedCapacity) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance in customize process mode, with an encryption key.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256) throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, long expectedCapacity)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.cryptKey = cryptKey;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * Create an MMKV instance with customize settings all in one.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 32 bytes).\n     * @param aes256   Use AES 256 key length.\n     * @param expectedCapacity The file size you expected when opening or creating file\n     * @throws RuntimeException if there's an runtime error.\n     */\n    @NonNull\n    public MMKV mmkvWithID(String mmapID, int mode, @Nullable String cryptKey, boolean aes256, long expectedCapacity)\n            throws RuntimeException {\n        MMKVConfig config = new MMKVConfig();\n        config.mode = mode;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey;\n        config.expectedCapacity = expectedCapacity;\n        return mmkvWithID(mmapID, config);\n    }\n\n    /**\n     * backup one MMKV instance to dstDir\n     *\n     * @param mmapID   the MMKV ID to backup\n     * @param dstDir   the backup destination directory\n     */\n    public boolean backupOneToDirectory(String mmapID, String dstDir) {\n        return MMKV.backupOneToDirectory(mmapID, dstDir, rootDir);\n    }\n\n    /**\n     * restore one MMKV instance from srcDir\n     *\n     * @param mmapID   the MMKV ID to restore\n     * @param srcDir   the restore source directory\n     */\n    public boolean restoreOneMMKVFromDirectory(String mmapID, String srcDir) {\n        return MMKV.restoreOneMMKVFromDirectory(mmapID, srcDir, rootDir);\n    }\n\n    // TODO: we can't have these functionality because we can't know for sure\n    //  that each instance inside NameSpace has been upgraded successfully or not.\n    //  The workaround is to manually call backup/restore on each instance of NameSpace.\n//    /**\n//     * backup all MMKV instance to dstDir\n//     *\n//     * @param dstDir the backup destination directory\n//     * @return count of MMKV successfully backuped\n//     */\n//    public long backupAllToDirectory(String dstDir) {\n//        return MMKV.backupAllToDirectory(dstDir, rootDir);\n//    }\n//\n//    /**\n//     * restore all MMKV instance from srcDir\n//     *\n//     * @param srcDir the restore source directory\n//     * @return count of MMKV successfully restored\n//     */\n//    public long restoreAllFromDirectory(String srcDir) {\n//        return MMKV.restoreAllFromDirectory(srcDir, rootDir);\n//    }\n\n    /**\n     * Check whether the MMKV file is valid or not.\n     * Note: Don't use this to check the existence of the instance, the result is undefined on nonexistent files.\n     */\n    public boolean isFileValid(String mmapID) {\n        return MMKV.isFileValid(mmapID, rootDir);\n    }\n\n    /**\n     * remove the storage of the MMKV, including the data file & meta file (.crc)\n     * Note: the existing instance (if any) will be closed & destroyed\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public boolean removeStorage(String mmapID) {\n        return MMKV.removeStorage(mmapID, rootDir);\n    }\n\n    /**\n     * check existence of the MMKV file\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public boolean checkExist(String mmapID) {\n        return MMKV.checkExist(mmapID, rootDir);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/NativeBuffer.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\n/**\n * A native memory wrapper, whose underlying memory can be passed to another JNI method directly.\n * Avoiding unnecessary JNI boxing and unboxing.\n * Must be destroy manually {@link MMKV#destroyNativeBuffer}.\n */\npublic final class NativeBuffer {\n    public long pointer;\n    public int size;\n\n    public NativeBuffer(long ptr, int length) {\n        pointer = ptr;\n        size = length;\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/ParcelableMMKV.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.os.Parcel;\nimport android.os.ParcelFileDescriptor;\nimport android.os.Parcelable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport org.jetbrains.annotations.Contract;\n\nimport java.io.IOException;\n\n/**\n * A helper class for MMKV based on Anonymous Shared Memory. {@link MMKV#mmkvWithAshmemID}\n */\npublic final class ParcelableMMKV implements Parcelable {\n    private final String mmapID;\n    private int ashmemFD = -1;\n    private int ashmemMetaFD = -1;\n    private String cryptKey = null;\n\n    public ParcelableMMKV(@NonNull MMKV mmkv) {\n        mmapID = mmkv.mmapID();\n        ashmemFD = mmkv.ashmemFD();\n        ashmemMetaFD = mmkv.ashmemMetaFD();\n        cryptKey = mmkv.cryptKey();\n    }\n\n    private ParcelableMMKV(String id, int fd, int metaFD, String key) {\n        mmapID = id;\n        ashmemFD = fd;\n        ashmemMetaFD = metaFD;\n        cryptKey = key;\n    }\n\n    @Nullable\n    public MMKV toMMKV() {\n        if (ashmemFD >= 0 && ashmemMetaFD >= 0) {\n            return MMKV.mmkvWithAshmemFD(mmapID, ashmemFD, ashmemMetaFD, cryptKey);\n        }\n        return null;\n    }\n\n    @Override\n    public int describeContents() {\n        return CONTENTS_FILE_DESCRIPTOR;\n    }\n\n    @Override\n    public void writeToParcel(@NonNull Parcel dest, int flags) {\n        try {\n            dest.writeString(mmapID);\n            ParcelFileDescriptor fd = ParcelFileDescriptor.fromFd(ashmemFD);\n            ParcelFileDescriptor metaFD = ParcelFileDescriptor.fromFd(ashmemMetaFD);\n            flags = flags | Parcelable.PARCELABLE_WRITE_RETURN_VALUE;\n            fd.writeToParcel(dest, flags);\n            metaFD.writeToParcel(dest, flags);\n            if (cryptKey != null) {\n                dest.writeString(cryptKey);\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static final Parcelable.Creator<ParcelableMMKV> CREATOR = new Parcelable.Creator<ParcelableMMKV>() {\n        @Nullable\n        @Override\n        public ParcelableMMKV createFromParcel(@NonNull Parcel source) {\n            String mmapID = source.readString();\n            ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(source);\n            ParcelFileDescriptor metaFD = ParcelFileDescriptor.CREATOR.createFromParcel(source);\n            String cryptKey = source.readString();\n            if (fd != null && metaFD != null) {\n                return new ParcelableMMKV(mmapID, fd.detachFd(), metaFD.detachFd(), cryptKey);\n            }\n            return null;\n        }\n\n        @NonNull\n        @Contract(value = \"_ -> new\", pure = true)\n        @Override\n        public ParcelableMMKV[] newArray(int size) {\n            return new ParcelableMMKV[size];\n        }\n    };\n}\n"
  },
  {
    "path": "Android/MMKV/mmkv/src/main/java/com/tencent/mmkv/UnsupportedArchitectureException.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\npublic class UnsupportedArchitectureException extends RuntimeException {\n    public UnsupportedArchitectureException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvannotation/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "Android/MMKV/mmkvannotation/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    namespace \"com.tencent.mmkv\"\n\n    compileSdk rootProject.ext.compileSdk\n\n    defaultConfig {\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        minSdkVersion rootProject.ext.minSdkVersion\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            consumerProguardFiles 'proguard-rules.pro'\n        }\n    }\n\n\n}\n\nconfigurations {\n    javadocDeps\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'androidx.annotation:annotation:1.9.1'\n    javadocDeps 'androidx.annotation:annotation:1.9.1'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.6.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvannotation/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n</manifest>"
  },
  {
    "path": "Android/MMKV/mmkvannotation/src/main/java/dalvik/annotation/optimization/FastNative.java",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage dalvik.annotation.optimization;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n/**\n * An ART runtime built-in optimization for {@code native} methods to speed up JNI transitions:\n * Compared to normal {@code native} methods, {@code native} methods that are annotated with\n * {@literal @}{@code FastNative} use faster JNI transitions from managed code to the native code\n * and back. Calls from a {@literal @}{@code FastNative} method implementation to JNI functions\n * that access the managed heap or call managed code also have faster internal transitions.\n *\n * <p>\n * While executing a {@literal @}{@code FastNative} method, the garbage collection cannot\n * suspend the thread for essential work and may become blocked. Use with caution. Do not use\n * this annotation for long-running methods, including usually-fast, but generally unbounded,\n * methods. In particular, the code should not perform significant I/O operations or acquire\n * native locks that can be held for a long time. (Some logging or native allocations, which\n * internally acquire native locks for a short time, are generally OK. However, as the cost\n * of several such operations adds up, the {@literal @}{@code FastNative} performance gain\n * can become insignificant and overshadowed by potential GC delays.)\n * Acquiring managed locks is OK as it internally allows thread suspension.\n * </p>\n *\n * <p>\n * For performance critical methods that need this annotation, it is strongly recommended\n * to explicitly register the method(s) with JNI {@code RegisterNatives} instead of relying\n * on the built-in dynamic JNI linking.\n * </p>\n *\n * <p>\n * The {@literal @}{@code FastNative} optimization was implemented for system use since\n * Android 8 and became CTS-tested public API in Android 14. Developers aiming for maximum\n * compatibility should avoid calling {@literal @}{@code FastNative} methods on Android 13-.\n * The optimization is likely to work also on Android 8-13 devices (after all, it was used\n * in the system, albeit without the strong CTS guarantees), especially those that use\n * unmodified versions of ART, such as Android 12+ devices with the official ART Module.\n * The built-in dynamic JNI linking is working only in Android 12+, the explicit registration\n * with JNI {@code RegisterNatives} is strictly required for running on Android versions 8-11.\n * The annotation is ignored on Android 7-.\n * </p>\n *\n * <p>\n * <b>Deadlock Warning:</b> As a rule of thumb, any native locks acquired in a\n * {@literal @}{@link FastNative} call (despite the above warning that this is an unbounded\n * operation that can block GC for a long time) must be released before returning to managed code.\n * </p>\n *\n * <p>\n * Say some code does:\n *\n * <code>\n * fast_jni_call_to_grab_a_lock();\n * does_some_java_work();\n * fast_jni_call_to_release_a_lock();\n * </code>\n *\n * <p>\n * This code can lead to deadlocks. Say thread 1 just finishes\n * {@code fast_jni_call_to_grab_a_lock()} and is in {@code does_some_java_work()}.\n * GC kicks in and suspends thread 1. Thread 2 now is in {@code fast_jni_call_to_grab_a_lock()}\n * but is blocked on grabbing the native lock since it's held by thread 1.\n * Now thread suspension can't finish since thread 2 can't be suspended since it's doing\n * FastNative JNI.\n * </p>\n *\n * <p>\n * Normal JNI doesn't have the issue since once it's in native code,\n * it is considered suspended from java's point of view.\n * FastNative JNI however doesn't do the state transition done by JNI.\n * </p>\n *\n * <p>\n * Note that even in FastNative methods you <b>are</b> allowed to\n * allocate objects and make upcalls into Java code. A call from Java to\n * a FastNative function and back to Java is equivalent to a call from one Java\n * method to another. What's forbidden in a FastNative method is blocking\n * the calling thread in some non-Java code and thereby preventing the thread\n * from responding to requests from the garbage collector to enter the suspended\n * state.\n * </p>\n *\n * <p>\n * Has no effect when used with non-native methods.\n * </p>\n */\n@Retention(RetentionPolicy.CLASS)  // Save memory, don't instantiate as an object at runtime.\n@Target(ElementType.METHOD)\npublic @interface FastNative { }\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    namespace \"com.tencent.mmkvdemo\"\n    compileSdk rootProject.ext.compileSdk\n\n    signingConfigs {\n        config {\n            keyAlias 'key0'\n            keyPassword 'mmkv.wxg'\n            storeFile rootProject.file('debug.keystore')\n            storePassword 'mmkv.wxg'\n        }\n    }\n\n    defaultConfig {\n        applicationId \"com.tencent.mmkvdemo\"\n        minSdkVersion rootProject.ext.minSdkVersion\n        targetSdkVersion rootProject.ext.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        externalNativeBuild {\n            cmake {\n                arguments = [\"-DANDROID_STL=c++_shared\"]\n                // use prefab or not: Gradle has bug handling local prefab modules, fails on linking\n//                arguments += [\"-DHAS_PREFAB=1\"]\n                cppFlags \"-fvisibility=hidden\", \"-funwind-tables\", \"-fasynchronous-unwind-tables\", \"-O2\",\n                        \"-Wl,-z,max-page-size=16384\"\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n            signingConfig signingConfigs.config\n//            debuggable true\n//            jniDebuggable true\n        }\n        debug {\n            signingConfig signingConfigs.config\n            jniDebuggable true\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    flavorDimensions = [\"stl_mode\"]\n    productFlavors {\n        DefaultCpp.dimension = \"stl_mode\"\n        StaticCpp.dimension = \"stl_mode\"\n        SharedCpp.dimension = \"stl_mode\"\n    }\n    externalNativeBuild {\n        cmake {\n            path file('src/main/cpp/CMakeLists.txt')\n        }\n    }\n\n    buildFeatures {\n        aidl true\n\n//        prefab true\n    }\n\n    kotlinOptions {\n        jvmTarget = JavaVersion.VERSION_1_8.toString()\n    }\n    /*splits {\n        // Configures multiple APKs based on ABI.\n        abi {\n\n            // Enables building multiple APKs per ABI.\n            enable true\n\n            // By default all ABIs are included, so use reset() and include to specify that we only want specific APKs\n            // Resets the list of ABIs that Gradle should create APKs for to none.\n            reset()\n\n            // Specifies a list of ABIs that Gradle should create APKs for.\n            include \"armeabi-v7a\"\n\n            // Specifies that we do not want to also generate a universal APK that includes all ABIs.\n            universalApk false\n        }\n    }*/\n}\n\nrepositories {\n    mavenCentral()\n    mavenLocal()\n}\n\n// Define the mmkv version to use\ndef mmkvVersion = \"${VERSION_NAME_PREFIX}${VERSION_NAME_SUFFIX}\"\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation project(':mmkv')\n//    implementation \"com.tencent:mmkv:$mmkvVersion\"\n//    implementation \"com.tencent:mmkv-static:$mmkvVersion\" // this is identical to 'com.tencent:mmkv'\n//    implementation \"com.tencent:mmkv-shared:$mmkvVersion\" // with prefab functionality\n    implementation 'androidx.appcompat:appcompat:1.7.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.2.1'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.7.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"\n    implementation 'com.getkeepsafe.relinker:relinker:1.4.5'\n}"
  },
  {
    "path": "Android/MMKV/mmkvdemo/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n-keepclasseswithmembers,includedescriptorclasses class com.tencent.mmkvdemo.** {\n    native <methods>;\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\"\n        android:name=\".MyApplication\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\".MyService\"\n            android:enabled=\"true\"\n            android:exported=\"false\"\n            android:process=\":mmkvdemo_svr\">\n            <intent-filter>\n                <action android:name=\"com.tencent.mmkvdemo.MyService\"/>\n            </intent-filter>\n        </service>\n        <service\n            android:name=\".MyService_1\"\n            android:enabled=\"true\"\n            android:exported=\"false\"\n            android:process=\":mmkvdemo_svr_1\">\n            <intent-filter>\n                <action android:name=\"com.tencent.mmkvdemo.MyService_1\"/>\n            </intent-filter>\n        </service>\n\n        <provider android:name=\".MultiProcessSharedPreferences\"\n            android:authorities=\".MultiProcessSharedPreferences\"\n            android:process=\".MultiProcessSharedPreferences\"\n            android:exported=\"false\">\n        </provider>\n\n        <provider\n            android:authorities=\"com.tencent.mmkvdemo.MMKVContentProvider\"\n            android:name=\"com.tencent.mmkv.MMKVContentProvider\"\n            android:exported=\"false\">\n        </provider>\n    </application>\n\n</manifest>"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/aidl/com/tencent/mmkvdemo/IAshmemMMKV.aidl",
    "content": "// IAshmemMMKV.aidl\npackage com.tencent.mmkvdemo;\n\nimport com.tencent.mmkv.ParcelableMMKV;\n\ninterface IAshmemMMKV {\n    ParcelableMMKV GetAshmemMMKV();\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/cpp/CMakeLists.txt",
    "content": "\n# For more information about using CMake with Android Studio, read the\n# documentation: https://d.android.com/studio/projects/add-native-code.html.\n# For more examples on how to use CMake, see https://github.com/android/ndk-samples.\n\n# Sets the minimum CMake version required for this project.\ncmake_minimum_required(VERSION 3.10.0)\n\n# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},\n# Since this is the top level CMakeLists.txt, the project name is also accessible\n# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level\n# build script scope).\nproject(\"mmkvdemo\")\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n#\n# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define\n# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}\n# is preferred for the same purpose.\n#\n# In order to load a library into your app from Java/Kotlin, you must call\n# System.loadLibrary() and pass the name of the library defined here;\n# for GameActivity/NativeActivity derived applications, the same library name must be\n# used in the AndroidManifest.xml file.\nadd_library(${CMAKE_PROJECT_NAME} SHARED\n    # List C/C++ source files with relative paths to this CMakeLists.txt.\n    mmkvdemo.cpp)\n\n# Find the mmkv lib in prefab\nif (HAS_PREFAB)\n    find_package(mmkv REQUIRED CONFIG)\n    target_link_libraries(${CMAKE_PROJECT_NAME}\n            mmkv::mmkv\n    )\n    target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE\n            -DENABLE_MMKV_NATIVE=1\n    )\nendif ()\n\n# Specifies libraries CMake should link to your target library. You\n# can link libraries from various origins, such as libraries defined in this\n# build script, prebuilt third-party libraries, or Android system libraries.\ntarget_link_libraries(${CMAKE_PROJECT_NAME}\n    # List libraries link to the target library\n    android\n    log\n)\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/cpp/mmkvdemo.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// A demo for using MMKV C++ interface in native code\n\n#include <string>\n#include <jni.h>\n#include <android/log.h>\n#ifdef ENABLE_MMKV_NATIVE\n#include <MMKV/MMKV.h>\n#endif\nusing namespace std;\n\nconstexpr auto APP_NAME = \"MMKVNativeDemo\";\n\nvoid _MMKVLogWithLevel(android_LogPriority level, const char *filename, const char *func, int line, const char *format, ...) {\n    string message;\n    char buffer[16];\n\n    va_list args;\n    va_start(args, format);\n    auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n    va_end(args);\n\n    if (length < 0) { // something wrong\n        message = {};\n    } else if (length < sizeof(buffer)) {\n        message = string(buffer, static_cast<unsigned long>(length));\n    } else {\n        message.resize(static_cast<unsigned long>(length), '\\0');\n        va_start(args, format);\n        std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n        va_end(args);\n    }\n\n    __android_log_print(level, APP_NAME, \"<%s:%d::%s> %s\", filename, line, func, message.c_str());\n}\n\n#define MMKVError(format, ...) \\\n    _MMKVLogWithLevel(ANDROID_LOG_ERROR, __FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n#define MMKVWarning(format, ...) \\\n    _MMKVLogWithLevel(ANDROID_LOG_WARN, __FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n#define MMKVInfo(format, ...) \\\n    _MMKVLogWithLevel(ANDROID_LOG_INFO, __FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n#define MMKVLog MMKVInfo\n\nstatic string jstring2string(JNIEnv *env, jstring str) {\n    if (str) {\n        const char *kstr = env->GetStringUTFChars(str, nullptr);\n        if (kstr) {\n            string result(kstr);\n            env->ReleaseStringUTFChars(str, kstr);\n            return result;\n        }\n    }\n    return \"\";\n}\n\nstring to_string(const std::string& str) {  return str; }\n\ntemplate <class T>\nstring to_string(const vector<T> &arr, const char* sp = \", \") {\n    string str;\n    for (const auto &element : arr) {\n        str += to_string(element);\n        str += sp;\n    }\n    if (!str.empty()) {\n        str.erase(str.length() - strlen(sp));\n    }\n    return str;\n}\n\n#ifdef ENABLE_MMKV_NATIVE\n\nvoid functionalTest(MMKV *mmkv, bool decodeOnly) {\n    if (!decodeOnly) {\n        mmkv->set(true, \"bool\");\n    }\n    MMKVLog(\"bool = %d\\n\", mmkv->getBool(\"bool\"));\n\n    if (!decodeOnly) {\n        mmkv->set(1024, \"int32\");\n    }\n    MMKVLog(\"int32 = %d\\n\", mmkv->getInt32(\"int32\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint32_t>::max(), \"uint32\");\n    }\n    MMKVLog(\"uint32 = %u\\n\", mmkv->getUInt32(\"uint32\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<int64_t>::min(), \"int64\");\n    }\n    MMKVLog(\"int64 = %lld\\n\", mmkv->getInt64(\"int64\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint64_t>::max(), \"uint64\");\n    }\n    MMKVLog(\"uint64 = %llu\\n\", mmkv->getUInt64(\"uint64\"));\n\n    if (!decodeOnly) {\n        mmkv->set(3.14f, \"float\");\n    }\n    MMKVLog(\"float = %f\\n\", mmkv->getFloat(\"float\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<double>::max(), \"double\");\n    }\n    MMKVLog(\"double = %f\\n\", mmkv->getDouble(\"double\"));\n\n    if (!decodeOnly) {\n        mmkv->set(\"Hello, MMKV-示例 for POSIX\", \"raw_string\");\n        std::string str = \"Hello, MMKV-示例 for POSIX string\";\n        mmkv->set(str, \"string\");\n        mmkv->set(std::string_view(str).substr(7, 21), \"string_view\");\n    }\n    std::string result;\n    mmkv->getString(\"raw_string\", result);\n    MMKVLog(\"raw_string = %s\\n\", result.c_str());\n    mmkv->getString(\"string\", result);\n    MMKVLog(\"string = %s\\n\", result.c_str());\n    mmkv->getString(\"string_view\", result);\n    MMKVLog(\"string_view = %s\\n\", result.c_str());\n\n    // containerTest(mmkv, decodeOnly);\n\n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"count = %zu, totalSize = %zu\\n\", mmkv->count(), mmkv->totalSize());\n    MMKVLog(\"containsKey[string]: %d\\n\", mmkv->containsKey(\"string\"));\n\n    mmkv->removeValueForKey(\"bool\");\n    MMKVLog(\"bool: %d\\n\", mmkv->getBool(\"bool\"));\n    mmkv->removeValuesForKeys({\"int\", \"long\"});\n\n    mmkv->set(\"some string\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string before set null: %s\\n\", result.c_str());\n    mmkv->set((const char *) nullptr, \"null string\");\n    //mmkv->set(\"\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string after set null: %s, containsKey: %d\\n\", result.c_str(), mmkv->containsKey(\"null string\"));\n\n    //kv.sync();\n    //kv.async();\n    //kv.clearAll();\n    mmkv->clearMemoryCache();\n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"isFileValid[%s]: %d\\n\", mmkv->mmapID().c_str(), MMKV::isFileValid(mmkv->mmapID()));\n}\n#endif\nstatic void testNameSpaceInNative(JNIEnv *env, jobject obj, jstring rootDir, jstring mmapID) {\n    string root = jstring2string(env, rootDir);\n    string id = jstring2string(env, mmapID);\n#ifdef ENABLE_MMKV_NATIVE\n    auto ns = MMKV::nameSpace(root);\n    auto kv = ns.mmkvWithID(id);\n    functionalTest(kv, false);\n#else\n    MMKVWarning(\"ENABLE_MMKV_NATIVE not defined.\");\n#endif\n}\n\n#ifndef ENABLE_MMKV_NATIVE\nenum MMKVLogLevel : int {\n    MMKVLogDebug = 0, // not available for release/product build\n    MMKVLogInfo = 1,  // default level\n    MMKVLogWarning,\n    MMKVLogError,\n    MMKVLogNone, // special level used to disable all log messages\n};\n#endif\n\nstatic android_LogPriority MMKVLogLevelDesc(MMKVLogLevel level) {\n    switch (level) {\n    case MMKVLogDebug:\n        return ANDROID_LOG_DEBUG;\n    case MMKVLogInfo:\n        return ANDROID_LOG_INFO;\n    case MMKVLogWarning:\n        return ANDROID_LOG_WARN;\n    case MMKVLogError:\n        return ANDROID_LOG_ERROR;\n    default:\n        return ANDROID_LOG_UNKNOWN;\n    }\n}\n\nstatic void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const char *message) {\n    auto desc = MMKVLogLevelDesc(level);\n    __android_log_print(desc, APP_NAME, \"<%s:%d::%s> %s\", file, line, function, message);\n}\n\nstatic jlong getNativeLogHandler(JNIEnv *env, jobject obj) {\n    return (jlong) mmkvLog;\n}\n\nstatic JNINativeMethod g_methods[] = {\n    {\"testNameSpaceInNative\", \"(Ljava/lang/String;Ljava/lang/String;)V\", (void *) testNameSpaceInNative},\n    {\"getNativeLogHandler\", \"()J\", (void *) getNativeLogHandler},\n};\n\nstatic int registerNativeMethods(JNIEnv *env, jclass cls) {\n    return env->RegisterNatives(cls, g_methods, sizeof(g_methods) / sizeof(g_methods[0]));\n}\n\nextern \"C\" JNIEXPORT JNICALL jint JNI_OnLoad(JavaVM *vm, void *reserved) {\n    JNIEnv *env;\n    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {\n        return -1;\n    }\n\n    static const char *clsName = \"com/tencent/mmkvdemo/MyApplication\";\n    jclass instance = env->FindClass(clsName);\n    if (!instance) {\n        MMKVError(\"fail to locate class: %s\", clsName);\n        return -2;\n    }\n\n    int ret = registerNativeMethods(env, instance);\n    if (ret != 0) {\n        MMKVError(\"fail to register native methods for class %s, ret = %d\", clsName, ret);\n        return -4;\n    }\n    return JNI_VERSION_1_6;\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/Baseline.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport static android.content.Context.MODE_PRIVATE;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.util.Log;\nimport com.tencent.mmkv.MMKV;\nimport java.math.RoundingMode;\nimport java.text.DecimalFormat;\nimport java.text.DecimalFormatSymbols;\nimport java.util.Locale;\nimport java.util.Random;\n\npublic final class Baseline {\n    private String[] m_arrStrings;\n    private String[] m_arrKeys;\n    private String[] m_arrIntKeys;\n    private int m_loops = 1000;\n    private Context m_context;\n    private static final String MMKV_ID = \"baseline3\";\n    private static final String CryptKey = null;\n    //private static final String CryptKey = \"baseline_key3\";\n    private static final String TAG = \"MMKV\";\n    private DecimalFormat m_formatter;\n\n    Baseline(Context context, int loops) {\n        m_context = context;\n        m_loops = loops;\n\n        m_arrStrings = new String[loops];\n        m_arrKeys = new String[loops];\n        m_arrIntKeys = new String[loops];\n        Random r = new Random();\n        final String filename = \"mmkv/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/Baseline.java_\";\n        for (int index = 0; index < loops; index++) {\n            //String str = \"[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication_\";\n            //m_arrStrings[index] = str + r.nextInt();\n            m_arrStrings[index] = filename + r.nextInt();\n            m_arrKeys[index] = \"str_\" + index;\n            m_arrIntKeys[index] = \"int_\" + index;\n        }\n        m_formatter = new DecimalFormat(\"0.0\", DecimalFormatSymbols.getInstance(Locale.ENGLISH));\n        m_formatter.setRoundingMode(RoundingMode.DOWN);\n    }\n\n    public void mmkvBaselineTest() {\n        mmkvBatchWriteInt();\n        mmkvBatchReadInt();\n        mmkvBatchWriteString();\n        mmkvBatchReadString();\n\n        //mmkvBatchDeleteString();\n        MMKV mmkv = mmkvForTest();\n        //mmkv.trim();\n        mmkv.clearMemoryCache();\n        mmkv.totalSize();\n    }\n\n    private MMKV mmkvForTest() {\n        return MMKV.mmkvWithID(MMKV_ID, MMKV.SINGLE_PROCESS_MODE, CryptKey);\n        //return MMKV.mmkvWithAshmemID(m_context, MMKV_ID, 65536, MMKV.SINGLE_PROCESS_MODE, CryptKey);\n    }\n\n    private void mmkvBatchWriteInt() {\n        Random r = new Random();\n        long startTime = System.nanoTime();\n\n        MMKV mmkv = mmkvForTest();\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n            mmkv.encode(key, tmp);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"MMKV write int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void mmkvBatchReadInt() {\n        long startTime = System.nanoTime();\n\n        MMKV mmkv = mmkvForTest();\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = mmkv.decodeInt(key);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"MMKV read int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void mmkvBatchWriteString() {\n        long startTime = System.nanoTime();\n\n        MMKV mmkv = mmkvForTest();\n        for (int index = 0; index < m_loops; index++) {\n            final String valueStr = m_arrStrings[index];\n            final String strKey = m_arrKeys[index];\n            mmkv.encode(strKey, valueStr);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"MMKV write String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void mmkvBatchReadString() {\n        long startTime = System.nanoTime();\n\n        MMKV mmkv = mmkvForTest();\n        for (int index = 0; index < m_loops; index++) {\n            String strKey = m_arrKeys[index];\n            String tmpStr = mmkv.decodeString(strKey);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"MMKV read String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void mmkvBatchDeleteString() {\n        long startTime = System.nanoTime();\n\n        MMKV mmkv = mmkvForTest();\n        for (int index = 0; index < m_loops; index++) {\n            String strKey = m_arrKeys[index];\n            mmkv.removeValueForKey(strKey);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"MMKV delete String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    public void sharedPreferencesBaselineTest() {\n        spBatchWriteInt();\n        spBatchReadInt();\n        spBatchWriteString();\n        spBatchReadString();\n    }\n\n    private void spBatchWriteInt() {\n        Random r = new Random();\n        long startTime = System.nanoTime();\n\n        SharedPreferences preferences = m_context.getSharedPreferences(MMKV_ID, MODE_PRIVATE);\n        SharedPreferences.Editor editor = preferences.edit();\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n            editor.putInt(key, tmp);\n            // editor.commit();\n            editor.apply();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"SharedPreferences write int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void spBatchReadInt() {\n        long startTime = System.nanoTime();\n\n        SharedPreferences preferences = m_context.getSharedPreferences(MMKV_ID, MODE_PRIVATE);\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = preferences.getInt(key, 0);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"SharedPreferences read int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void spBatchWriteString() {\n        long startTime = System.nanoTime();\n\n        SharedPreferences preferences = m_context.getSharedPreferences(MMKV_ID, MODE_PRIVATE);\n        SharedPreferences.Editor editor = preferences.edit();\n        for (int index = 0; index < m_loops; index++) {\n            final String str = m_arrStrings[index];\n            final String key = m_arrKeys[index];\n            editor.putString(key, str);\n            // editor.commit();\n            editor.apply();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"SharedPreferences write String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void spBatchReadString() {\n        long startTime = System.nanoTime();\n\n        SharedPreferences preferences = m_context.getSharedPreferences(MMKV_ID, MODE_PRIVATE);\n        for (int index = 0; index < m_loops; index++) {\n            final String key = m_arrKeys[index];\n            final String tmp = preferences.getString(key, null);\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        Log.i(TAG, \"SharedPreferences read String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    public void sqliteBaselineTest(boolean useTransaction) {\n        sqliteWriteInt(useTransaction);\n        sqliteReadInt(useTransaction);\n        sqliteWriteString(useTransaction);\n        sqliteReadString(useTransaction);\n    }\n\n    private void sqliteWriteInt(boolean useTransaction) {\n        Random r = new Random();\n        long startTime = System.nanoTime();\n\n        SQLiteKV sqliteKV = new SQLiteKV(m_context);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n            sqliteKV.putInt(key, tmp);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        final String msg = useTransaction ? \"sqlite transaction\" : \"sqlite\";\n        Log.i(TAG, msg + \" write int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void sqliteReadInt(boolean useTransaction) {\n        long startTime = System.nanoTime();\n\n        SQLiteKV sqliteKV = new SQLiteKV(m_context);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = sqliteKV.getInt(key);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        final String msg = useTransaction ? \"sqlite transaction\" : \"sqlite\";\n        Log.i(TAG, msg + \" read int: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void sqliteWriteString(boolean useTransaction) {\n        long startTime = System.nanoTime();\n\n        SQLiteKV sqliteKV = new SQLiteKV(m_context);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            final String value = m_arrStrings[index];\n            final String key = m_arrKeys[index];\n            sqliteKV.putString(key, value);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        final String msg = useTransaction ? \"sqlite transaction\" : \"sqlite\";\n        Log.i(TAG, msg + \" write String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n\n    private void sqliteReadString(boolean useTransaction) {\n        long startTime = System.nanoTime();\n\n        SQLiteKV sqliteKV = new SQLiteKV(m_context);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            final String key = m_arrKeys[index];\n            final String tmp = sqliteKV.getString(key);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        double endTime = (System.nanoTime() - startTime) / 1000000.0;\n        final String msg = useTransaction ? \"sqlite transaction\" : \"sqlite\";\n        Log.i(TAG, msg + \" read String: loop[\" + m_loops + \"]: \" + m_formatter.format(endTime) + \" ms\");\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.IBinder;\nimport android.util.Log;\nimport androidx.annotation.Nullable;\nimport com.tencent.mmkv.MMKV;\nimport com.tencent.mmkv.MMKVConfig;\nimport com.tencent.mmkv.ParcelableMMKV;\nimport java.util.Random;\n\npublic abstract class BenchMarkBaseService extends Service {\n    public static final String CMD_ID = \"cmd_id\";\n    public static final String CMD_READ_INT = \"cmd_read_int\";\n    public static final String CMD_WRITE_INT = \"cmd_write_int\";\n    public static final String CMD_READ_STRING = \"cmd_read_string\";\n    public static final String CMD_WRITE_STRING = \"cmd_write_string\";\n    public static final String CMD_PREPARE_ASHMEM_BY_CP = \"cmd_prepare_ashmem_by_ContentProvider\";\n    public static final String CMD_PREPARE_ASHMEM_KEY = \"cmd_prepare_ashmem_key\";\n\n    // 1M, ashmem cannot change size after opened\n    public static final int AshmemMMKV_Size = 1024 * 1024;\n    public static final String AshmemMMKV_ID = \"testAshmemMMKVByCP\";\n\n    private String[] m_arrStrings;\n    private String[] m_arrKeys;\n    private String[] m_arrIntKeys;\n\n    private static final int m_loops = 1000;\n    public static final String MMKV_ID = \"benchmark_interprocess\";\n    //public static final String MMKV_ID = \"benchmark_interprocess_crypt1\";\n    private static final String SP_ID = \"benchmark_interprocess_sp\";\n    public static final String CryptKey = null;\n    //public static final String CryptKey = \"Tencent MMKV\";\n    private static final boolean SQLite_Use_Transaction = false;\n    private static final String TAG = \"MMKV\";\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.i(TAG, \"onCreate BenchMarkBaseService\");\n\n        MMKV.initialize(this);\n        MMKV.unregisterHandler();\n        MMKV.unregisterContentChangeNotify();\n        {\n            long startTime = System.currentTimeMillis();\n\n            MMKV mmkv = MMKV.mmkvWithID(MMKV_ID, MMKV.MULTI_PROCESS_MODE, CryptKey);\n\n            long endTime = System.currentTimeMillis();\n            Log.i(TAG, \"load [\" + MMKV_ID + \"]: \" + (endTime - startTime) + \" ms\");\n        }\n        m_arrStrings = new String[m_loops];\n        m_arrKeys = new String[m_loops];\n        m_arrIntKeys = new String[m_loops];\n        Random r = new Random();\n        final String filename =\n            \"mmkv/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java_\";\n        for (int index = 0; index < m_loops; index++) {\n            //String str = \"[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication_\";\n            //m_arrStrings[index] = str + r.nextInt();\n            m_arrStrings[index] = filename + r.nextInt();\n            m_arrKeys[index] = \"testStr_\" + index;\n            m_arrIntKeys[index] = \"int_\" + index;\n        }\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        Log.i(TAG, \"onDestroy BenchMarkBaseService\");\n\n        MMKV.onExit();\n    }\n\n    protected void batchWriteInt(String caller) {\n        mmkvBatchWriteInt(caller);\n        sqliteWriteInt(caller, SQLite_Use_Transaction);\n        spBatchWriteInt(caller);\n    }\n\n    private void mmkvBatchWriteInt(String caller) {\n        Random r = new Random();\n        Log.i(TAG, caller + \" mmkv write int: loop[\" + m_loops + \"] star.\");\n        long startTime = System.currentTimeMillis();\n\n        MMKV mmkv = GetMMKV();\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n//            MMKV mmkv =  MMKV.mmkvWithID(MMKV_ID + key, MMKV.MULTI_PROCESS_MODE, CryptKey);\n            mmkv.encode(key, tmp);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" mmkv write int: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void sqliteWriteInt(String caller, boolean useTransaction) {\n        Random r = new Random();\n        long startTime = System.currentTimeMillis();\n\n        SQLiteKV sqliteKV = new SQLiteKV(this);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n            sqliteKV.putInt(key, tmp);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        long endTime = System.currentTimeMillis();\n        final String msg = useTransaction ? \" sqlite transaction \" : \" sqlite \";\n        Log.i(TAG, caller + msg + \"write int: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void spBatchWriteInt(String caller) {\n        Random r = new Random();\n        long startTime = System.currentTimeMillis();\n\n        SharedPreferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);\n        SharedPreferences.Editor editor = preferences.edit();\n        for (int index = 0; index < m_loops; index++) {\n            int tmp = r.nextInt();\n            String key = m_arrIntKeys[index];\n            editor.putInt(key, tmp);\n            //editor.commit();\n            editor.apply();\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" MultiProcessSharedPreferences write int: loop[\" + m_loops + \"]: \" + (endTime - startTime)\n                       + \" ms\");\n    }\n\n    protected void batchReadInt(String caller) {\n        mmkvBatchReadInt(caller);\n        sqliteReadInt(caller, SQLite_Use_Transaction);\n        spBatchReadInt(caller);\n    }\n\n    private void mmkvBatchReadInt(String caller) {\n        Log.i(TAG, caller + \" mmkv read int: loop[\" + m_loops + \"] star.\");\n        long startTime = System.currentTimeMillis();\n\n        MMKV mmkv = GetMMKV();\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = mmkv.decodeInt(key);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" mmkv read int: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void sqliteReadInt(String caller, boolean useTransaction) {\n        long startTime = System.currentTimeMillis();\n\n        SQLiteKV sqliteKV = new SQLiteKV(this);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = sqliteKV.getInt(key);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        long endTime = System.currentTimeMillis();\n        final String msg = useTransaction ? \" sqlite transaction \" : \" sqlite \";\n        Log.i(TAG, caller + msg + \"read int: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void spBatchReadInt(String caller) {\n        long startTime = System.currentTimeMillis();\n\n        SharedPreferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);\n        for (int index = 0; index < m_loops; index++) {\n            String key = m_arrIntKeys[index];\n            int tmp = preferences.getInt(key, 0);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" MultiProcessSharedPreferences read int: loop[\" + m_loops + \"]: \" + (endTime - startTime)\n                       + \" ms\");\n    }\n\n    protected void batchWriteString(String caller) {\n        mmkvBatchWriteString(caller);\n        sqliteWriteString(caller, SQLite_Use_Transaction);\n        spBatchWrieString(caller);\n    }\n\n    private void mmkvBatchWriteString(String caller) {\n        Log.i(TAG, caller + \" mmkv write String: loop[\" + m_loops + \"] star.\");\n        long startTime = System.currentTimeMillis();\n\n        MMKV mmkv = GetMMKV();\n        for (int index = 0; index < m_loops; index++) {\n            final String valueStr = m_arrStrings[index];\n            final String strKey = m_arrKeys[index];\n            mmkv.encode(strKey, valueStr);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" mmkv write String: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void sqliteWriteString(String caller, boolean useTransaction) {\n        long startTime = System.currentTimeMillis();\n\n        SQLiteKV sqliteKV = new SQLiteKV(this);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            final String value = m_arrStrings[index];\n            final String key = m_arrKeys[index];\n            sqliteKV.putString(key, value);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        long endTime = System.currentTimeMillis();\n        final String msg = useTransaction ? \" sqlite transaction \" : \" sqlite \";\n        Log.i(TAG, caller + msg + \"write String: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void spBatchWrieString(String caller) {\n        long startTime = System.currentTimeMillis();\n\n        SharedPreferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);\n        SharedPreferences.Editor editor = preferences.edit();\n        for (int index = 0; index < m_loops; index++) {\n            final String str = m_arrStrings[index];\n            final String key = m_arrKeys[index];\n            editor.putString(key, str);\n            //editor.commit();\n            editor.apply();\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" MultiProcessSharedPreferences write String: loop[\" + m_loops\n                       + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    protected void batchReadString(String caller) {\n        mmkvBatchReadString(caller);\n        sqliteReadString(caller, SQLite_Use_Transaction);\n        spBatchReadStrinfg(caller);\n    }\n\n    private void mmkvBatchReadString(String caller) {\n        Log.i(TAG, caller + \" mmkv read String: loop[\" + m_loops + \"] star.\");\n        long startTime = System.currentTimeMillis();\n\n        MMKV mmkv = GetMMKV();\n        for (int index = 0; index < m_loops; index++) {\n            final String strKey = m_arrKeys[index];\n            final String tmpStr = mmkv.decodeString(strKey);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" mmkv read String: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void sqliteReadString(String caller, boolean useTransaction) {\n        long startTime = System.currentTimeMillis();\n\n        SQLiteKV sqliteKV = new SQLiteKV(this);\n        if (useTransaction) {\n            sqliteKV.beginTransaction();\n        }\n        for (int index = 0; index < m_loops; index++) {\n            final String key = m_arrKeys[index];\n            final String tmp = sqliteKV.getString(key);\n        }\n        if (useTransaction) {\n            sqliteKV.endTransaction();\n        }\n        long endTime = System.currentTimeMillis();\n        final String msg = useTransaction ? \" sqlite transaction \" : \" sqlite \";\n        Log.i(TAG, caller + msg + \"read String: loop[\" + m_loops + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    private void spBatchReadStrinfg(String caller) {\n        long startTime = System.currentTimeMillis();\n\n        SharedPreferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);\n        for (int index = 0; index < m_loops; index++) {\n            final String key = m_arrKeys[index];\n            final String tmp = preferences.getString(key, null);\n        }\n        long endTime = System.currentTimeMillis();\n        Log.i(TAG, caller + \" MultiProcessSharedPreferences read String: loop[\" + m_loops\n                       + \"]: \" + (endTime - startTime) + \" ms\");\n    }\n\n    MMKV m_ashmemMMKV;\n\n    protected MMKV GetMMKV() {\n        if (m_ashmemMMKV != null) {\n            return m_ashmemMMKV;\n        } else {\n            return MMKV.mmkvWithID(MMKV_ID, MMKV.MULTI_PROCESS_MODE, CryptKey);\n        }\n    }\n\n    public class AshmemMMKVGetter extends IAshmemMMKV.Stub {\n\n        private AshmemMMKVGetter() {\n            // 1M, ashmem cannot change size after opened\n            final String id = \"tetAshmemMMKV\";\n            try {\n                MMKVConfig config = new MMKVConfig();\n                config.expectedCapacity = AshmemMMKV_Size;\n                config.mode = MMKV.MULTI_PROCESS_MODE;\n                config.cryptKey = CryptKey;\n                m_ashmemMMKV = MMKV.mmkvWithAshmemID(BenchMarkBaseService.this, id, config);\n                m_ashmemMMKV.encode(\"bool\", true);\n            } catch (Exception e) {\n                Log.e(\"MMKV\", e.getMessage());\n            }\n        }\n\n        public ParcelableMMKV GetAshmemMMKV() {\n            return new ParcelableMMKV(m_ashmemMMKV);\n        }\n    }\n\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        Log.i(TAG, \"onBind, intent=\" + intent);\n        return new AshmemMMKVGetter();\n    }\n\n    protected void prepareAshmemMMKVByCP() {\n        // it's ok for other process not knowing cryptKey\n        final String cryptKey = null;\n        try {\n            m_ashmemMMKV = MMKV.mmkvWithAshmemID(this, AshmemMMKV_ID, AshmemMMKV_Size, MMKV.MULTI_PROCESS_MODE, cryptKey);\n        } catch (Exception e) {\n            Log.e(\"MMKV\", e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/FakeInfo.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport androidx.annotation.NonNull;\n\npublic class FakeInfo implements Parcelable {\n    public String channelId;\n\n    public int position;\n    public FakeInfo() {\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"fake channelID = \" + channelId + \", position = \" + position;\n    }\n\n    protected FakeInfo(Parcel in) {\n        channelId = in.readString();\n        position = in.readInt();\n    }\n\n    public static final Creator<FakeInfo> CREATOR = new Creator<FakeInfo>() {\n        @Override\n        public FakeInfo createFromParcel(Parcel in) {\n            return new FakeInfo(in);\n        }\n\n        @Override\n        public FakeInfo[] newArray(int size) {\n            return new FakeInfo[size];\n        }\n    };\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(channelId);\n        dest.writeInt(position);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/Info.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport androidx.annotation.NonNull;\n\nclass Info implements Parcelable {\n    public String channelId;\n\n    public int position;\n    public Info(String id, int pos) {\n        channelId = id;\n        position = pos;\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"channelID = \" + channelId + \", position = \" + position;\n    }\n\n    protected Info(Parcel in) {\n        channelId = in.readString();\n        position = in.readInt();\n    }\n\n    public static final Creator<Info> CREATOR = new Creator<Info>() {\n        @Override\n        public Info createFromParcel(Parcel in) {\n            return new Info(in);\n        }\n\n        @Override\n        public Info[] newArray(int size) {\n            return new Info[size];\n        }\n    };\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(channelId);\n        dest.writeInt(position);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MainActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport static com.tencent.mmkvdemo.BenchMarkBaseService.AshmemMMKV_ID;\nimport static com.tencent.mmkvdemo.BenchMarkBaseService.AshmemMMKV_Size;\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.SystemClock;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.TextView;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport com.tencent.mmkv.MMKV;\nimport com.tencent.mmkv.MMKVConfig;\nimport com.tencent.mmkv.MMKVRecoverStrategic;\nimport com.tencent.mmkv.NameSpace;\nimport com.tencent.mmkv.NativeBuffer;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Objects;\n\npublic class MainActivity extends AppCompatActivity {\n    static private final String KEY_1 = \"Ashmem_Key_1\";\n    static private final String KEY_2 = \"Ashmem_Key_2\";\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        // Enable edge-to-edge display\n        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);\n        setContentView(R.layout.activity_main);\n\n        View mainLayout = findViewById(R.id.main_layout);\n        ViewCompat.setOnApplyWindowInsetsListener(mainLayout, (v, insets) -> {\n            int statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top;\n            int navigationBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;\n            v.setPadding(0, statusBarHeight, 0, navigationBarHeight);\n            return insets;\n        });\n\n        TextView tv = (TextView) findViewById(R.id.sample_text);\n        String rootDir = MMKV.getRootDir();\n        tv.setText(rootDir);\n\n        final Button button = findViewById(R.id.button);\n        button.setOnClickListener(new View.OnClickListener() {\n            final Baseline baseline = new Baseline(getApplicationContext(), 1000);\n\n            public void onClick(View v) {\n                baseline.mmkvBaselineTest();\n                baseline.sharedPreferencesBaselineTest();\n                baseline.sqliteBaselineTest(false);\n\n                //testInterProcessReKey();\n                //testInterProcessLockPhase2();\n            }\n        });\n\n        //testHolderForMultiThread();\n\n        //prepareInterProcessAshmem();\n        //prepareInterProcessAshmemByContentProvider(KEY_1);\n\n        final Button button_read_int = findViewById(R.id.button_read_int);\n        button_read_int.setOnClickListener(new View.OnClickListener() {\n            public void onClick(View v) {\n                interProcessBaselineTest(BenchMarkBaseService.CMD_READ_INT);\n            }\n        });\n\n        final Button button_write_int = findViewById(R.id.button_write_int);\n        button_write_int.setOnClickListener(new View.OnClickListener() {\n            public void onClick(View v) {\n                interProcessBaselineTest(BenchMarkBaseService.CMD_WRITE_INT);\n            }\n        });\n\n        final Button button_read_string = findViewById(R.id.button_read_string);\n        button_read_string.setOnClickListener(new View.OnClickListener() {\n            public void onClick(View v) {\n                interProcessBaselineTest(BenchMarkBaseService.CMD_READ_STRING);\n            }\n        });\n\n        final Button button_write_string = findViewById(R.id.button_write_string);\n        button_write_string.setOnClickListener(new View.OnClickListener() {\n            public void onClick(View v) {\n                interProcessBaselineTest(BenchMarkBaseService.CMD_WRITE_STRING);\n            }\n        });\n\n        String otherDir = getFilesDir().getAbsolutePath() + \"/mmkv_3\";\n        MMKV kv = testMMKV(\"test/AES\", \"Tencent MMKV\", false, false, otherDir);\n        kv.checkContentChangedByOuterProcess();\n        kv.close();\n\n        // prepare for backup customize root path\n        kv = testMMKV(\"test_backup\", \"MMKV Backup\", false, false, otherDir);\n        kv.close();\n\n        testAshmem();\n        testReKey();\n\n        KotlinUsecaseKt.kotlinFunctionalTest();\n\n        testInterProcessLogic();\n        testImportSharedPreferences();\n\n        //testInterProcessLockPhase1();\n        //testCornerSize();\n        //testFastRemoveCornerSize();\n        //testTrimNonEmptyInterProcess();\n        //testItemSizeHolderOverride();\n\n//        testDiskFull();\n\n        testBackup();\n        testRestore();\n\n        testAutoExpire();\n        testExpectedCapacity();\n        testCompareBeforeSet();\n        testClearAllKeepSpace();\n//        testFastNativeSpeed();\n        testRemoveStorageAndCheckExist();\n        overrideTest();\n        testReadOnly();\n        testImport();\n    }\n\n    private void testCompareBeforeSet() {\n        MMKV mmkv = MMKV.mmkvWithID(\"testCompareBeforeSet\");\n        mmkv.enableCompareBeforeSet();\n\n        mmkv.encode(\"key\", \"extra\");\n\n        {\n            String key = \"int\";\n            int v = 12345;\n            mmkv.encode(key, v);\n            long actualSize = mmkv.actualSize();\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + actualSize);\n            Log.d(\"mmkv\", \"testCompareBeforeSet v = \" + mmkv.getInt(key, -1));\n            mmkv.encode(key, v);\n            long actualSize2 = mmkv.actualSize();\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + actualSize2);\n            if (actualSize2 != actualSize) {\n                Log.e(\"mmkv\", \"testCompareBeforeSet fail\");\n            }\n\n            mmkv.encode(key, v * 23);\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + mmkv.actualSize());\n            Log.d(\"mmkv\", \"testCompareBeforeSet v = \" + mmkv.getInt(key, -1));\n        }\n\n        {\n            String key = \"string\";\n            String v = \"w012A🏊🏻good\";\n            mmkv.encode(key, v);\n            long actualSize = mmkv.actualSize();\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + actualSize);\n            Log.d(\"mmkv\", \"testCompareBeforeSet v = \" + mmkv.getString(key, \"\"));\n            mmkv.encode(key, v);\n            long actualSize2 = mmkv.actualSize();\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + actualSize2);\n            if (actualSize2 != actualSize) {\n                Log.e(\"mmkv\", \"testCompareBeforeSet fail\");\n            }\n\n            mmkv.encode(key, \"temp data 👩🏻‍🏫\");\n            Log.d(\"mmkv\", \"testCompareBeforeSet actualSize = \" + mmkv.actualSize());\n            Log.d(\"mmkv\", \"testCompareBeforeSet v = \" + mmkv.getString(key, \"\"));\n        }\n    }\n\n    /**\n     * <a href=\"https://developer.android.com/reference/dalvik/annotation/optimization/FastNative\">FastNative</a>\n     * Before Test, remove `MMKVInfo` log print in `enableCompareBeforeSet` function\n     */\n    private void testFastNativeSpeed() {\n        int repeatCount = 5000000;\n        MMKV mmkv = MMKV.mmkvWithID(\"test_fastnative_speed\");\n        long start, end;\n        start = System.currentTimeMillis();\n        for (int i = 0; i < repeatCount; i++) {\n            mmkv.enableCompareBeforeSet();\n        }\n        end = System.currentTimeMillis();\n\n        Log.e(\"MMKV\", \"testFastNativeSpeed： \" + (end - start));\n    }\n\n    private void testInterProcessLogic() {\n        MMKV mmkv = MMKV.mmkvWithID(MyService.MMKV_ID, MMKV.MULTI_PROCESS_MODE, MyService.CryptKey);\n        mmkv.putInt(MyService.CMD_ID, 1024);\n        Log.d(\"mmkv in main\", \"\" + mmkv.decodeInt(MyService.CMD_ID));\n\n        Intent intent = new Intent(this, MyService.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, MyService.CMD_REMOVE);\n        startService(intent);\n\n        SystemClock.sleep(1000 * 3);\n        int value = mmkv.decodeInt(MyService.CMD_ID);\n        Log.d(\"mmkv\", \"\" + value);\n    }\n\n    private MMKV testMMKV(String mmapID, String cryptKey, boolean aes256, boolean decodeOnly, String rootPath) {\n        //MMKV kv = MMKV.defaultMMKV();\n        MMKV kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, aes256, rootPath);\n        testMMKV(kv, decodeOnly);\n        Log.i(\"MMKV\", \"isFileValid[\" + kv.mmapID() + \"]: \" + MMKV.isFileValid(kv.mmapID(), rootPath));\n        return kv;\n    }\n\n    static void testMMKV(MMKV kv, boolean decodeOnly) {\n        if (!decodeOnly) {\n            kv.encode(\"bool\", true);\n        }\n        Log.i(\"MMKV\", \"bool: \" + kv.decodeBool(\"bool\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"int\", Integer.MIN_VALUE);\n        }\n        Log.i(\"MMKV\", \"int: \" + kv.decodeInt(\"int\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"long\", Long.MAX_VALUE);\n        }\n        Log.i(\"MMKV\", \"long: \" + kv.decodeLong(\"long\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"float\", -3.14f);\n        }\n        Log.i(\"MMKV\", \"float: \" + kv.decodeFloat(\"float\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"double\", Double.MIN_VALUE);\n        }\n        Log.i(\"MMKV\", \"double: \" + kv.decodeDouble(\"double\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"string\", \"Hello from mmkv\");\n        }\n        Log.i(\"MMKV\", \"string: \" + kv.decodeString(\"string\"));\n\n        if (!decodeOnly) {\n            byte[] bytes = {'m', 'm', 'k', 'v'};\n            kv.encode(\"bytes\", bytes);\n        }\n        byte[] bytes = kv.decodeBytes(\"bytes\");\n        Log.i(\"MMKV\", \"bytes: \" + new String(bytes));\n        Log.i(\"MMKV\", \"bytes length = \" + bytes.length + \", value size consumption = \" + kv.getValueSize(\"bytes\")\n                          + \", value size = \" + kv.getValueActualSize(\"bytes\"));\n\n        int sizeNeeded = kv.getValueActualSize(\"bytes\");\n        NativeBuffer nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);\n        if (nativeBuffer != null) {\n            int size = kv.writeValueToNativeBuffer(\"bytes\", nativeBuffer);\n            Log.i(\"MMKV\", \"size Needed = \" + sizeNeeded + \" written size = \" + size);\n            MMKV.destroyNativeBuffer(nativeBuffer);\n        }\n\n        if (!decodeOnly) {\n            TestParcelable testParcelable = new TestParcelable(1024, \"Hi Parcelable\");\n            kv.encode(\"parcel\", testParcelable);\n        }\n        TestParcelable result = kv.decodeParcelable(\"parcel\", TestParcelable.class);\n        if (result != null) {\n            Log.d(\"MMKV\", \"parcel: \" + result.iValue + \", \" + result.sValue + \", \" + result.list);\n        } else {\n            Log.e(\"MMKV\", \"fail to decodeParcelable of key:parcel\");\n        }\n\n        kv.encode(\"null string\", \"some string\");\n        Log.i(\"MMKV\", \"string before set null: \" + kv.decodeString(\"null string\"));\n        kv.encode(\"null string\", (String) null);\n        Log.i(\"MMKV\", \"string after set null: \" + kv.decodeString(\"null string\")\n                          + \", containsKey:\" + kv.contains(\"null string\"));\n\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allKeys()));\n        Log.i(\"MMKV\",\n                \"count = \" + kv.count() + \", totalSize = \" + kv.totalSize() + \", actualSize = \" + kv.actualSize());\n        Log.i(\"MMKV\", \"containsKey[string]: \" + kv.containsKey(\"string\"));\n\n        kv.removeValueForKey(\"bool\");\n        Log.i(\"MMKV\", \"bool: \" + kv.decodeBool(\"bool\"));\n        kv.removeValuesForKeys(new String[] {\"int\", \"long\"});\n\n        //kv.sync();\n        //kv.async();\n        //kv.clearAll();\n        kv.clearMemoryCache();\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allKeys()));\n    }\n\n    private void testImportSharedPreferences() {\n        SharedPreferences preferences = getSharedPreferences(\"imported\", MODE_PRIVATE);\n        SharedPreferences.Editor editor = preferences.edit();\n        editor.putBoolean(\"bool\", true);\n        editor.putInt(\"int\", Integer.MIN_VALUE);\n        editor.putLong(\"long\", Long.MAX_VALUE);\n        editor.putFloat(\"float\", -3.14f);\n        editor.putString(\"string\", \"hello, imported\");\n        HashSet<String> set = new HashSet<String>();\n        set.add(\"W\");\n        set.add(\"e\");\n        set.add(\"C\");\n        set.add(\"h\");\n        set.add(\"a\");\n        set.add(\"t\");\n        editor.putStringSet(\"string-set\", set);\n        editor.commit();\n\n        MMKV kv = MMKV.mmkvWithID(\"imported\");\n        kv.clearAll();\n        kv.importFromSharedPreferences(preferences);\n        editor.clear().commit();\n\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allKeys()));\n\n        Log.i(\"MMKV\", \"bool: \" + kv.getBoolean(\"bool\", false));\n        Log.i(\"MMKV\", \"int: \" + kv.getInt(\"int\", 0));\n        Log.i(\"MMKV\", \"long: \" + kv.getLong(\"long\", 0));\n        Log.i(\"MMKV\", \"float: \" + kv.getFloat(\"float\", 0));\n        Log.i(\"MMKV\", \"double: \" + kv.decodeDouble(\"double\"));\n        Log.i(\"MMKV\", \"string: \" + kv.getString(\"string\", null));\n        Log.i(\"MMKV\", \"string-set: \" + kv.getStringSet(\"string-set\", null));\n        Log.i(\"MMKV\", \"linked-string-set: \" + kv.decodeStringSet(\"string-set\", null, LinkedHashSet.class));\n\n        // test @Nullable\n        kv.putStringSet(\"string-set\", null);\n        Log.i(\"MMKV\", \"after set null, string-set: \" + kv.getStringSet(\"string-set\", null));\n    }\n\n    private void testReKey() {\n        final String mmapID = \"test/AES_reKey1\";\n        MMKV kv = testMMKV(mmapID, null, false, false, null);\n\n        kv.reKey(\"Key_seq_1\");\n        kv.clearMemoryCache();\n        testMMKV(mmapID, \"Key_seq_1\", false, true, null);\n\n        kv.reKey(\"Key_Seq_Very_Looooooooong\", true);\n        kv.clearMemoryCache();\n        testMMKV(mmapID, \"Key_Seq_Very_Looooooooong\", true, true, null);\n\n        kv.reKey(null);\n        kv.clearMemoryCache();\n        testMMKV(mmapID, null, false, true, null);\n    }\n\n    private void interProcessBaselineTest(String cmd) {\n        Intent intent = new Intent(this, MyService.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, cmd);\n        startService(intent);\n\n        intent = new Intent(this, MyService_1.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, cmd);\n        startService(intent);\n    }\n\n    private void testAshmem() {\n        String cryptKey = \"Tencent MMKV\";\n        MMKV kv = MMKV.mmkvWithAshmemID(this, \"testAshmem\", MMKV.pageSize(), MMKV.SINGLE_PROCESS_MODE, cryptKey);\n\n        kv.encode(\"bool\", true);\n        Log.i(\"MMKV\", \"bool: \" + kv.decodeBool(\"bool\"));\n\n        kv.encode(\"int\", Integer.MIN_VALUE);\n        Log.i(\"MMKV\", \"int: \" + kv.decodeInt(\"int\"));\n\n        kv.encode(\"long\", Long.MAX_VALUE);\n        Log.i(\"MMKV\", \"long: \" + kv.decodeLong(\"long\"));\n\n        kv.encode(\"float\", -3.14f);\n        Log.i(\"MMKV\", \"float: \" + kv.decodeFloat(\"float\"));\n\n        kv.encode(\"double\", Double.MIN_VALUE);\n        Log.i(\"MMKV\", \"double: \" + kv.decodeDouble(\"double\"));\n\n        kv.encode(\"string\", \"Hello from mmkv\");\n        Log.i(\"MMKV\", \"string: \" + kv.decodeString(\"string\"));\n\n        byte[] bytes = {'m', 'm', 'k', 'v'};\n        kv.encode(\"bytes\", bytes);\n        Log.i(\"MMKV\", \"bytes: \" + new String(kv.decodeBytes(\"bytes\")));\n\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allKeys()));\n        Log.i(\"MMKV\",\n              \"count = \" + kv.count() + \", totalSize = \" + kv.totalSize() + \", actualSize = \" + kv.actualSize());\n        Log.i(\"MMKV\", \"containsKey[string]: \" + kv.containsKey(\"string\"));\n\n        kv.removeValueForKey(\"bool\");\n        Log.i(\"MMKV\", \"bool: \" + kv.decodeBool(\"bool\"));\n        kv.removeValueForKey(\"int\");\n        kv.removeValueForKey(\"long\");\n        //kv.removeValuesForKeys(new String[] {\"int\", \"long\"});\n        //kv.clearAll();\n        kv.clearMemoryCache();\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allKeys()));\n        Log.i(\"MMKV\", \"isFileValid[\" + kv.mmapID() + \"]: \" + MMKV.isFileValid(kv.mmapID()));\n    }\n\n    private void prepareInterProcessAshmem() {\n        Intent intent = new Intent(this, MyService_1.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, MyService_1.CMD_PREPARE_ASHMEM);\n        startService(intent);\n    }\n\n    private void prepareInterProcessAshmemByContentProvider(String cryptKey) {\n        // first of all, init ashmem mmkv in main process\n        MMKV.mmkvWithAshmemID(this, AshmemMMKV_ID, AshmemMMKV_Size, MMKV.MULTI_PROCESS_MODE, cryptKey);\n\n        // then other process can get by ContentProvider\n        Intent intent = new Intent(this, MyService.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, BenchMarkBaseService.CMD_PREPARE_ASHMEM_BY_CP);\n        startService(intent);\n\n        intent = new Intent(this, MyService_1.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, BenchMarkBaseService.CMD_PREPARE_ASHMEM_BY_CP);\n        startService(intent);\n    }\n\n    private void testInterProcessReKey() {\n        MMKV mmkv = MMKV.mmkvWithAshmemID(this, AshmemMMKV_ID, AshmemMMKV_Size, MMKV.MULTI_PROCESS_MODE, KEY_1);\n        mmkv.reKey(KEY_2);\n\n        prepareInterProcessAshmemByContentProvider(KEY_2);\n    }\n\n    private void testHolderForMultiThread() {\n        final int COUNT = 1;\n        final int THREAD_COUNT = 1;\n        final String ID = \"Hotel\";\n        final String KEY = \"California\";\n        final String VALUE = \"You can checkout any time you like, but you can never leave.\";\n\n        final MMKV mmkv = MMKV.mmkvWithID(ID);\n        Runnable task = new Runnable() {\n            public void run() {\n                for (int i = 0; i < COUNT; ++i) {\n                    mmkv.putString(KEY, VALUE);\n                    mmkv.getString(KEY, null);\n                    mmkv.remove(KEY);\n                }\n            }\n        };\n\n        for (int i = 0; i < THREAD_COUNT; ++i) {\n            new Thread(task, \"MMKV-\" + i).start();\n        }\n    }\n\n    private void testInterProcessLockPhase1() {\n        MMKV mmkv1 = MMKV.mmkvWithID(MyService.LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE);\n        mmkv1.lock();\n        Log.d(\"locked in main\", MyService.LOCK_PHASE_1);\n\n        Intent intent = new Intent(this, MyService.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, MyService.CMD_LOCK);\n        startService(intent);\n    }\n    private void testInterProcessLockPhase2() {\n        MMKV mmkv2 = MMKV.mmkvWithID(MyService.LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE);\n        mmkv2.lock();\n        Log.d(\"locked in main\", MyService.LOCK_PHASE_2);\n    }\n\n    private void testCornerSize() {\n        MMKV mmkv = MMKV.mmkvWithID(\"cornerSize\", MMKV.MULTI_PROCESS_MODE, \"aes\");\n        mmkv.clearAll();\n        int size = MMKV.pageSize() - 2;\n        size -= 4;\n        String key = \"key\";\n        int keySize = 3 + 1;\n        size -= keySize;\n        int valueSize = 3;\n        size -= valueSize;\n        byte[] value = new byte[size];\n        mmkv.encode(key, value);\n    }\n\n    private void testFastRemoveCornerSize() {\n        MMKV mmkv = MMKV.mmkvWithID(\"fastRemoveCornerSize\");\n        mmkv.clearAll();\n        int size = MMKV.pageSize() - 4;\n        size -= 4; // place holder size\n        String key = \"key\";\n        int keySize = 3 + 1;\n        size -= keySize;\n        int valueSize = 3;\n        size -= valueSize;\n        size -= (keySize + 1); // total size of fast remove\n        size /= 16;\n        byte[] value = new byte[size];\n        for (int i = 0; i < value.length; i++) {\n            value[i] = 'A';\n        }\n        for (int i = 0; i < 16; i++) {\n            mmkv.encode(key, value); // when a full write back is occur, here's corruption happens\n            mmkv.removeValueForKey(key);\n        }\n    }\n\n    private void testTrimNonEmptyInterProcess() {\n        MMKV mmkv = MMKV.mmkvWithID(\"trimNonEmptyInterProcess\", MMKV.MULTI_PROCESS_MODE);\n        mmkv.clearAll();\n        mmkv.encode(\"NonEmptyKey\", \"Hello, world!\");\n        byte[] value = new byte[MMKV.pageSize()];\n        mmkv.encode(\"largeKV\", value);\n        mmkv.removeValueForKey(\"largeKV\");\n        mmkv.trim();\n\n        Intent intent = new Intent(this, MyService.class);\n        intent.putExtra(BenchMarkBaseService.CMD_ID, MyService.CMD_TRIM_FINISH);\n        startService(intent);\n\n        SystemClock.sleep(1000 * 3);\n        Log.i(\"MMKV\", \"NonEmptyKey: \" + mmkv.decodeString(\"NonEmptyKey\"));\n    }\n\n    private void testItemSizeHolderOverride() {\n        // final String mmapID = \"testItemSizeHolderOverride_crypted\";\n        // final String encryptKey = \"encrypeKey\";\n        final String mmapID = \"testItemSizeHolderOverride_plaintext\";\n        final String encryptKey = null;\n        MMKV mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, encryptKey);\n        /* do this in v1.1.2\n        {\n            // mmkv.encode(\"b\", true);\n            byte[] value = new byte[512];\n            mmkv.encode(\"data\", value);\n            Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }*/\n        // do this in v1.2.0\n        {\n            long totalSize = mmkv.totalSize();\n            long bufferSize = totalSize - 512;\n            byte[] value = new byte[(int) bufferSize];\n            // force a fullwriteback()\n            mmkv.encode(\"bigData\", value);\n\n            mmkv.clearMemoryCache();\n            Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }\n    }\n\n    private void testBackup() {\n        File f = new File(MMKV.getRootDir());\n        String backupRootDir = f.getParent() + \"/mmkv_backup_3\";\n        String mmapID = \"test/AES\";\n        String otherDir = getFilesDir().getAbsolutePath() + \"/mmkv_3\";\n\n        boolean ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, otherDir);\n        Log.i(\"MMKV\", \"backup one [\" + mmapID + \"] ret = \" + ret);\n        if (ret) {\n            MMKV mmkv = MMKV.backedUpMMKVWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, \"Tencent MMKV\", backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }\n\n        // test backup a normal mmkv from custom root path\n        mmapID = \"test_backup\";\n        ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, otherDir);\n        Log.i(\"MMKV\", \"backup one [\" + mmapID + \"] ret = \" + ret);\n        if (ret) {\n            MMKV mmkv = MMKV.backedUpMMKVWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, \"MMKV Backup\", backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }\n\n        /*{\n            MMKV mmkv = MMKV.mmkvWithID(\"imported\");\n            mmkv.close();\n            mmkv = MMKV.mmkvWithID(\"test/AES_reKey1\");\n            mmkv.close();\n        }*/\n        backupRootDir = f.getParent() + \"/mmkv_backup\";\n        long count = MMKV.backupAllToDirectory(backupRootDir);\n        Log.i(\"MMKV\", \"backup all count \" + count);\n        if (count > 0) {\n            MMKV mmkv = MMKV.backedUpMMKVWithID(\"imported\", MMKV.SINGLE_PROCESS_MODE, null, backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.backedUpMMKVWithID(\"testKotlin\", MMKV.SINGLE_PROCESS_MODE, null, backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.backedUpMMKVWithID(\"test/AES_reKey1\", MMKV.SINGLE_PROCESS_MODE, null, backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.backedUpMMKVWithID(\"benchmark_interprocess\", MMKV.MULTI_PROCESS_MODE, null, backupRootDir);\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys count: \" + mmkv.count());\n        }\n    }\n\n    private void testRestore() {\n        File f = new File(MMKV.getRootDir());\n        String backupRootDir = f.getParent() + \"/mmkv_backup_3\";\n        String mmapID = \"test/AES\";\n        String otherDir = getFilesDir().getAbsolutePath() + \"/mmkv_3\";\n\n        MMKV mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, \"Tencent MMKV\", otherDir);\n        mmkv.encode(\"test_restore\", true);\n        Log.i(\"MMKV\", \"before restore [\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        boolean ret = MMKV.restoreOneMMKVFromDirectory(mmapID, backupRootDir, otherDir);\n        Log.i(\"MMKV\", \"restore one [\" + mmapID + \"] ret = \" + ret);\n        if (ret) {\n            Log.i(\"MMKV\", \"after restore [\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }\n\n        // test backup a normal mmkv from custom root path\n        mmapID = \"test_backup\";\n        mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, \"MMKV Backup\", otherDir);\n        mmkv.encode(\"test_restore\", 1024);\n        Log.i(\"MMKV\", \"before restore [\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        ret = MMKV.restoreOneMMKVFromDirectory(mmapID, backupRootDir, otherDir);\n        Log.i(\"MMKV\", \"backup one [\" + mmapID + \"] ret = \" + ret);\n        if (ret) {\n            Log.i(\"MMKV\", \"check on backup file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n        }\n\n        /*{\n            mmkv = MMKV.mmkvWithID(\"imported\");\n            mmkv.close();\n            mmkv = MMKV.mmkvWithID(\"test/AES_reKey1\");\n            mmkv.close();\n        }*/\n        backupRootDir = f.getParent() + \"/mmkv_backup\";\n        long count = MMKV.restoreAllFromDirectory(backupRootDir);\n        Log.i(\"MMKV\", \"restore all count \" + count);\n        if (count > 0) {\n            mmkv = MMKV.mmkvWithID(\"imported\");\n            Log.i(\"MMKV\", \"check on restore file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.mmkvWithID(\"testKotlin\");\n            Log.i(\"MMKV\", \"check on restore file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.mmkvWithID(\"test/AES_reKey1\");\n            Log.i(\"MMKV\", \"check on restore file[\" + mmkv.mmapID() + \"] allKeys: \" + Arrays.toString(mmkv.allKeys()));\n\n            mmkv = MMKV.mmkvWithID(\"benchmark_interprocess\", MMKV.MULTI_PROCESS_MODE);\n            Log.i(\"MMKV\", \"check on restore file[\" + mmkv.mmapID() + \"] allKeys count: \" + mmkv.count());\n        }\n    }\n\n    private void testAutoExpire(MMKV kv, boolean decodeOnly, int expiration) {\n        if (!decodeOnly) {\n            kv.encode(\"bool\", true, expiration);\n        }\n        Log.i(\"MMKV\", \"bool: \" + kv.decodeBool(\"bool\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"int\", Integer.MIN_VALUE, expiration);\n        }\n        Log.i(\"MMKV\", \"int: \" + kv.decodeInt(\"int\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"long\", Long.MAX_VALUE, expiration);\n        }\n        Log.i(\"MMKV\", \"long: \" + kv.decodeLong(\"long\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"float\", -3.14f, expiration);\n        }\n        Log.i(\"MMKV\", \"float: \" + kv.decodeFloat(\"float\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"double\", Double.MIN_VALUE, expiration);\n        }\n        Log.i(\"MMKV\", \"double: \" + kv.decodeDouble(\"double\"));\n\n        if (!decodeOnly) {\n            kv.encode(\"string\", \"Hello from mmkv\", expiration);\n        }\n        Log.i(\"MMKV\", \"string: \" + kv.decodeString(\"string\"));\n\n        if (!decodeOnly) {\n            byte[] bytes = {'m', 'm', 'k', 'v'};\n            kv.encode(\"bytes\", bytes, expiration);\n        }\n        byte[] bytes = kv.decodeBytes(\"bytes\");\n        if (bytes != null) {\n            Log.i(\"MMKV\", \"bytes: \" + new String(bytes));\n            Log.i(\"MMKV\", \"bytes length = \" + bytes.length + \", value size consumption = \" + kv.getValueSize(\"bytes\")\n                    + \", value size = \" + kv.getValueActualSize(\"bytes\"));\n\n            int sizeNeeded = kv.getValueActualSize(\"bytes\");\n            NativeBuffer nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);\n            if (nativeBuffer != null) {\n                int size = kv.writeValueToNativeBuffer(\"bytes\", nativeBuffer);\n                Log.i(\"MMKV\", \"size Needed = \" + sizeNeeded + \" written size = \" + size);\n                MMKV.destroyNativeBuffer(nativeBuffer);\n            }\n        }\n\n        if (!decodeOnly) {\n            TestParcelable testParcelable = new TestParcelable(1024, \"Hi Parcelable\");\n            kv.encode(\"parcel\", testParcelable, expiration);\n        }\n        TestParcelable result = kv.decodeParcelable(\"parcel\", TestParcelable.class);\n        if (result != null) {\n            Log.i(\"MMKV\", \"parcel: \" + result.iValue + \", \" + result.sValue + \", \" + result.list);\n        }\n\n        if (!decodeOnly) {\n            kv.encode(\"null string\", \"some string\", expiration);\n        }\n        Log.i(\"MMKV\", \"string before set null: \" + kv.decodeString(\"null string\"));\n        if (!decodeOnly) {\n            kv.encode(\"null string\", (String) null, expiration);\n        }\n        Log.i(\"MMKV\", \"string after set null: \" + kv.decodeString(\"null string\")\n                + \", containsKey:\" + kv.contains(\"null string\"));\n\n        Log.i(\"MMKV\", \"allKeys: \" + Arrays.toString(kv.allNonExpireKeys()));\n        Log.i(\"MMKV\",\n                \"count = \" + kv.countNonExpiredKeys() + \", totalSize = \" + kv.totalSize() + \", actualSize = \" + kv.actualSize());\n        Log.i(\"MMKV\", \"containsKey[string]: \" + kv.containsKey(\"string\"));\n    }\n\n    private void testAutoExpire() {\n        // disable auto expire by configuration\n        MMKVConfig config = new MMKVConfig();\n        config.enableKeyExpire = false;\n//        config.recover = MMKVRecoverStrategic.OnErrorRecover;\n//        config.itemSizeLimit = 1;\n        MMKV mmkv = MMKV.mmkvWithID(\"test_auto_expire\", config);\n        mmkv.clearAll();\n        mmkv.disableAutoKeyExpire(); // this become a no-op\n\n        // enable auto expire by configuration\n        mmkv.close();\n        config.enableKeyExpire = true;\n        config.expiredInSeconds = 1;\n        mmkv = MMKV.mmkvWithID(\"test_auto_expire\", config);\n        mmkv.enableAutoKeyExpire(1); // this become a no-op\n        mmkv.encode(\"auto_expire_key_1\", true);\n        mmkv.encode(\"never_expire_key_1\", true, MMKV.ExpireNever);\n\n//        mmkv.enableCompareBeforeSet();\n\n        testAutoExpire(mmkv, false, 1);\n        SystemClock.sleep(1000 * 2);\n        testAutoExpire(mmkv, true, 1);\n\n        // mmkv.encode(\"string\", \"Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJFbHFTUjB\");\n        // mmkv.clearMemoryCache();\n        // Log.i(\"MMKV\", \"space string = \" + mmkv.decodeString(\"string\", \"\"));\n\n        if (mmkv.contains(\"auto_expire_key_1\")) {\n            Log.e(\"MMKV\", \"auto key expiration auto_expire_key_1\");\n        } else {\n            Log.i(\"MMKV\", \"auto key expiration auto_expire_key_1\");\n        }\n        if (mmkv.contains(\"never_expire_key_1\")) {\n            Log.i(\"MMKV\", \"auto key expiration never_expire_key_1\");\n        } else {\n            Log.e(\"MMKV\", \"auto key expiration never_expire_key_1\");\n        }\n    }\n\n    private int addExtraRoundUp(int len) {\n        int pageSize = MMKV.pageSize();\n        int rest = len % pageSize;\n        return rest == 0 ? len + pageSize :  (2 + len / pageSize) * pageSize;\n    }\n\n    private void testDiskFull() {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                MMKV mmkv = MMKV.mmkvWithID(\"disk_full\");\n                long i = 0;\n                StringBuilder value = new StringBuilder();\n                for (int j = 0; j < 100000; j++) {\n                    value.append(\"a\");\n                }\n                while (true) {\n                    boolean ret = mmkv.encode(i++ + \"\", value.toString());\n                    if (!ret) {\n                        break;\n                    }\n                }\n            }\n        }).start();\n    }\n\n    private void testClearAllKeepSpace() {\n        MMKV mmkv = MMKV.mmkvWithID(\"testClearAllKeepSpace\");\n        mmkv.encode(\"key\", \"value\");\n        mmkv.encode(\"key2\", \"value2\");\n        mmkv.clearAllWithKeepingSpace();\n        mmkv.encode(\"key3\", \"value3\");\n        mmkv.clearAll();\n        mmkv.encode(\"key4\", \"value4\");\n    }\n\n    private void testExpectedCapacity() {\n        String key = \"key0\";\n        String value = \"🏊🏻®4️⃣🐅_\";\n        int len = 10000;\n        for (int i = 0; i < len; i++) {\n            value += \"0\";\n        }\n        Log.i(\"MMKV\", \"value size = \" + value.getBytes().length);\n        int expectedSize = key.getBytes().length + value.getBytes().length;\n        // if we know exactly the sizes of key and value, set expectedCapacity for performance improvement\n        // extra space can be added to round up\n        MMKV mmkv = MMKV.mmkvWithID(\"test_expected_capacity0\", MMKV.SINGLE_PROCESS_MODE,\n                addExtraRoundUp(len));\n        // 0 times expand\n        mmkv.encode(key, value);\n\n        int count = 10;\n        expectedSize = expectedSize * count;\n        MMKV mmkv1 = MMKV.mmkvWithID(\"test_expected_capacity1\", MMKV.SINGLE_PROCESS_MODE,\n                addExtraRoundUp(expectedSize));\n        for (int i = 0; i < count; i++) {\n            String k = \"key\" + i;\n            // 0 times expand\n            mmkv1.encode(k, value);\n        }\n    }\n\n    private void testRemoveStorageAndCheckExist() {\n        String mmapID = \"test_remove\";\n        {\n            MMKV mmkv = MMKV.mmkvWithID(mmapID, MMKV.MULTI_PROCESS_MODE);\n            mmkv.encode(\"bool\", true);\n        }\n        Log.i(\"MMKV\", \"checkExist = \" + MMKV.checkExist(mmapID));\n        MMKV.removeStorage(mmapID);\n        Log.i(\"MMKV\", \"after remove, checkExist = \" + MMKV.checkExist(mmapID));\n        {\n            MMKV mmkv = MMKV.mmkvWithID(mmapID, MMKV.MULTI_PROCESS_MODE);\n            if (mmkv.count() != 0) {\n                Log.e(\"MMKV\", \"storage not successfully removed\");\n            }\n        }\n\n        mmapID = \"test_remove/sg\";\n        String rootDir = getFilesDir().getAbsolutePath() + \"/mmkv_sg\";\n        MMKV mmkv = MMKV.mmkvWithID(mmapID, rootDir);\n        mmkv.encode(\"bool\", true);\n        Log.i(\"MMKV\", \"checkExist = \" + MMKV.checkExist(mmapID, rootDir));\n        MMKV.removeStorage(mmapID, rootDir);\n        Log.i(\"MMKV\", \"after remove, checkExist = \" + MMKV.checkExist(mmapID, rootDir));\n        mmkv = MMKV.mmkvWithID(mmapID, rootDir);\n        if (mmkv.count() != 0) {\n            Log.e(\"MMKV\", \"storage not successfully removed\");\n        }\n    }\n\n    private void overrideTest() {\n        MMKV mmkv0 = MMKV.mmkvWithID(\"overrideTest\");\n        String key = \"hello\";\n        String key2 = \"hello2\";\n        String value = \"world\";\n\n        mmkv0.encode(key, value);\n        String v2 = mmkv0.decodeString(key);\n        if (!value.equals(v2)) {\n            System.out.println(\"value = \" + v2);\n            System.exit(1);\n        }\n        mmkv0.removeValueForKey(key);\n\n        mmkv0.encode(key2, value);\n        v2 = mmkv0.decodeString(key2);\n        if (!value.equals(v2)) {\n            System.out.println(\"value = \" + v2);\n            System.exit(1);\n        }\n        mmkv0.removeValueForKey(key2);\n\n        int len = 10000;\n        StringBuilder bigValue = new StringBuilder(\"🏊🏻®4️⃣🐅_\");\n        for (int i = 0; i < len; i++) {\n            bigValue.append(\"0\");\n        }\n        mmkv0.encode(key, bigValue.toString());\n        String v3 = mmkv0.decodeString(key);\n        if (!bigValue.toString().equals(v3)) {\n            System.exit(1);\n        }\n\n        // rewrite\n        mmkv0.encode(key, \"OK\");\n        String v4 = mmkv0.decodeString(key);\n        if (!\"OK\".equals(v4)) {\n            System.out.println(\"value = \" + v2);\n            System.exit(1);\n        }\n\n        mmkv0.encode(key, 12345);\n        int v5 = mmkv0.decodeInt(key);\n        if (v5 != 12345) {\n            System.out.println(\"value = \" + v5);\n            System.exit(1);\n        }\n        mmkv0.removeValueForKey(key);\n\n        mmkv0.clearAll();\n\n        overrideTestEncrypt();\n    }\n\n    private void overrideTestEncrypt() {\n        // test small value\n        encryptionTest(\"cryptworld\");\n        // test medium value\n        encryptionTest(\"An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX.\");\n        // test large value\n        encryptionTest(\"An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX. MMKV is an efficient, small, easy-to-use mobile key-value storage framework used in the WeChat application. It's currently available on Android, iOS/macOS, Windows, POSIX and HarmonyOS NEXT.\");\n    }\n\n    private void encryptionTest(String value) {\n        String key = \"hello\";\n        String key2 = \"hello2\";\n\n        encryptionTestKV(key, value);\n        encryptionTestKV(key2, value);\n    }\n\n    private void encryptionTestKV(String key, String value) {\n        String crypt = \"fastestCrypt\";\n        MMKV mmkv0 = MMKV.mmkvWithID(\"overrideCryptTest\", MMKV.SINGLE_PROCESS_MODE, crypt);\n\n        mmkv0.encode(key, value);\n        String v2 = mmkv0.decodeString(key);\n        if (!value.equals(v2)) {\n            System.out.println(\"value = \" + value + \", result = \" + v2);\n            System.exit(1);\n        }\n\n        mmkv0.close();\n        mmkv0 = MMKV.mmkvWithID(\"overrideCryptTest\", MMKV.SINGLE_PROCESS_MODE, crypt);\n        v2 = mmkv0.decodeString(key);\n        if (!value.equals(v2)) {\n            System.out.println(\"value = \" + value + \", result = \" + v2);\n            System.exit(1);\n        }\n        mmkv0.encode(key, value);\n        v2 = mmkv0.decodeString(key);\n        if (!value.equals(v2)) {\n            System.out.println(\"value = \" + value + \", result = \" + v2);\n            System.exit(1);\n        }\n        mmkv0.removeValueForKey(key);\n    }\n\n    private void testReadOnly() {\n        final String name = \"testReadOnly\";\n        final String key = \"readonly+key\";\n        {\n            MMKV kv = MMKV.mmkvWithID(name, MMKV.SINGLE_PROCESS_MODE, key);\n            testMMKV(kv, false);\n            kv.close();\n        }\n\n        String path = MMKV.getRootDir() + \"/\" + name;\n        File file = new File(path);\n        file.setReadOnly();\n        File crcFile = new File(path + \".crc\");\n        crcFile.setReadOnly();\n\n        MMKV kv = MMKV.mmkvWithID(name, MMKV.SINGLE_PROCESS_MODE | MMKV.READ_ONLY_MODE, key);\n        testMMKV(kv, true);\n\n        // also check if it tolerate update operations without crash\n        testMMKV(kv, false);\n\n        file.setWritable(true);\n        crcFile.setWritable(true);\n    }\n\n    private void testImport() {\n        final String mmapID = \"testImportSrc\";\n        MMKV src = MMKV.mmkvWithID(mmapID);\n        src.encode(\"bool\", true);\n        src.encode(\"int\", Integer.MIN_VALUE);\n        src.encode(\"long\", Long.MAX_VALUE);\n        src.encode(\"string\", \"test import\");\n\n        MMKV dst = MMKV.mmkvWithID(\"testImportDst\");\n        dst.clearAll();\n        dst.enableAutoKeyExpire(1);\n        dst.encode(\"bool\", false);\n        dst.encode(\"int\", -1);\n        dst.encode(\"long\", 0);\n        dst.encode(\"string\", mmapID);\n\n        long count = dst.importFrom(src);\n        if (count != 4 || dst.count() != 4) {\n            Log.e(\"MMKV\", \"import check count fail\");\n        }\n        if (!dst.decodeBool(\"bool\")) {\n            Log.e(\"MMKV\", \"import check bool fail\");\n        }\n        if (dst.decodeInt(\"int\") != Integer.MIN_VALUE) {\n            Log.e(\"MMKV\", \"import check int fail\");\n        }\n        if (dst.decodeLong(\"long\") != Long.MAX_VALUE) {\n            Log.e(\"MMKV\", \"import check long fail\");\n        }\n        if (!Objects.equals(dst.decodeString(\"string\"), \"test import\")) {\n            Log.e(\"MMKV\", \"import check string fail\");\n        }\n\n        SystemClock.sleep(1000 * 2);\n        if (dst.countNonExpiredKeys() != 0) {\n            Log.e(\"MMKV\", \"import check expire fail\");\n        }\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MultiProcessSharedPreferences.java",
    "content": "package com.tencent.mmkvdemo;\n\nimport android.content.BroadcastReceiver;\nimport android.content.ContentProvider;\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.content.UriMatcher;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PackageManager.NameNotFoundException;\nimport android.content.pm.ProviderInfo;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport java.lang.ref.SoftReference;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 使用ContentProvider实现多进程SharedPreferences读写;<br>\n * 1、ContentProvider天生支持多进程访问；<br>\n * 2、使用内部私有BroadcastReceiver实现多进程OnSharedPreferenceChangeListener监听；<br>\n * \n * 使用方法：AndroidManifest.xml中添加provider申明：<br>\n * <pre>\n * &lt;provider android:name=\"com.tencent.mm.sdk.patformtools.MultiProcessSharedPreferences\"\n * android:authorities=\"com.tencent.mm.sdk.patformtools.MultiProcessSharedPreferences\"\n * android:exported=\"false\" /&gt;\n * &lt;!-- authorities属性里面最好使用包名做前缀，apk在安装时authorities同名的provider需要校验签名，否则无法安装；--!/&gt;<br>\n * </pre>\n * \n * ContentProvider方式实现要注意：<br>\n * 1、当ContentProvider所在进程android.os.Process.killProcess(pid)时，会导致整个应用程序完全意外退出或者ContentProvider所在进程重启；<br>\n * 重启报错信息：Acquiring provider <processName> for user 0: existing object's process dead；<br>\n * 2、如果设备处在“安全模式”下，只有系统自带的ContentProvider才能被正常解析使用，因此put值时默认返回false，get值时默认返回null；<br>\n * \n * 其他方式实现SharedPreferences的问题：<br>\n * 使用FileLock和FileObserver也可以实现多进程SharedPreferences读写，但是会有兼容性问题：<br>\n * 1、某些设备上卸载程序时锁文件无法删除导致卸载残留，进而导致无法重新安装该程序（报INSTALL_FAILED_UID_CHANGED错误）；<br>\n * 2、某些设备上FileLock会导致僵尸进程出现进而导致耗电；<br>\n * 3、僵尸进程出现后，正常进程的FileLock会一直阻塞等待僵尸进程中的FileLock释放，导致进程一直阻塞；<br>\n * \n * @author seven456@gmail.com\n * @version\t1.0\n * @since JDK1.6\n *\n */\npublic class MultiProcessSharedPreferences extends ContentProvider implements SharedPreferences {\n    private static final String TAG = \"MicroMsg.MultiProcessSharedPreferences\";\n    public static final boolean DEBUG = BuildConfig.DEBUG;\n    private Context mContext;\n    private String mName;\n    private int mMode;\n    private boolean mIsSafeMode;\n    private List<SoftReference<OnSharedPreferenceChangeListener>> mListeners;\n    private BroadcastReceiver mReceiver;\n\n    private static String AUTHORITY;\n    private static volatile Uri AUTHORITY_URI;\n    private UriMatcher mUriMatcher;\n    private static final String KEY = \"value\";\n    private static final String KEY_NAME = \"name\";\n    private static final String PATH_WILDCARD = \"*/\";\n    private static final String PATH_GET_ALL = \"getAll\";\n    private static final String PATH_GET_STRING = \"getString\";\n    private static final String PATH_GET_INT = \"getInt\";\n    private static final String PATH_GET_LONG = \"getLong\";\n    private static final String PATH_GET_FLOAT = \"getFloat\";\n    private static final String PATH_GET_BOOLEAN = \"getBoolean\";\n    private static final String PATH_CONTAINS = \"contains\";\n    private static final String PATH_APPLY = \"apply\";\n    private static final String PATH_COMMIT = \"commit\";\n    private static final String PATH_REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER =\n        \"registerOnSharedPreferenceChangeListener\";\n    private static final String PATH_UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER =\n        \"unregisterOnSharedPreferenceChangeListener\";\n    private static final int GET_ALL = 1;\n    private static final int GET_STRING = 2;\n    private static final int GET_INT = 3;\n    private static final int GET_LONG = 4;\n    private static final int GET_FLOAT = 5;\n    private static final int GET_BOOLEAN = 6;\n    private static final int CONTAINS = 7;\n    private static final int APPLY = 8;\n    private static final int COMMIT = 9;\n    private static final int REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = 10;\n    private static final int UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = 11;\n    private Map<String, Integer> mListenersCount;\n\n    private static class ReflectionUtil {\n\n        public static ContentValues contentValuesNewInstance(HashMap<String, Object> values) {\n            try {\n                Constructor<ContentValues> c =\n                    ContentValues.class.getDeclaredConstructor(new Class[] {HashMap.class}); // hide\n                c.setAccessible(true);\n                return c.newInstance(values);\n            } catch (IllegalArgumentException e) {\n                throw new RuntimeException(e);\n            } catch (IllegalAccessException e) {\n                throw new RuntimeException(e);\n            } catch (InvocationTargetException e) {\n                throw new RuntimeException(e);\n            } catch (NoSuchMethodException e) {\n                throw new RuntimeException(e);\n            } catch (InstantiationException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        public static Editor editorPutStringSet(Editor editor, String key, Set<String> values) {\n            try {\n                Method method = editor.getClass().getDeclaredMethod(\n                    \"putStringSet\", new Class[] {String.class, Set.class}); // Android 3.0\n                return (Editor) method.invoke(editor, key, values);\n            } catch (IllegalArgumentException e) {\n                throw new RuntimeException(e);\n            } catch (IllegalAccessException e) {\n                throw new RuntimeException(e);\n            } catch (InvocationTargetException e) {\n                throw new RuntimeException(e);\n            } catch (NoSuchMethodException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        public static void editorApply(Editor editor) {\n            try {\n                Method method = editor.getClass().getDeclaredMethod(\"apply\"); // Android 2.3\n                method.invoke(editor);\n            } catch (IllegalArgumentException e) {\n                throw new RuntimeException(e);\n            } catch (IllegalAccessException e) {\n                throw new RuntimeException(e);\n            } catch (InvocationTargetException e) {\n                throw new RuntimeException(e);\n            } catch (NoSuchMethodException e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    private void checkInitAuthority(Context context) {\n        if (AUTHORITY_URI == null) {\n            String AUTHORITY = null;\n            Uri AUTHORITY_URI = MultiProcessSharedPreferences.AUTHORITY_URI;\n            synchronized (MultiProcessSharedPreferences.this) {\n                if (AUTHORITY_URI == null) {\n                    AUTHORITY = queryAuthority(context);\n                    AUTHORITY_URI = Uri.parse(ContentResolver.SCHEME_CONTENT + \"://\" + AUTHORITY);\n                    if (DEBUG) {\n                        //\t\t\t\t\t\tLog.d(TAG, \"checkInitAuthority.AUTHORITY = \" + AUTHORITY);\n                    }\n                }\n                if (AUTHORITY == null) {\n                    throw new IllegalArgumentException(\"'AUTHORITY' initialize failed.\");\n                }\n            }\n            MultiProcessSharedPreferences.AUTHORITY = AUTHORITY;\n            MultiProcessSharedPreferences.AUTHORITY_URI = AUTHORITY_URI;\n        }\n    }\n\n    private static String queryAuthority(Context context) {\n        PackageInfo packageInfos = null;\n        try {\n            PackageManager mgr = context.getPackageManager();\n            if (mgr != null) {\n                packageInfos = mgr.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);\n            }\n        } catch (NameNotFoundException e) {\n            if (DEBUG) {\n                //\t\t\t\tLog.printErrStackTrace(TAG, e, \"\");\n            }\n        }\n        if (packageInfos != null && packageInfos.providers != null) {\n            for (ProviderInfo providerInfo : packageInfos.providers) {\n                if (providerInfo.name.equals(MultiProcessSharedPreferences.class.getName())) {\n                    return providerInfo.authority;\n                }\n            }\n        }\n        return null;\n    }\n\n    /**\n\t * mode不使用{@link Context#MODE_MULTI_PROCESS}特可以支持多进程了；\n\t * \n\t * @param mode\n\t * \n\t * @see Context#MODE_PRIVATE\n\t * @see Context#MODE_WORLD_READABLE\n\t * @see Context#MODE_WORLD_WRITEABLE\n\t */\n    public static SharedPreferences getSharedPreferences(Context context, String name, int mode) {\n        return new MultiProcessSharedPreferences(context, name, mode);\n    }\n\n    public MultiProcessSharedPreferences() {\n    }\n\n    private MultiProcessSharedPreferences(Context context, String name, int mode) {\n        mContext = context;\n        mName = name;\n        mMode = mode;\n        PackageManager mgr = context.getPackageManager();\n        if (mgr != null) {\n            mIsSafeMode =\n                mgr.isSafeMode(); // 如果设备处在“安全模式”下，只有系统自带的ContentProvider才能被正常解析使用；\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public Map<String, ?> getAll() {\n        return (Map<String, ?>) getValue(PATH_GET_ALL, null, null);\n    }\n\n    @Override\n    public String getString(String key, String defValue) {\n        String v = (String) getValue(PATH_GET_STRING, key, defValue);\n        return v != null ? v : defValue;\n    }\n\n    // @Override // Android 3.0\n    public Set<String> getStringSet(String key, Set<String> defValues) {\n        synchronized (this) {\n            @SuppressWarnings(\"unchecked\")\n            Set<String> v = (Set<String>) getValue(PATH_GET_STRING, key, defValues);\n            return v != null ? v : defValues;\n        }\n    }\n\n    @Override\n    public int getInt(String key, int defValue) {\n        Integer v = (Integer) getValue(PATH_GET_INT, key, defValue);\n        return v != null ? v : defValue;\n    }\n\n    @Override\n    public long getLong(String key, long defValue) {\n        Long v = (Long) getValue(PATH_GET_LONG, key, defValue);\n        return v != null ? v : defValue;\n    }\n\n    @Override\n    public float getFloat(String key, float defValue) {\n        Float v = (Float) getValue(PATH_GET_FLOAT, key, defValue);\n        return v != null ? v : defValue;\n    }\n\n    @Override\n    public boolean getBoolean(String key, boolean defValue) {\n        Boolean v = (Boolean) getValue(PATH_GET_BOOLEAN, key, defValue);\n        return v != null ? v : defValue;\n    }\n\n    @Override\n    public boolean contains(String key) {\n        Boolean v = (Boolean) getValue(PATH_CONTAINS, key, null);\n        return v != null ? v : false;\n    }\n\n    @Override\n    public Editor edit() {\n        return new EditorImpl();\n    }\n\n    @Override\n    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        synchronized (this) {\n            if (mListeners == null) {\n                mListeners = new ArrayList<SoftReference<OnSharedPreferenceChangeListener>>();\n            }\n            Boolean result = (Boolean) getValue(PATH_REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER, null, false);\n            if (result != null && result) {\n                mListeners.add(new SoftReference<OnSharedPreferenceChangeListener>(listener));\n                if (mReceiver == null) {\n                    mReceiver = new BroadcastReceiver() {\n                        @Override\n                        public void onReceive(Context context, Intent intent) {\n                            String name = intent.getStringExtra(KEY_NAME);\n                            @SuppressWarnings(\"unchecked\")\n                            List<String> keysModified = (List<String>) intent.getSerializableExtra(KEY);\n                            if (mName.equals(name) && keysModified != null) {\n                                List arrayListeners;\n                                synchronized (MultiProcessSharedPreferences.this) {\n                                    arrayListeners = mListeners;\n                                }\n                                List<SoftReference<OnSharedPreferenceChangeListener>> listeners =\n                                    new ArrayList<SoftReference<OnSharedPreferenceChangeListener>>(arrayListeners);\n                                for (int i = keysModified.size() - 1; i >= 0; i--) {\n                                    final String key = keysModified.get(i);\n                                    for (SoftReference<OnSharedPreferenceChangeListener> srlistener : listeners) {\n                                        OnSharedPreferenceChangeListener listener = srlistener.get();\n                                        if (listener != null) {\n                                            listener.onSharedPreferenceChanged(MultiProcessSharedPreferences.this, key);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    };\n                    mContext.registerReceiver(mReceiver, new IntentFilter(makeAction(mName)));\n                }\n            }\n        }\n    }\n\n    @Override\n    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {\n        synchronized (this) {\n            getValue(PATH_UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER, null, false);\n            if (mListeners != null) {\n                List<SoftReference<OnSharedPreferenceChangeListener>> removing = new ArrayList<>();\n                for (SoftReference<OnSharedPreferenceChangeListener> srlistener : mListeners) {\n                    OnSharedPreferenceChangeListener listenerFromSR = srlistener.get();\n                    if (listenerFromSR != null && listenerFromSR.equals(listener)) {\n                        removing.add(srlistener);\n                    }\n                }\n                for (SoftReference<OnSharedPreferenceChangeListener> srlistener : removing) {\n                    mListeners.remove(srlistener);\n                }\n                if (mListeners.isEmpty() && mReceiver != null) {\n                    mContext.unregisterReceiver(mReceiver);\n                    mReceiver = null;\n                    mListeners = null;\n                }\n            }\n        }\n    }\n\n    public final class EditorImpl implements Editor {\n        private final Map<String, Object> mModified = new HashMap<String, Object>();\n        private boolean mClear = false;\n\n        @Override\n        public Editor putString(String key, String value) {\n            synchronized (this) {\n                mModified.put(key, value);\n                return this;\n            }\n        }\n\n        // @Override // Android 3.0\n        public Editor putStringSet(String key, Set<String> values) {\n            synchronized (this) {\n                mModified.put(key, (values == null) ? null : new HashSet<String>(values));\n                return this;\n            }\n        }\n\n        @Override\n        public Editor putInt(String key, int value) {\n            synchronized (this) {\n                mModified.put(key, value);\n                return this;\n            }\n        }\n\n        @Override\n        public Editor putLong(String key, long value) {\n            synchronized (this) {\n                mModified.put(key, value);\n                return this;\n            }\n        }\n\n        @Override\n        public Editor putFloat(String key, float value) {\n            synchronized (this) {\n                mModified.put(key, value);\n                return this;\n            }\n        }\n\n        @Override\n        public Editor putBoolean(String key, boolean value) {\n            synchronized (this) {\n                mModified.put(key, value);\n                return this;\n            }\n        }\n\n        @Override\n        public Editor remove(String key) {\n            synchronized (this) {\n                mModified.put(key, null);\n                return this;\n            }\n        }\n\n        @Override\n        public Editor clear() {\n            synchronized (this) {\n                mClear = true;\n                return this;\n            }\n        }\n\n        @Override\n        public void apply() {\n            setValue(PATH_APPLY);\n        }\n\n        @Override\n        public boolean commit() {\n            return setValue(PATH_COMMIT);\n        }\n\n        private boolean setValue(String pathSegment) {\n            if (mIsSafeMode) { // 如果设备处在“安全模式”，返回false；\n                return false;\n            } else {\n                synchronized (MultiProcessSharedPreferences.this) {\n                    checkInitAuthority(mContext);\n                    String[] selectionArgs = new String[] {String.valueOf(mMode), String.valueOf(mClear)};\n                    synchronized (this) {\n                        Uri uri = Uri.withAppendedPath(Uri.withAppendedPath(AUTHORITY_URI, mName), pathSegment);\n                        ContentValues values =\n                            ReflectionUtil.contentValuesNewInstance((HashMap<String, Object>) mModified);\n                        return mContext.getContentResolver().update(uri, values, null, selectionArgs) > 0;\n                    }\n                }\n            }\n        }\n    }\n\n    private Object getValue(String pathSegment, String key, Object defValue) {\n        if (mIsSafeMode) { // 如果设备处在“安全模式”，返回null；\n            return null;\n        } else {\n            checkInitAuthority(mContext);\n            Object v = null;\n            Uri uri = Uri.withAppendedPath(Uri.withAppendedPath(AUTHORITY_URI, mName), pathSegment);\n            String[] selectionArgs =\n                new String[] {String.valueOf(mMode), key, defValue == null ? null : String.valueOf(defValue)};\n            Cursor cursor = mContext.getContentResolver().query(uri, null, null, selectionArgs, null);\n            if (cursor != null) {\n                try {\n                    Bundle bundle = cursor.getExtras();\n                    if (bundle != null) {\n                        v = bundle.get(KEY);\n                        bundle.clear();\n                    }\n                } catch (Exception e) {\n                }\n                cursor.close();\n            }\n            return v != null ? v : defValue;\n        }\n    }\n\n    private String makeAction(String name) {\n        return String.format(\"%1$s_%2$s\", MultiProcessSharedPreferences.class.getName(), name);\n    }\n\n    @Override\n    public boolean onCreate() {\n        checkInitAuthority(getContext());\n        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_ALL, GET_ALL);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_STRING, GET_STRING);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_INT, GET_INT);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_LONG, GET_LONG);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_FLOAT, GET_FLOAT);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_GET_BOOLEAN, GET_BOOLEAN);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_CONTAINS, CONTAINS);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_APPLY, APPLY);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_COMMIT, COMMIT);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER,\n                           REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER);\n        mUriMatcher.addURI(AUTHORITY, PATH_WILDCARD + PATH_UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER,\n                           UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER);\n        return true;\n    }\n\n    @Override\n    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {\n        String name = uri.getPathSegments().get(0);\n        int mode = Integer.parseInt(selectionArgs[0]);\n        String key = selectionArgs[1];\n        String defValue = selectionArgs[2];\n        Bundle bundle = new Bundle();\n        switch (mUriMatcher.match(uri)) {\n            case GET_ALL:\n                bundle.putSerializable(KEY,\n                                       (HashMap<String, ?>) getContext().getSharedPreferences(name, mode).getAll());\n                break;\n            case GET_STRING:\n                bundle.putString(KEY, getContext().getSharedPreferences(name, mode).getString(key, defValue));\n                break;\n            case GET_INT:\n                bundle.putInt(KEY,\n                              getContext().getSharedPreferences(name, mode).getInt(key, Integer.parseInt(defValue)));\n                break;\n            case GET_LONG:\n                bundle.putLong(KEY,\n                               getContext().getSharedPreferences(name, mode).getLong(key, Long.parseLong(defValue)));\n                break;\n            case GET_FLOAT:\n                bundle.putFloat(\n                    KEY, getContext().getSharedPreferences(name, mode).getFloat(key, Float.parseFloat(defValue)));\n                break;\n            case GET_BOOLEAN:\n                bundle.putBoolean(\n                    KEY, getContext().getSharedPreferences(name, mode).getBoolean(key, Boolean.parseBoolean(defValue)));\n                break;\n            case CONTAINS:\n                bundle.putBoolean(KEY, getContext().getSharedPreferences(name, mode).contains(key));\n                break;\n            case REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER: {\n                checkInitListenersCount();\n                Integer countInteger = mListenersCount.get(name);\n                int count = (countInteger == null ? 0 : countInteger) + 1;\n                mListenersCount.put(name, count);\n                countInteger = mListenersCount.get(name);\n                bundle.putBoolean(KEY, count == (countInteger == null ? 0 : countInteger));\n            } break;\n            case UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER: {\n                checkInitListenersCount();\n                Integer countInteger = mListenersCount.get(name);\n                int count = (countInteger == null ? 0 : countInteger) - 1;\n                if (count <= 0) {\n                    mListenersCount.remove(name);\n                    bundle.putBoolean(KEY, !mListenersCount.containsKey(name));\n                } else {\n                    mListenersCount.put(name, count);\n                    countInteger = mListenersCount.get(name);\n                    bundle.putBoolean(KEY, count == (countInteger == null ? 0 : countInteger));\n                }\n            } break;\n            default:\n                throw new IllegalArgumentException(\"This is Unknown Uri：\" + uri);\n        }\n        return new BundleCursor(bundle);\n    }\n\n    @Override\n    public String getType(Uri uri) {\n        throw new UnsupportedOperationException(\"No external call\");\n    }\n\n    @Override\n    public Uri insert(Uri uri, ContentValues values) {\n        throw new UnsupportedOperationException(\"No external insert\");\n    }\n\n    @Override\n    public int delete(Uri uri, String selection, String[] selectionArgs) {\n        throw new UnsupportedOperationException(\"No external delete\");\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {\n        int result = 0;\n        String name = uri.getPathSegments().get(0);\n        int mode = Integer.parseInt(selectionArgs[0]);\n        SharedPreferences preferences = getContext().getSharedPreferences(name, mode);\n        int match = mUriMatcher.match(uri);\n        switch (match) {\n            case APPLY:\n            case COMMIT:\n                boolean hasListeners =\n                    mListenersCount != null && mListenersCount.get(name) != null && mListenersCount.get(name) > 0;\n                ArrayList<String> keysModified = null;\n                Map<String, Object> map = new HashMap();\n                if (hasListeners) {\n                    keysModified = new ArrayList<String>();\n                    map = (Map<String, Object>) preferences.getAll();\n                }\n                Editor editor = preferences.edit();\n                boolean clear = Boolean.parseBoolean(selectionArgs[1]);\n                if (clear) {\n                    if (hasListeners && map != null && !map.isEmpty()) {\n                        for (Map.Entry<String, Object> entry : map.entrySet()) {\n                            keysModified.add(entry.getKey());\n                        }\n                    }\n                    editor.clear();\n                }\n                for (Map.Entry<String, Object> entry : values.valueSet()) {\n                    String k = entry.getKey();\n                    Object v = entry.getValue();\n                    // Android 5.L_preview : \"this\" is the magic value for a removal mutation. In addition,\n                    // setting a value to \"null\" for a given key is specified to be\n                    // equivalent to calling remove on that key.\n                    if (v instanceof EditorImpl || v == null) {\n                        editor.remove(k);\n                        if (hasListeners && map != null && map.containsKey(k)) {\n                            keysModified.add(k);\n                        }\n                    } else {\n                        if (hasListeners && map != null\n                            && (!map.containsKey(k) || (map.containsKey(k) && !v.equals(map.get(k))))) {\n                            keysModified.add(k);\n                        }\n                    }\n\n                    if (v instanceof String) {\n                        editor.putString(k, (String) v);\n                    } else if (v instanceof Set) {\n                        ReflectionUtil.editorPutStringSet(editor, k,\n                                                          (Set<String>) v); // Android 3.0\n                    } else if (v instanceof Integer) {\n                        editor.putInt(k, (Integer) v);\n                    } else if (v instanceof Long) {\n                        editor.putLong(k, (Long) v);\n                    } else if (v instanceof Float) {\n                        editor.putFloat(k, (Float) v);\n                    } else if (v instanceof Boolean) {\n                        editor.putBoolean(k, (Boolean) v);\n                    }\n                }\n                if (hasListeners && keysModified.isEmpty()) {\n                    result = 1;\n                } else {\n                    switch (match) {\n                        case APPLY:\n                            ReflectionUtil.editorApply(editor); // Android 2.3\n                            result = 1;\n                            // Okay to notify the listeners before it's hit disk\n                            // because the listeners should always get the same\n                            // SharedPreferences instance back, which has the\n                            // changes reflected in memory.\n                            notifyListeners(name, keysModified);\n                            break;\n                        case COMMIT:\n                            if (editor.commit()) {\n                                result = 1;\n                                notifyListeners(name, keysModified);\n                            }\n                            break;\n                    }\n                }\n                values.clear();\n                break;\n            default:\n                throw new IllegalArgumentException(\"This is Unknown Uri：\" + uri);\n        }\n        return result;\n    }\n\n    @Override\n    public void onLowMemory() {\n        if (mListenersCount != null) {\n            mListenersCount.clear();\n        }\n        super.onLowMemory();\n    }\n\n    @Override\n    public void onTrimMemory(int level) {\n        if (mListenersCount != null) {\n            mListenersCount.clear();\n        }\n        super.onTrimMemory(level);\n    }\n\n    private void checkInitListenersCount() {\n        if (mListenersCount == null) {\n            mListenersCount = new HashMap<String, Integer>();\n        }\n    }\n\n    private void notifyListeners(String name, ArrayList<String> keysModified) {\n        if (keysModified != null && !keysModified.isEmpty()) {\n            Intent intent = new Intent();\n            intent.setAction(makeAction(name));\n            intent.setPackage(getContext().getPackageName());\n            intent.putExtra(KEY_NAME, name);\n            intent.putExtra(KEY, keysModified);\n            getContext().sendBroadcast(intent);\n        }\n    }\n\n    private static final class BundleCursor extends MatrixCursor {\n        private Bundle mBundle;\n\n        public BundleCursor(Bundle extras) {\n            super(new String[] {}, 0);\n            mBundle = extras;\n        }\n\n        @Override\n        public Bundle getExtras() {\n            return mBundle;\n        }\n\n        @Override\n        public Bundle respond(Bundle extras) {\n            mBundle = extras;\n            return mBundle;\n        }\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyApplication.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.app.Application;\nimport android.util.Log;\nimport com.getkeepsafe.relinker.ReLinker;\nimport com.tencent.mmkv.MMKV;\nimport com.tencent.mmkv.MMKVHandler;\nimport com.tencent.mmkv.MMKVLogLevel;\nimport com.tencent.mmkv.MMKVRecoverStrategic;\nimport com.tencent.mmkv.NameSpace;\n\npublic class MyApplication extends Application implements MMKVHandler {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        // NameSpace can access an MMKV instance regardless of MMKV.initialized() been called or not\n        testNameSpace();\n\n        // set root dir\n        //String rootDir = MMKV.initialize(this);\n        String dir = getFilesDir().getAbsolutePath() + \"/mmkv\";\n        String rootDir = MMKV.initialize(this, dir, new MMKV.LibLoader() {\n            @Override\n            public void loadLibrary(String libName) {\n                ReLinker.loadLibrary(MyApplication.this, libName);\n            }\n        }, MMKVLogLevel.LevelInfo, this);\n        Log.i(\"MMKV\", \"mmkv root: \" + rootDir);\n\n        // set log level\n        // MMKV.setLogLevel(MMKVLogLevel.LevelInfo);\n\n        // you can turn off logging\n        //MMKV.setLogLevel(MMKVLogLevel.LevelNone);\n\n        // register log redirecting & recover handler is moved into MMKV.initialize()\n        // MMKV.registerHandler(this);\n\n        // deprecated: content change notification\n        // MMKV.registerContentChangeNotify(this);\n    }\n\n    @Override\n    public void onTerminate() {\n        MMKV.onExit();\n\n        super.onTerminate();\n    }\n\n    @Override\n    public MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) {\n        return MMKVRecoverStrategic.OnErrorRecover;\n    }\n\n    @Override\n    public MMKVRecoverStrategic onMMKVFileLengthError(String mmapID) {\n        return MMKVRecoverStrategic.OnErrorRecover;\n    }\n\n    @Override\n    public boolean wantLogRedirecting() {\n        return true;\n    }\n\n    @Override\n    public void mmkvLog(MMKVLogLevel level, String file, int line, String func, String message) {\n        String log = \"<\" + file + \":\" + line + \"::\" + func + \"> \" + message;\n        switch (level) {\n            case LevelDebug:\n                Log.d(\"redirect logging MMKV\", log);\n                break;\n            case LevelNone:\n            case LevelInfo:\n                Log.i(\"redirect logging MMKV\", log);\n                break;\n            case LevelWarning:\n                Log.w(\"redirect logging MMKV\", log);\n                break;\n            case LevelError:\n                Log.e(\"redirect logging MMKV\", log);\n                break;\n        }\n    }\n\n    @Override\n    public native long getNativeLogHandler();\n\n    @Override\n    public void onContentChangedByOuterProcess(String mmapID) {\n        Log.i(\"MMKV\", \"other process has changed content of : \" + mmapID);\n    }\n\n    @Override\n    public boolean wantContentChangeNotification() {\n        return true;\n    }\n\n    @Override\n    public void onMMKVContentLoadSuccessfully(String mmapID) {\n        Log.i(\"MMKV\", \"content load successfully : \" + mmapID);\n    }\n\n    private void testNameSpace() {\n        final String nsRoot = getFilesDir().getAbsolutePath() + \"/namespace\";\n        final NameSpace ns = MMKV.nameSpace(nsRoot);\n        MMKV mmkv = ns.mmkvWithID(\"test/namespace/crypt\", MMKV.MULTI_PROCESS_MODE, \"crypt_key\", 4096 * 2);\n        MainActivity.testMMKV(mmkv, false);\n\n        System.loadLibrary(\"mmkvdemo\");\n\n        long nativeLogger = getNativeLogHandler();\n        Log.i(\"MMKVDemo\", \"native log handler: \" + nativeLogger);\n\n        testNameSpaceInNative(nsRoot, \"testNameSpaceInNative\");\n    }\n\n    private native void testNameSpaceInNative(String nameSpaceRoot, String mmapID);\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.content.Intent;\nimport android.util.Log;\nimport com.tencent.mmkv.MMKV;\n\npublic class MyService extends BenchMarkBaseService {\n    private static final String CALLER = \"MyService\";\n    public static final String CMD_REMOVE = \"cmd_remove\";\n\n    public static final String CMD_LOCK = \"cmd_lock\";\n    public static final String LOCK_PHASE_1 = \"lock_phase_1\";\n    public static final String LOCK_PHASE_2 = \"lock_phase_2\";\n    public static final String CMD_TRIM_FINISH = \"trim_finish\";\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.i(\"MMKV\", \"onCreate MyService\");\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        Log.i(\"MMKV\", \"onDestroy MyService\");\n    }\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        Log.i(\"MMKV\", \"MyService onStartCommand\");\n        if (intent != null) {\n            String cmd = intent.getStringExtra(CMD_ID);\n            if (cmd != null) {\n                if (cmd.equals(CMD_READ_INT)) {\n                    super.batchReadInt(CALLER);\n                } else if (cmd.equals(CMD_READ_STRING)) {\n                    super.batchReadString(CALLER);\n                } else if (cmd.equals(CMD_WRITE_INT)) {\n                    super.batchWriteInt(CALLER);\n                } else if (cmd.equals(CMD_WRITE_STRING)) {\n                    super.batchWriteString(CALLER);\n                } else if (cmd.equals(CMD_PREPARE_ASHMEM_BY_CP)) {\n                    super.prepareAshmemMMKVByCP();\n                } else if (cmd.equals(CMD_REMOVE)) {\n                    testRemove();\n                } else if (cmd.equals(CMD_LOCK)) {\n                    testLock();\n                } else if (cmd.equals(CMD_TRIM_FINISH)) {\n                    testTrimNonEmpty();\n                }\n            }\n        }\n        return super.onStartCommand(intent, flags, startId);\n    }\n\n    private void testRemove() {\n        MMKV mmkv = GetMMKV();\n        Log.d(\"mmkv in child\", \"\" + mmkv.decodeInt(CMD_ID));\n        mmkv.remove(CMD_ID);\n    }\n\n    private void testLock() {\n        // get the lock immediately\n        MMKV mmkv2 = MMKV.mmkvWithID(LOCK_PHASE_2, MMKV.MULTI_PROCESS_MODE);\n        mmkv2.lock();\n        Log.d(\"locked in child\", LOCK_PHASE_2);\n\n        Runnable waiter = new Runnable() {\n            @Override\n            public void run() {\n                MMKV mmkv1 = MMKV.mmkvWithID(LOCK_PHASE_1, MMKV.MULTI_PROCESS_MODE);\n                mmkv1.lock();\n                Log.d(\"locked in child\", LOCK_PHASE_1);\n            }\n        };\n        // wait infinitely\n        new Thread(waiter).start();\n    }\n\n    private void testTrimNonEmpty() {\n        MMKV mmkv = MMKV.mmkvWithID(\"trimNonEmptyInterProcess\", MMKV.MULTI_PROCESS_MODE);\n        byte[] value = new byte[64];\n        mmkv.encode(\"SomeKey\", value);\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/MyService_1.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\nimport com.tencent.mmkv.ParcelableMMKV;\n\npublic class MyService_1 extends BenchMarkBaseService implements ServiceConnection {\n    public static final String CMD_PREPARE_ASHMEM = \"cmd_prepare_ashmem\";\n    private static final String CALLER = \"MyService_1\";\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        Log.i(\"MMKV\", \"MyService_1 onStartCommand:\");\n        if (intent != null) {\n            String cmd = intent.getStringExtra(CMD_ID);\n            Log.i(\"MMKV\", \"----MyService_1 onStartCommand:\" + cmd);\n            if (cmd != null) {\n                if (cmd.equals(CMD_READ_INT)) {\n                    super.batchReadInt(CALLER);\n                } else if (cmd.equals(CMD_READ_STRING)) {\n                    super.batchReadString(CALLER);\n                } else if (cmd.equals(CMD_WRITE_INT)) {\n                    super.batchWriteInt(CALLER);\n                } else if (cmd.equals(CMD_WRITE_STRING)) {\n                    super.batchWriteString(CALLER);\n                } else if (cmd.equals(CMD_PREPARE_ASHMEM)) {\n                    Intent i = new Intent(\"com.tencent.mmkvdemo.MyService\").setPackage(\"com.tencent.mmkvdemo\");\n                    bindService(i, this, BIND_AUTO_CREATE);\n                } else if (cmd.equals(CMD_PREPARE_ASHMEM_BY_CP)) {\n                    super.prepareAshmemMMKVByCP();\n                }\n            }\n        }\n        return super.onStartCommand(intent, flags, startId);\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.i(\"MMKV\", \"onCreate MyService_1\");\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        Log.i(\"MMKV\", \"onDestroy MyService_1\");\n    }\n\n    @Override\n    public void onServiceConnected(ComponentName name, IBinder service) {\n        IAshmemMMKV ashmemMMKV = IAshmemMMKV.Stub.asInterface(service);\n        try {\n            ParcelableMMKV parcelableMMKV = ashmemMMKV.GetAshmemMMKV();\n            if (parcelableMMKV != null) {\n                m_ashmemMMKV = parcelableMMKV.toMMKV();\n                if (m_ashmemMMKV != null) {\n                    Log.i(\"MMKV\", \"ashmem bool: \" + m_ashmemMMKV.decodeBool(\"bool\"));\n                }\n            }\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void onServiceDisconnected(ComponentName name) {\n    }\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/SQLiteKV.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\npublic final class SQLiteKV {\n    private static class SQLiteKVDBHelper extends SQLiteOpenHelper {\n        private static final int DB_VERSION = 1;\n        private static final String DB_NAME = \"kv.db\";\n        public static final String TABLE_NAME_STR = \"kv_str\";\n        public static final String TABLE_NAME_INT = \"kv_int\";\n\n        public SQLiteKVDBHelper(Context context) {\n            super(context, DB_NAME, null, DB_VERSION);\n        }\n\n        @Override\n        public void onCreate(SQLiteDatabase sqLiteDatabase) {\n            String sql =\n                \"create table if not exists \" + TABLE_NAME_STR + \" (k text UNIQUE on conflict replace, v text)\";\n            sqLiteDatabase.execSQL(sql);\n            sql = \"create table if not exists \" + TABLE_NAME_INT + \" (k text UNIQUE on conflict replace, v integer)\";\n            sqLiteDatabase.execSQL(sql);\n        }\n\n        @Override\n        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {\n            String sql = \"DROP TABLE IF EXISTS \" + TABLE_NAME_STR;\n            sqLiteDatabase.execSQL(sql);\n            sql = \"DROP TABLE IF EXISTS \" + TABLE_NAME_INT;\n            sqLiteDatabase.execSQL(sql);\n            onCreate(sqLiteDatabase);\n        }\n    }\n    private SQLiteKVDBHelper m_dbHelper;\n    private SQLiteDatabase m_writetableDB;\n    private SQLiteDatabase m_readableDB;\n\n    public SQLiteKV(Context context) {\n        m_dbHelper = new SQLiteKVDBHelper(context);\n        m_dbHelper.setWriteAheadLoggingEnabled(true);\n    }\n\n    public void beginTransaction() {\n        getWritetableDB().beginTransaction();\n    }\n\n    public void endTransaction() {\n        if (m_writetableDB != null) {\n            try {\n                m_writetableDB.setTransactionSuccessful();\n            } finally {\n                m_writetableDB.endTransaction();\n            }\n        }\n    }\n\n    private SQLiteDatabase getWritetableDB() {\n        if (m_writetableDB == null) {\n            m_writetableDB = m_dbHelper.getWritableDatabase();\n        }\n        return m_writetableDB;\n    }\n\n    private SQLiteDatabase getReadableDatabase() {\n        if (m_readableDB == null) {\n            m_readableDB = m_dbHelper.getReadableDatabase();\n        }\n        return m_readableDB;\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        if (m_readableDB != null) {\n            m_readableDB.close();\n        }\n        if (m_writetableDB != null) {\n            m_writetableDB.close();\n        }\n        super.finalize();\n    }\n\n    public boolean putInt(String key, int value) {\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"k\", key);\n        contentValues.put(\"v\", value);\n        long rowID = getWritetableDB().insert(SQLiteKVDBHelper.TABLE_NAME_INT, null, contentValues);\n        return rowID != -1;\n    }\n\n    public int getInt(String key) {\n        int value = 0;\n        Cursor cursor = getReadableDatabase().rawQuery(\n            \"select v from \" + SQLiteKVDBHelper.TABLE_NAME_INT + \" where k=?\", new String[] {key});\n        if (cursor.moveToFirst()) {\n            value = cursor.getInt(cursor.getColumnIndexOrThrow(\"v\"));\n        }\n        cursor.close();\n        return value;\n    }\n\n    public boolean putString(String key, String value) {\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"k\", key);\n        contentValues.put(\"v\", value);\n        long rowID = getWritetableDB().insert(SQLiteKVDBHelper.TABLE_NAME_STR, null, contentValues);\n        return rowID != -1;\n    }\n\n    public String getString(String key) {\n        String value = null;\n        Cursor cursor = getReadableDatabase().rawQuery(\n            \"select v from \" + SQLiteKVDBHelper.TABLE_NAME_STR + \" where k=?\", new String[] {key});\n        if (cursor.moveToFirst()) {\n            value = cursor.getString(cursor.getColumnIndexOrThrow(\"v\"));\n        }\n        cursor.close();\n        return value;\n    }\n}"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/TestParcelable.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport java.util.LinkedList;\nimport java.util.List;\n\nclass TestParcelable implements Parcelable {\n\n    int iValue;\n    String sValue;\n    List<FakeInfo> list;\n    @Override\n    public void writeToParcel(Parcel parcel, int i) {\n        parcel.writeInt(iValue);\n        parcel.writeString(sValue);\n        // parcel.writeTypedList(list);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    TestParcelable(int i, String s) {\n        iValue = i;\n        sValue = s;\n        // list = new LinkedList<>();\n        // list.add(new Info(\"channel\", 1));\n        // list.add(new Info(\"oppo\", 2));\n    }\n\n    private TestParcelable(Parcel in) {\n        iValue = in.readInt();\n        sValue = in.readString();\n        list = in.createTypedArrayList(FakeInfo.CREATOR);\n    }\n\n    public static final Creator<TestParcelable> CREATOR = new Creator<TestParcelable>() {\n        @Override\n        public TestParcelable createFromParcel(Parcel parcel) {\n            return new TestParcelable(parcel);\n        }\n\n        @Override\n        public TestParcelable[] newArray(int i) {\n            return new TestParcelable[i];\n        }\n    };\n}\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/kotlin/KotlinUsecase.kt",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkvdemo\n\nimport com.tencent.mmkv.MMKV\nimport java.util.*\n\nfun kotlinFunctionalTest() {\n    val mmkv = MMKV.mmkvWithID(\"testKotlin\")\n    mmkv.encode(\"bool\", true)\n    println(\"bool = \" + mmkv.decodeBool(\"bool\"))\n\n    mmkv.encode(\"int\", Integer.MIN_VALUE)\n    println(\"int: \" + mmkv.decodeInt(\"int\"))\n\n    mmkv.encode(\"long\", java.lang.Long.MAX_VALUE)\n    println(\"long: \" + mmkv.decodeLong(\"long\"))\n\n    mmkv.encode(\"float\", -3.14f)\n    println(\"float: \" + mmkv.decodeFloat(\"float\"))\n\n    mmkv.encode(\"double\", java.lang.Double.MIN_VALUE)\n    println(\"double: \" + mmkv.decodeDouble(\"double\"))\n\n    mmkv.encode(\"string\", \"Hello from mmkv\")\n    println(\"string: \" + mmkv.decodeString(\"string\"))\n\n    val bytes = byteArrayOf('m'.code.toByte(), 'm'.code.toByte(), 'k'.code.toByte(), 'v'.code.toByte())\n    mmkv.encode(\"bytes\", bytes)\n    println(\"bytes: \" + mmkv.decodeBytes(\"bytes\")?.let { String(it) })\n\n    mmkv.encode(\"empty_bytes\", \"\".toByteArray());\n    println(\"empty_bytes: \" + mmkv.decodeBytes(\"empty_bytes\")?.let { String(it) })\n\n    mmkv.encode(\"stringSet\", HashSet<String>())\n    println(\"empty string set: \" + mmkv.decodeStringSet(\"stringSet\"))\n\n    println(\"allKeys: \" + Arrays.toString(mmkv.allKeys()))\n    println(\"count = \" + mmkv.count() + \", totalSize = \" + mmkv.totalSize())\n    println(\"containsKey[string]: \" + mmkv.containsKey(\"string\"))\n\n    mmkv.removeValueForKey(\"bool\")\n    println(\"bool: \" + mmkv.decodeBool(\"bool\"))\n    mmkv.removeValuesForKeys(arrayOf(\"int\", \"long\"))\n    //mmkv.clearAll();\n    mmkv.clearMemoryCache()\n    println(\"allKeys: \" + Arrays.toString(mmkv.allKeys()))\n    println(\"isFileValid[\" + mmkv.mmapID() + \"]: \" + MMKV.isFileValid(mmkv.mmapID()))\n}"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/main_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <Button\n        android:id=\"@+id/button_read_string\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Inter Read String\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHorizontal_bias=\"0.502\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/button_write_string\" />\n\n    <Button\n        android:id=\"@+id/button_write_string\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Inter Write String\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/button_read_int\" />\n\n    <Button\n        android:id=\"@+id/button_write_int\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Inter Write Int\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/button\"\n        tools:text=\"Inter Write Int\" />\n\n    <Button\n        android:id=\"@+id/button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"60dp\"\n        android:text=\"Baseline\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <Button\n        android:id=\"@+id/button_read_int\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Inter Read Int\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHorizontal_bias=\"0.501\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/button_write_int\"\n        tools:text=\"Inter Read Int\" />\n\n    <TextView\n        android:id=\"@+id/sample_text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:text=\"Hello World!\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_bias=\"0.967\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">MMKVDemo</string>\n</resources>\n"
  },
  {
    "path": "Android/MMKV/mmkvdemo/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "Android/MMKV/pmd-ruleset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n<ruleset name=\"PMD.rul\" xmlns=\"http://pmd.sourceforge.net/ruleset/2.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd\">\n\n    <description>This ruleset was created from PMD.rul</description>\n\n    <rule ref=\"rulesets/java/basic.xml\">\n        <exclude name=\"AvoidBranchingStatementAsLastInLoop\"/>\n    </rule>\n    <rule ref=\"rulesets/java/braces.xml\"/>\n    <rule ref=\"rulesets/java/strings.xml\">\n        <!-- TODO: This warns about annotations, apparently fixed in a later version. -->\n        <exclude name=\"AvoidDuplicateLiterals\"/>\n    </rule>\n    <rule ref=\"rulesets/java/unusedcode.xml\"/>\n\n    <rule ref=\"rulesets/java/design.xml\">\n        <exclude name=\"ConfusingTernary\"/>\n        <exclude name=\"EmptyMethodInAbstractClassShouldBeAbstract\"/>\n        <exclude name=\"AvoidSynchronizedAtMethodLevel\"/>\n\n        <!-- This check breaks on double checked locking which is safe in Java 6/7 -->\n        <exclude name=\"NonThreadSafeSingleton\"/>\n        <!-- This check breaks the builder pattern, I didn't find the solution-->\n        <exclude name=\"AccessorClassGeneration\"/>\n        <!-- TODO: Fix these -->\n        <exclude name=\"AvoidReassigningParameters\"/>\n        <exclude name=\"GodClass\"/>\n\n    </rule>\n\n    <rule ref=\"rulesets/java/design.xml/AvoidDeeplyNestedIfStmts\">\n        <properties>\n            <property name=\"problemDepth\" value=\"5\"/>\n        </properties>\n    </rule>\n\n    <rule message=\"Commented blocks are ok\" ref=\"rulesets/java/empty.xml/EmptyCatchBlock\">\n        <properties>\n            <property name=\"allowCommentedBlocks\" value=\"true\"/>\n        </properties>\n    </rule>\n</ruleset>"
  },
  {
    "path": "Android/MMKV/proguard-rules/proguard-rules-android-lib.pro",
    "content": "# OSS_ANDROID_TEMPLATE_FILE_HEADER\n#\n# Proguard config for publish an private aar module\n# NOTE: It's different from libraries' consumer proguard config\n#\n-renamesourcefileattribute SourceFile\n-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod\n\n# Preserve all annotations.\n\n-keepattributes *Annotation*\n\n# Preserve all public classes, and their public and protected fields and\n# methods.\n\n-keep public class * {\n    public protected *;\n}\n\n# Preserve all .class method names.\n\n-keepclassmembernames class * {\n    java.lang.Class class$(java.lang.String);\n    java.lang.Class class$(java.lang.String, boolean);\n}\n\n# Preserve all native method names and the names of their classes.\n\n-keepclasseswithmembernames class * {\n    native <methods>;\n}\n\n# Preserve the special static methods that are required in all enumeration\n# classes.\n\n-keepclassmembers class * extends java.lang.Enum {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n# Explicitly preserve all serialization members. The Serializable interface\n# is only a marker interface, so it wouldn't save them.\n# You can comment this out if your library doesn't use serialization.\n# If your code contains serializable classes that have to be backward\n# compatible, please refer to the manual.\n\n-keepclassmembers class * implements java.io.Serializable {\n    static final long serialVersionUID;\n    static final java.io.ObjectStreamField[] serialPersistentFields;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n}"
  },
  {
    "path": "Android/MMKV/proguard-rules/proguard-rules-test.pro",
    "content": "# OSS_ANDROID_TEMPLATE_FILE_HEADER\n-dontnote junit.**\n-dontnote org.xmlpull.v1.**\n-dontwarn junit.**\n-dontwarn org.junit.**\n-dontwarn android.content.**"
  },
  {
    "path": "Android/MMKV/settings.gradle",
    "content": "rootProject.name = \"MMKV-Android\"\n\ninclude ':mmkv', ':mmkvdemo', 'mmkvannotation'\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# MMKV Change Log\n## v2.4.0 / 2026-03-18\n\nThis release introduces a **unified `MMKVHandler` callback interface** across all platforms, replacing scattered per-callback registration with a single, OO-style handler. It also adds the `MMKVConfig` all-in-one configuration and several bug fixes.\n\n### Changes for All Platforms\n* **Feature:** Refactored the callback system into a **unified `MMKVHandler`** interface. All callbacks (log redirecting, error handling, content change notification, content loaded notification) are now grouped in a single handler registered via `registerHandler()`. The old per-callback `registerLogHandler()` / `registerErrorHandler()` / `registerContentChangeHandler()` / `registerContentLoadedHandler()` APIs have been removed.\n* **Feature:** Added `onMMKVContentLoadSuccessfully` callback, triggered when an MMKV file is loaded/mapped successfully.\n* **Feature:** Added `MMKVConfig` for all-in-one instance configuration, supporting all options (`mode`, `cryptKey`, `aes256`, `expectedCapacity`, `enableKeyExpire`, `expiredInSeconds`, `enableCompareBeforeSet`, `recover`, `itemSizeLimit`) in a single struct/class.\n* **Feature:** Added `defaultMMKV(config)` variant for creating the default instance with full configuration.\n* **Fix:** Robust check on encryption mode ([#1642](https://github.com/nicksunday/mmkv/issues/1642)).\n* **Fix:** Protect from `delete` file failure on corrupted files.\n* **Fix:** Protect from `m_file` not valid for `isDiskOfMMAPFileCorrupted()`.\n* **Fix:** Reduce `absolutePath()` calls as much as possible.\n* Drop old-style actual size downgrade support.\n* MMKV must be compiled with **C++17**.\n\n### Android\n* **Change:** Merged `MMKVContentChangeNotification` into `MMKVHandler`. The old `MMKVContentChangeNotification` interface is now `@Deprecated`.\n* **Fix:** Fix `fcntl()` OFD lock failure on ashmem ([#1637](https://github.com/nicksunday/mmkv/issues/1637)).\n* Merge ashmem size with `expectedCapacity`.\n* Correctly collect so-symbols.\n\n### iOS/macOS\n* **Feature:** Added `getBytes()` with `string_view` key for ObjC++.\n* **Fix:** Fixed a memory leak on getting `NameSpace` instance.\n* Support **Swift Package Manager** ([#535](https://github.com/nicksunday/mmkv/issues/535)).\n* Fix compile error on `tryAtomicRename()`.\n\n### HarmonyOS NEXT\n* Merge ashmem size with `expectedCapacity`.\n\n### Flutter\n* Updated to use the unified `MMKVHandler` callback interface.\n\n### Win32\n* Fix: Convert log message to UTF-8 for Win32 client.\n* Verify all pages for corrupted file detection.\n\n## v2.3.0 / 2025-12-03 (Breaking Change)\n\nThis release is a **breaking change** and introduces **AES-256 encryption** for enhanced security.\n\n### All Platforms\n\n* **Feature:** Added **AES-256 encryption** functionality. To upgrade an existing encrypted MMKV instance to AES-256, first load it using the old key. Then, call the `reKey()` method with the new key and set the `aes256` parameter to `true`, e.g., `reKey(newKey, aes256=true)`. After this, you should use the new key for all future loads of this instance.\n* **Fix:** Resolved a crash that occurred when loading an empty file in `ReadOnly` mode.\n* **Fix:** Added protection against the `weakly_canonical()` exception caused by an invalid file path.\n* **Fix:** Fixed an issue where the file size could change during multi-process loading.\n* **Fix:** Corrected a bug where a single key could be overridden incorrectly when upgrading from a v1.1.x version.\n\n### iOS/macOS/watchOS\n\n* **Change:** Dropped support for the **armv7k** architecture on Apple Watch. To adapt, you should add `armv7k` to the **\"Excluded Architectures\"** setting for your Apple Watch App or Extension target.\n* **Important:** Do not upgrade to this version if you need to maintain `armv7k` support.\n* **Change:** Dropped deprecated methods that use the `relativePath:` parameter. If you were using these methods, please migrate to those using the `rootPath:` parameter instead.\n\n### POSIX\n\n* **Change:** Dropped support for **armv7** architecture. **Do not** upgrade to this version if you need to maintain `armv7` support.\n\n## v2.2.4 / 2025-09-25\nThis is a hotfix release mainly for iOS/macOS CocoaPods users.\n\n### Changes for All platforms\n* Improve the performance of MMBuffer a little bit in some cases.\n\n### iOS/macOS\n* Make MMKV and MMKVCore podspec define modules.\n\n## v2.2.3 / 2025-08-20\nThis is a feature release that brings **full desktop support to Flutter**. It also includes key bug fixes and enhancements for Android and other platforms.\n\n### Flutter\n* **Added Full Desktop Support**: MMKV for Flutter now officially supports **Windows**, **macOS**, and **Linux**.\n* **Example App**: The example application has been updated to run on all new desktop platforms.\n\n### Android\n* **Feature**: Added `MMKVHandler.getNativeLogHandler()` to allow native handling of MMKV logs from C++ code, improving performance.\n* **Fix**: Corrected an integer overflow bug where a native memory address exceeding `Long.MAX_VALUE` was misinterpreted as a negative number.\n* **Fix**: Prevented potential memory overflow by ensuring JNI local references are deleted after use. \n* **Fix**: Fixed an issue where directories for special relative paths were not being created correctly.\n* **Enhancement**: Improved the thread-safety of the callback handler to prevent crashes.\n\n### iOS/macOS\n\n* **Fix**: Resolved a build error on Apple platforms that occurred when the `MMKV_APPLE` flag was not set.\n* **Fix**: Addressed a naming conflict in the Swift implementation.\n\n### Windows\n\n* **Fix**: Corrected character encoding issues in the Win32 demo by converting files to UTF-8.\n\n## v2.2.2 / 2025-05-08\nThis is a hot fix version mainly **for Android/Linux platforms**. It’s highly recommended for v2.2.0~v2.2.1 users.\n### Changes for All platforms\n* Improve file lock consistency for Mayfly FD MMKV instances.\n\n### Android\n* Fix a potential ANR on multi-process mode MMKV init/creation.\n\n### POSIX\n* Fix a potential ANR on multi-process mode MMKV init/creation on Linux.\n\n### iOS/macOS\n* Retain the callback handler to prevent a potential short-lived callback handler from crashing.\n\n### Win32\n* Improve efficiency on MMKV instance init/creation.\n\n## v2.2.1 / 2025-04-25\n### Changes for All platforms\n* Add `importFrom()`.\n\n### Android\n* Fix a bug on initialization.\n\n## v2.2.0 / 2025-04-24\nWe introduce the **Mayfly FD** (short-lived file descriptor) enhancement, reducing MMKV's fd footprint by half and more. For a single-process mode MMKV instance, the fd footprint is reduced to zero (except Android/OHOS, details below). For a multi-process mode MMKV instance, the fd footprint is reduced by half, for we still need a long-lived fd to inter-process lock the shared memory.\n### Changes for All platforms\n* Add **Mayfly FD** (short-lived file descriptor) enhancement.\n* Improve multi-process access efficiency by about 20%.\n* Add `checkExist()` to check if a MMKV instance exists on disk.\n* Drop deprecated armv7 AES hardware acceleration.\n\n### Android\n* Reduce the fd footprint by half. For a single-process mode MMKV instance, we still need a long-lived fd to support the **process-mode checker** and **legacy name upgrading**. In the far future, when all legacy names have been upgraded, we might drop the long-lived fd like other platforms.\n* Upgrade Android compileSdk/targetSdk to 35, NDK to 28.1, Java to 11, Gradle to 8.11.1, and AGP to 8.9.2.\n\n### HarmonyOS NEXT\n* Reduce the fd footprint by half. For a single-process mode MMKV instance, we still need a long-lived fd to support the **legacy name upgrading**. In the far future, when all legacy names have been upgraded, we might drop the long-lived fd like other platforms.\n* Drop `checkProcessMode()`, it’s never been used.\n* Improve obfuscation configuration with relative path.\n\n### iOS\n* Add `+mmkvGroupPath` to get the group folder for MMKV multi-process storage.\n\n### Flutter\n* Add `isFileValid()`.\n* Add `groupPath()` on iOS.\n* Fix `checkContentChangedByOuterProcess()` not working bug.\n\n## v2.1.0 / 2025-02-18\nHappy Chinese New Year!  \nThis is a **breaking change version for the Android/OHOS** platform. Read the change log bellow and upgrade carefully.\n### Changes for All platforms\n* Add the `NameSpace` feature that easily supports customizing a root directory.\n* Add protection from bad disk records of MMKV files.\n* Fix FileLock not being unlocked on destruction.\n* Improve directory creation on `ReadOnly` mode.\n\n### Android\n* **Breaking change**: Migrate legacy MMKV in a custom directory to normal MMKV. Historically Android/OHOS mistakenly use mmapKey as mmapID, which will be problematic with the `NameSpace` feature. Starting from v2.1.0, MMKV will try to migrate them back to normal when possible.  \nIt's highly recommended that you **upgrade to v2.0.2/v1.3.11 first** with **forward support** of normal MMKV in a custom directory.\n* Supports using MMKV directly in C++ code.\n* Improve inter-process locking by using `F_OFD_SETLK` instead of `F_SETLK`.\n* Add *experimental* protection from bad disk records of MMKV files.\n\n### HarmonyOS NEXT\n* **Breaking change**: Migrate legacy MMKV in a custom directory to normal MMKV. Historically Android/OHOS mistakenly use mmapKey as mmapID, which will be problematic with the `NameSpace` feature. Starting from v2.1.0, MMKV will try to migrate them back to normal when possible.  \nIt's highly recommended that you **upgrade to v2.0.2/v1.3.11 first** with **forward support** of normal MMKV in a custom directory.\n* Supports using MMKV directly in C++ code.\n* Improve inter-process locking by using `F_OFD_SETLK` instead of `F_SETLK`.\n* Add *experimental* protection from bad disk records of MMKV files.\n\n### iOS/macOS\n* Upgrade to iOS 13, macOS 10.15, and watchOS 6 to support the `NameSpace` functionality.\n* Supports using MMKV directly in C++ code.\n* Drop the background `mlock()` protection given that we are iOS 13+.\n* Add *tested* protection from bad disk records of MMKV files.\n* Fix a package error when using MMKV by submodule.\n\n### Flutter\n* Remove unused imports and fix deprecated implementations. \n* Support flutter 3.29.\n\n### Win32\n* Add *tested* protection from bad disk records of MMKV files.\n\n### POSIX\n* Improve inter-process locking by using `F_OFD_SETLK` instead of `F_SETLK`.\n* Add *experimental* protection from bad disk records of MMKV files.\n* Fix a compile error on the GNU toolchain.\n\n### Golang\n* Fix a link error in the armv8 arch.\n* Improve multi-platform building.\n\n## v2.0.2 / 2024-12-27\nMary holiday and a happy new year!\n### Changes for All platforms\n* Fix a bug that MMKV might fail to backup/restore across different filesystems.\n* Add protection from invalid value size of auto-key-expire mmkv.\n\n### Android\n* If the running App is 32-bit only, warn about it (by throwing `UnsupportedArchitectureException`) before trying to load native lib.\n* Add forward support for the correct filename with a custom root path.\n\n### HarmonyOS NEXT\n* Obfuscation fully supported.\n* Use atomic file rename on OHOS.\n* Add forward support for the correct filename with a custom root path.\n\n### Win32\n* Only `mmap()` on `ftruncate()`/`zeroFillFile()` failure iff we have a valid file mapping before.\n\n## v2.0.1 / 2024-11-12\n**This is a hotfix release.**\n### Changes for All platforms\n* Fix a bug that might cause MMKV to become dead-locked for other threads after decoding container-type values. The affected platforms and value types are listed below. So don't be surprised if you find no update on the unaffected platforms.\n\n### HarmonyOS NEXT\n* Fix a bug that MMKV might become dead-locked for other threads after `decodeStringSet()` / `decodeNumberSet` / `decodeBoolSet` or decoding `TypedArray`.\n\n### Flutter\n* Fix the bug on HarmonyOS NEXT listed above. A version named v2.0.1 was added to fix the Android version conflict between the LTS series & v2.0. Thanks to the federated plugins framework, only the underlying `mmkv_ohos` plugin is upgraded, the `mmkv` plugin stays the same.\n\n### POSIX\n* Fix a bug that MMKV might become dead-locked for other threads after decoding `std::vector<T>` or `std::span<T>` values.\n\n## v2.0.0 / 2024-10-21\n**This release is a breaking change release, especially for Android.**\n### Changes for All platforms\n* Add **readonly mode** support.\n* Fix a compile error when `MMKV_DISABLE_CRYPT` is defined and MMKV is built by source in DEBUG.\n\n### Android\n* Support 16K page size for Android 15.\n* Drop the support of **32-bit ABI**.\n* Bump the mini **SDK level to API 23**.\n\n### iOS / macOS\n* Add Mac Catalyst support\n\n### Flutter\n* Add add log/error/content-change callback.\n\n### HarmonyOS NEXT\n* Add add log/error/content-change callback.\n* Support obfuscation. For the time being, you will have to manually copy the content of MMKV's [consumer-rules.txt](https://github.com/Tencent/MMKV/blob/master/OpenHarmony/MMKV/consumer-rules.txt) into your App's obfuscation-rules.txt.\n\n### Win32\n* Replace `random()` with `rand()` to fix a compile error.\n\n## v1.3.9 / 2024-07-26\n**This is a Long Term Support (LTS) release.**\n### Changes for All platforms\n* Fix a data corruption bug on an encrypted MMKV with only one key value stored.\n* Make encryption more resilient from brute force cracking.\n* Fix a bug that `pthread_mutex` is not being destroyed correctly.\n\n### Android\n* Use an alternative way to get the process name to avoid potential App review issues.\n* Upgrade to NDK 26.3.11579264.\n\n### Flutter\n* Add support for HarmonyOS NEXT. In fact, a temp version named v1.3.8 adds this support with the native lib of v1.3.7. To avoid potential confusion, bump both versions to v1.3.9.\n\n## v1.3.7 / 2024-07-08\n### Android & Flutter\n**This Long Term Support (LTS) release** primarily reintroduces support for the ARMv7 architecture and lowers the minimum SDK version requirement to 21. Please note that only critical bug fixes will be applied to the 1.3.x series. New features will be introduced in version 2.0 and later, which will discontinue support for 32-bit architectures and raise the minimum SDK version requirement to 23.\n\n### For other platforms\nThis is exactly the same as v1.3.6, so don't be surprised if you don't see this version in CocoaPods or OHPM.\n\n## v1.3.6 / 2024-07-05\n### Changes for All platforms\n* The Core library now upgrades the C++ standard from C++17 to C++20 to make the most of morden C++ feature such as `Concept` & unordered containers with `std::string_view` as key. From now on, if you build MMKV by source (iOS, Windows, POSIX, etc), you will need a C++ compiler that supports the C++20 standard. If you only use MMKV in binary (Android, OHOS, etc), this upgrade of the C++ compiler is not required.\n* The key type changes from `std::string` to `std::string_view`, to avoid unnecessary string construction and destruction when the key is a raw string.\n\n### Android\n* Use the latest ashmem API if possible.\n* Use the latest API to get the device API level.\n\n### Flutter\n* MMKV will try to load libmmkv.so before Dart code, to reduce the error of loading library in Android.\n\n### HarmonyOS NEXT\n* Fix a bug that a `String` value might get truncated on encoding.\n* MMKV returns `undefined` when a key does not exist, previously a default value of the type (`false` for `boolean`, `0` for `number`, etc) is returned.\n* Add the feature to encode/decode a `float` value.\n* Add the feature to encode/decode a `TypedArray` value.\n* Support encoding a part of an `ArrayBuffer`.\n\n### iOS/macOS\n* Hide the default `NSObject.initialize()` from Swift/ObjC to prevent potential misuse.\n\n### POSIX\n* Support encode/decode `std::vector<T>` or `std::span<T>` values, with T as primitive types.\n* Fix a compile error on Linux env.\n* Fix a compile error on the GNU compiler.\n* Fix a compile error with some old version of zlib (on CentOS).\n\n### Python\n* Python now runs on Windows. Check out the latest [wiki](https://github.com/Tencent/MMKV/wiki/python_setup) for instructions.\n\n### Windows\n* Support encode/decode `std::vector<T>` or `std::span<T>` values, with T as primitive types.\n* Python now runs on Windows. Check out the latest [wiki](https://github.com/Tencent/MMKV/wiki/python_setup) for instructions.\n\n## v1.3.5 / 2024-04-24\n### HarmonyOS NEXT\n* This is the first official support of HarmonyOS NEXT.\n* Most things actually work!\n* Checkout the [wiki](https://github.com/Tencent/MMKV/wiki/ohos_setup) for more.\n\n### Flutter\n* Migrate to federated plugins to avoid the iOS rename headache. From now on, no more renaming from `mmkv` to `mmkvflutter` is needed.\n* Bump iOS Deployment Target to iOS 12.\n* Bump Android minSdkVersion to 23.\n\n### iOS & macOS\n* Avoid using so-called privacy APIs (`lstat()`, `fstat()`, `NSUserDefaults`).\n* Bump iOS Deployment Target to iOS 12.\n\n### Android\n* Bump minSdkVersion to 23.\n* Drop armv7 & x86 support.\n\n### POSIX\n* Use the embedded libz when libz can not be found.\n* Fix compile error when building with gcc.\n\n### Windows\n* Support x64 architecture.\n\n## v1.3.4 / 2024-03-15\n### Changes for All platforms\n* Make `trim()` more robust in multi-process mode.\n\n### iOS & macOS\n* Support visionOS.\n\n### POSIX\n* Fix a compile error on `::unlink()`.\n\n## v1.3.3 / 2024-01-25\n### Changes for All platforms\n* Add `removeStorage()` static method to safely delete underlying files of an MMKV instance.\n* Add protection from a potential crash of a multi-process MMKV loading due to the MMKV file not being valid.\n* Add back the lazy load feature. It was first introduced in v1.2.16. But it was rollbacked in v1.3.0 of potential ANR & file corruption. Now it's clear that the bug was caused by something else, it's time to bring it back.\n* **Optimize loading speed** by using shared inter-process lock unless there's a need to truncate the file size, which is rare.\n* Make these two lately added features **more robust**: customizing the initial file size & optimizing write speed when there's only one key inside MMKV.\n\n### Android\n* Fix a bug that `null` is returned when the value is in fact an empty ByteArray.\n* Fix AGP >= 8 package namespace error.\n* Fix the `FastNative` naming conflict.\n* Upgrade to SDK 34.\n* Upgrade `androidx.annotation`  to v1.7.1.\n\n### iOS & macOS\n* On the Xcode 15 build, an App will crash on iOS 14 and below. Previously we have recommended some workarounds (check the v1.3.2 release note for details). Now you can use Xcode 15.1 to fix this issue.\n* Fix a bug that the multi-process mode won't configure correctly. It was introduced in v1.3.2.\n* Fix a macro naming conflict.\n* Avoid using a so-called privacy API when creating temp files.\n\n### POSIX\n* Fix a compile error on `memcpy()`.\n\n### Golang\n* Fix a compile error when `MMKV_DISABLE_CRYPT` is on.\n\n## v1.3.2 / 2023-11-20\nAmong most of the features added in this version, the credit goes to @kaitian521.\n### Changes for All platforms\n* Add the feature of customizing the **initial file size** of an MMKV instance.\n* **Optimize write speed** when there's only one key inside MMKV, the new key is the same as the old one, and MMKV is in `SINGLE_PROCESS_MODE`.\n* **Optimize write speed** by overriding from the beginning of the file instead of append in the back, when there's zero key inside MMKV, and MMKV is in `SINGLE_PROCESS_MODE`.\n* Add the feature of `clearAll()` with keeping file disk space unchanged, **reducing the need to expand file size** on later insert & update operations. This feature is off by default, you will have to call it with relative params or newly added methods. \n* Add the feature of **comparing values before setting/encoding** on the same key.\n* Fix a potential bug that the MMKV file will be invalid state after a successful expansion but a failure `zeroFill()`, will lead to a crash.\n* Fix a potential crash due to other module/static lib turn-off **RTTI**, which will cause MMKV to fail to catch `std::exception`.\n* Fix several potential crash due to the MMKV file not being valid.\n\n### Android\n* Use the `-O2` optimization level by default, which will **reduce native lib size** and improve read/write speed a little bit.\n* Experimantal use `@fastNative` annotation on `enableCompareBeforeCompare()` to speed up JNI call.\n\n### iOS & macOS\n* Optimize auto-clean logic to **reduce lock waiting time**.\n* Turn-off mlock() protection in background on iOS 13+. We have **verified it on WeChat** that the protection is no longer needed from at least iOS 13. Maybe iOS 12 or older is also not needed, but we don't have the chance to verify that because WeChat no longer supports iOS 12.\n\n#### Known Issue\n* On Xcode 15 build, App will crash on iOS 14 and below. The bug is introduced by Apple's new linker. The official solutions provided by Apple are either:\n  * Drop the support of iOS 14.\n  * Add `-Wl,-weak_reference_mismatches,weak` or `-Wl,-ld_classic` options to the `OTHER_LDFLAGS` build setting of Xcode 15. Note that these options are **not recognized** by older versions of Xcode.\n  * Use older versions of Xcode, or **wait for Xcode 15.2**.\n\n## v1.3.1 / 2023-8-11\nThis is a hotfix version. It's **highly recommended** that v1.2.16 & v1.3.0 users upgrade as soon as possible.\n\n### Changes for All platforms\n* Fix a critical bug that might cause multi-process MMKV corrupt. This bug was introduced in v1.2.16.\n* Add the ability to filter expired keys on `count()` & `allKeys()` methods when auto key expiration is turn on.\n* Reduce the `msync()` call on newly created MMKV instances.\n\n### iOS & macOS\n* Fix a bug that NSKeyedArchive object might fail to decode when auto key expiration is turn on.\n\n## v1.3.0 / 2023-06-14\n### Changes for All platforms\n* Add auto key expiration feature. Note that this is a breaking change, once upgrade to auto expiration, the MMKV file is not valid for older versions of MMKV (v1.2.16 and below) to correctly operate.\n* Roll back the lazy load optimization due to reported ANR issues. It was introduced in v1.2.16.\n\n### iOS & macOS\n* Fix a potential memory leak on setting a new value for an existing key.\n* Upgrade min support target to iOS 11 / macOS 10.13 / tvOS 13 / watchOS 4.\n\n### Windows\n* Fix a bug that might fail to truncate the file size to a smaller size in some cases.\n\n### Flutter\n* The version of MMKV for Flutter is now the same as the MMKV native library.\n* Starting from v1.3.0, Flutter for Android will use `com.tencent:mmkv`. Previously it's `com.tencent:mmkv-static`. It's the same as `com.tencent:mmkv` starting from v1.2.11.\n\n## v1.2.16 / 2023-04-20\n### Changes for All platforms\n* Optimization: The actual file content is lazy loaded now, saving time on MMKV instance creation, and avoiding lock waiting when a lot of instances are created at the same time.\n* Fix a bug when restoring a loaded MMKV instance the meta file might mistakenly report corrupted.\n\n### Android\n* Optimization: Remove unnecessary binder call on main process instantiation.\n\n### Flutter\n* Fix a crash on decoding an empty list.\n* Remove deprecated dependence.\n* Make the script more robust to fix the iOS Flutter plugin name.\n\n### Windows\n* Fix a string format bug on the MinGW64 environment.\n\n### golang\n* Fix a build error on 32-bit OS.\n\n## v1.2.15 / 2023-01-12\n### Changes for All platforms\n* Log handler now handles all logs from the very beginning, especially the logs in initialization.\n* Log handler register method is now deprecated. It's integrated with `initialize()`.\n* Fix a bug that `lock()`/`unlock()`/`try_lock()` is not thread-safe.\n\n### Flutter\n* Reduce the privacy info needed to obtain android `sdkInt`, avoid unnecessary risk on Android App Review.\n\n### iOS & macOS\n* Fix a compile error on macOS.\n* Fix a bug that some ObjC exceptions are not being caught.\n* Add assert on nil MMKV base path, protect from mis-using MMKV in global variable initialization.\n* Starting from v1.2.15, one must call `+[MMKV initializeMMKV:]` manually before calling any MMKV methods.\n\n### golang\n* Fix a compile error on GCC.\n\n### Windows\n* Support CMake project on Windows.\n\n## v1.2.14 / 2022-08-10\n### Changes for All platforms\n* Fix a bug that `MMKV.getXXX()` may return invalid results in multi-process mode.\n\n### Android\n* Return `[]` instead of `null` on empty `StringSet` from `MMKV.decodeStringSet()` methods.\n* Upgrade Android Compile & Target SDK to `32`.\n\n### iOS\n* Protect from the crash in `-[MMKV getObject:forKey:]` method when the key-value doesn't exist.\n\n## v1.2.13 / 2022-03-30\n\n### Android\n* Fix crash on using Ashmem while `MMKV_DISABLE_CRYPT` macro is defined.\n\n### iOS\n* Add ability to retrieve key existece while getting value, aka `-[MMKV getXXX:forKey:hasValue:]` methods.\n\n### POSIX\n* Add ability to retrieve key existece while getting value, aka `MMKV::getXXX(key, defaultValue, hasValue)` methods.\n\n### Windows\n* Add ability to retrieve key existece while getting value, aka `MMKV::getXXX(key, defaultValue, hasValue)` methods.\n\n## v1.2.12 / 2022-01-17\n### Changes for All platforms\n* Fix a bug that a subsequential `clearAll()` call may fail to take effect in multi-process mode.\n* Hide some OpenSSL symbols to prevent link-time symbol conflict, when an App somehow also static linking OpenSSL.\n\n### Android\n* Upgrade `compileSdkVersion` & `targetSdkVersion` from `30` to `31`.\n\n## v1.2.11 / 2021-10-26\n\n### Android\n* Due to increasing report about crash inside STL, we have decided to make MMKV **static linking** `libc++` **by default**. Starting from v1.2.11, `com.tencent:mmkv-static` is the same as `com.tencent:mmkv`.\n* For those still in need of MMKV with shared linking of `libc++_shared`, you could use `com.tencent:mmkv-shared` instead.\n* Add backup & restore ability.\n\n### iOS / macOS\n* Add backup & restore ability.\n* Support tvOS.\n* Fix a compile error on some old Xcode.\n\n### Flutter (v1.2.12)\n* Add backup & restore ability.\n\n### POSIX / golang / Python\n* Add backup & restore ability.\n* Fix a compile error on Gentoo.\n\n### Windows\n* Add backup & restore ability.\n\n## v1.2.10 / 2021-06-25\nThis version is mainly for Android & Flutter.  \n\n### Android\n* Complete **JavaDoc documentation** for all public methods, classes, and interfaces. From now on, you can find the [API reference online](https://javadoc.io/doc/com.tencent/mmkv).\n* Drop the support of **armeabi** arch. Due to some local build cache mistake, the last version (v1.2.9) of MMKV still has an unstripped armeabi arch inside. This is fixed.\n* Change `MMKV.mmkvWithID()` from returning `null` to throwing exceptions on any error.\n* Add `MMKV.actualSize()` to get the actual used size of the file.\n* Mark `MMKV.commit()` & `MMKV.apply()` as deprecated, to avoid some misuse after migration from SharedPreferences to MMKV.\n\n### Flutter (v1.2.11)\n* Bug Fixed: When building on iOS, occasionally it will fail on symbol conflict with other libs. We have renamed all public native methods to avoid potential conflict.\n* Keep up with MMKV native lib v1.2.10.\n\n## v1.2.9 / 2021-05-26\nThis version is mainly for Android & Flutter.  \n\n### Android\n* Drop the support of **armeabi** arch. As has been mention in the last release, to avoid some crashes on the old NDK (r16b), and make the most of a more stable `libc++`, we have decided to upgrade MMKV's building NDK in this release. That means we can't support **armeabi** anymore. Those who still in need of armeabi can **build from sources** by following the [instruction in the wiki](https://github.com/Tencent/MMKV/wiki/android_setup).\n\nWe really appreciate your understanding.\n\n### Flutter (v1.2.10)\n* Bug Fixed: When calling `MMKV.encodeString()` with an empty string value on Android, `MMKV.decodeString()` will return `null`.\n* Bug Fixed: After **upgrading** from Flutter 1.20+ to 2.0+, calling `MMKV.defaultMMKV()` on Android might fail to load, you can try calling `MMKV.defaultMMKV(cryptKey: '\\u{2}U')` with an **encrytion key** '\\u{2}U' instead.\n* Keep up with MMKV native lib v1.2.9.\n\n## v1.2.8 / 2021-05-06\nThis will be the last version that supports **armeabi arch** on Android. To avoid some crashes on the old NDK (r16b), and make the most of a more stable `libc++`, we have decided to upgrade MMKV's building NDK in the next release. That means we can't support **armeabi** anymore.  \n\nWe really appreciate your understanding.\n\n### Android\n* Migrate MMKV to Maven Central Repository. For versions older than v1.2.7 (including), they are still available on JCenter.\n* Add `MMKV.disableProcessModeChecker()`. There are some native crash reports due to the process mode checker. You can disable it manually.\n* For the same reason described above (native crashed), MMKV will now turn off the process mode checker on a non-debuggable app (aka, a release build).\n* For MMKV to detect whether the app is debuggable or not, when calling `MMKV.initialize()` to customize the root directory, a `context` parameter is required now.\n\n### iOS / macOS\n* Min iOS support has been **upgrade to iOS 9**.\n* Support building by Xcode 12.\n\n### Flutter (v1.2.9)\n* Support null-safety.\n* Upgrade to flutter 2.0.\n* Fix a crash on the iOS when calling `encodeString()` with an empty string value.\n\n**Known Issue on Flutter**  \n\n* When calling `encodeString()` with an empty string value on Android, `decodeString()` will return `null`. This bug will be fixed in the next version of Android Native Lib. iOS does not have such a bug.\n\n### Windows\n* Fix a compile error on Visual Studio 2019.\n\n## v1.2.7 / 2020-12-25\nHappy holidays everyone!\n \n### Changes for All platforms\n* Fix a bug when calling `sync()` with `false ` won't do `msync()` asynchronous and won't return immediately.\n\n### Android\n* Fix an null pointer exception when calling `putStringSet()` with `null`.\n* Complete review of all MMKV methods about Java nullable/nonnull annotation.\n* Add API for `MMKV.initialize()` with both `Context` and `LibLoader` parammeters.\n\n### Flutter (v1.2.8)\n* Fix a crash on the iOS simulator when accessing the default MMKV instance.\n* Fix a bug on iOS when initing the default MMKV instance with a crypt key, the instance is still in plaintext.\n\n### Golang\nAdd golang for POSIX platforms. Most things actually work!. Check out the [wiki](https://github.com/Tencent/MMKV/wiki/golang_setup) for information.\n\n## v1.2.6 / 2020-11-27\n### Changes for All platforms\n* Fix a file corruption when calling `reKey()` after `removeKeys()` has just been called.\n\n### Android\n* Fix compile error when `MMKV_DISABLE_CRYPT` is set.\n* Add a preprocess directive `MMKV_DISABLE_FLUTTER` to disable flutter plugin features. If you integrate MMKV by source code, and if you are pretty sure the flutter plugin is not needed, you can turn that off to save some binary size.\n\n### Flutter (v1.2.7)\nAdd MMKV support for **Flutter** on iOS & Android platform.  Most things actually work!  \nCheck out the [wiki](https://github.com/Tencent/MMKV/wiki/flutter_setup) for more info.\n\n## v1.2.5 / 2020-11-13\nThis is a pre-version for Flutter. The official Flutter plugin of MMKV will come out soon. Stay Tune!\n\n### iOS / macOS\n* Fix an assert error of encrypted MMKV when encoding some `<NSCoding>` objects.\n* Fix a potential leak when decoding duplicated keys from the file.\n* Add `+[MMKV pageSize]`, `+[MMKV version]` methods.\n* Add `+[MMKV defaultMMKVWithCryptKey:]`, you can encrypt the default MMKV instance now, just like the Android users who already enjoy this for a long time.\n* Rename `-[MMKV getValueSizeForKey:]` to `-[MMKV getValueSizeForKey: actualSize:]` to align with Android interface.\n\n### Android\n* Fix a potential crash when getting MMKV instances in multi-thread at the same time.\n* Add `MMKV.version()` method.\n\n## v1.2.4 / 2020-10-21\nThis is a hotfix mainly for iOS.\n\n### iOS / macOS\n* Fix a decode error of encrypted MMKV on some devices.\n\n### Android\n* Fix a potential issue on checking `rootDir` in multi-thread while MMKV initialization is not finished.\n\n## v1.2.3 / 2020-10-16\n### Changes for All platforms\n* Fix a potential crash on 32-bit devices due to pointer alignment issue.\n* Fix a decode error of encrypted MMKV on some 32-bit devices.\n\n### iOS / macOS\n* Fix a potential `crc32()` crash on some kind of arm64 devices.\n* Fix a potential crash after calling `+[MMKV onAppTerminate]`.\n\n### Android\n* Fix a rare lock conflict on `checkProcessMode()`.\n\n### POSIX\nAdd MMKV support for **Python** on POSIX platforms.  Most things actually work!  \nCheck out the [wiki](https://github.com/Tencent/MMKV/wiki/python_setup) for more info.\n\n## v1.2.2 / 2020-07-30\n\n### iOS / macOS\n* Add auto clean up feature. Call `+[MMKV enableAutoCleanUp:]` to enable auto cleanup MMKV instances that not been accessed recently.\n* Fix a potential crash on devices under iPhone X.\n\n### Android\n* Add multi-process mode check. After so many issues had been created due to mistakenly using MMKV in multi-process mode in Android, this check is added. If an MMKV instance is accessed with `SINGLE_PROCESS_MODE` in multi-process, an `IllegalArgumentException` will be thrown.\n\n### POSIX\n* Add support for armv7 & arm64 arch on Linux.\n\n## v1.2.1 / 2020-07-03\nThis is a hotfix release. Anyone who has upgraded to v1.2.0 should upgrade to this version **immediately**.\n\n* Fix a potential file corruption bug when writing a file that was created in versions older than v1.2.0. This bug was introduced in v1.2.0.\n* Add a preprocess directive `MMKV_DISABLE_CRYPT` to turn off MMKV encryption ability once and for all. If you integrate MMKV by source code, and if you are pretty sure encryption is not needed, you can turn that off to save some binary size.\n* The parameter `relativePath` (customizing a separate folder for an MMKV instance), has been renamed to `rootPath`. Making it clear that an absolute path is expected for that parameter.\n\n### iOS / macOS\n* `-[MMKV mmkvWithID: relativePath:]` is deprecated. Use `-[MMKV mmkvWithID: rootPath:]` instead. \n* Likewise, `-[MMKV mmkvWithID: cryptKey: relativePath:]` is deprecated. Use `-[MMKV mmkvWithID: cryptKey: rootPath:]` instead. \n\n## v1.2.0 / 2020-06-30\nThis is the second **major version** of MMKV. Everything you call is the same as the last version, while almost everything underneath has been improved. \n\n* **Reduce Memory Footprint**. We used to cache all key-values in a dictionary for efficiency. From now on we store the offset of each key-value inside the mmap-memory instead, **reducing memory footprint by almost half** for (non-encrypt) MMKV. And the accessing efficiency is almost the same as before. As for encrypted MMKV, we can't simply store the offset of each key-value, the relative encrypt info needs to be stored as well. That will be too much for small key-values. We only store such info for large key-values (larger than 256B).\n* **Improve Writeback Efficiency**. Thanks to the optimization above, we now can implement writeback by simply calling **memmove()** multiple times. Efficiency is increased and memory consumption is down.\n* **Optimize Small Key-Values**. Small key-values of encrypted MMKV are still cached in memory, as all the old versions did. From now on, the struct `MMBuffer` will try to **store small values in the stack** instead of in the heap, saving a lot of time from `malloc()` & `free()`. In fact, all primitive types will be store in the stack memory.\n\nAll of the improvements above are available to all supported platforms. Here are the additional changes for each platform.\n\n### iOS / macOS\n* **Optimize insert & delete**. Especially for inserting new values to **existing keys**, or deleting keys. We now use the UTF-8 encoded keys in the mmap-memory instead of live encoding from keys, cutting the cost of string encoding conversion.\n* Fix Xcode compile error on some projects.\n* Drop the support of iOS 8. `thread_local` is not available on iOS 8. We choose to drop support instead of working around because iOS 8's market share is considerably small.\n\n### POSIX\n* It's known that GCC before 5.0 doesn't support C++17 standard very well. You should upgrade to the latest version of GCC to compile MMKV.\n\n## v1.1.2 / 2020-05-29\n\n### Android / iOS & macOS / Windows / POSIX\n\n* Fix a potential crash after `trim()` a multi-process MMKV instance.\n* Improve `clearAll()` a bit.\n\n## v1.1.1 / 2020-04-13\n\n### iOS / macOS\n\n* Support WatchOS.\n* Rename `+[MMKV onExit]` to `+[MMKV onAppTerminate]`, to avoid naming conflict with some other OpenSource projects.\n* Make background write protection much more robust, fix a potential crash when writing meta info in background.\n* Fix a potential data corruption bug when writing a UTF-8 (non-ASCII) key.\n\n### Android\n\n* Fix a crash in the demo project when the App is hot reloaded.\n* Improve wiki & readme to recommend users to init & destruct MMKV in the `Application` class instead of the `MainActivity` class.\n\n### POSIX\n* Fix two compile errors with some compilers.\n\n## v1.1.0 / 2020-03-24\nThis is the first **major breaking version** ever since MMKV was made public in September 2018, introducing bunches of improvement. Due to the Covid-19, it has been delayed for about a month. Now it's finally here! \n\n* **Improved File Recovery Strategic**. We store the CRC checksum & actual file size on each sync operation & full write back, plus storing the actual file size in the same file(aka the .crc meta file) as the CRC checksum. Base on our usage inside WeChat on the iOS platform, it cuts the file **corruption rate down by almost half**.\n* **Unified Core Library**. We refactor the whole MMKV project and unify the cross-platform Core library. From now on, MMKV on iOS/macOS, Android, Windows all **share the same core logic code**. It brings many benefits such as reducing the work to fix common bugs, improvements on one platform are available to other platforms immediately, and much more.\n* **Supports POSIX Platforms**. Thanks to the unified Core library, we port MMKV to POSIX platforms easily.\n* **Multi-Process Access on iOS/macOS**. Thanks to the unified Core library, we add multi-process access to iOS/macOS platforms easily.\n* **Efficiency Improvement**. We make the most of armv8 ability including the AES & CRC32 instructions to tune **encryption & error checking speed up by one order higher** than before on armv8 devices. There are bunches of other speed tuning all around the whole project.\n\nHere are the old-style change logs of each platform.\n\n### iOS / macOS\n* Adds **multi-process access** support. You should initialize MMKV by calling `+[MMKV initializeMMKV: groupDir: logLevel:]`, passing your **app group id**. Then you can get a multi-process instance by calling `+[MMKV mmkvWithID: mode:]` or `+[MMKV mmkvWithID: cryptKey: mode:]`, accessing it cross your app & your app extensions.\n* Add **inter-process content change notification**. You can get MMKV changes notification of other processes by implementing `- onMMKVContentChange:` of `<MMKVHandler>` protocol.\n* **Improved File Recovery Strategic**. Cuts the file corruption rate down by almost half. Details are above.\n* **Efficiency Improvement**. Encryption & error checking speed are up by one order higher on armv8 devices(aka iDevice including iPhone 5S and above). Encryption on armv7 devices is improved as well. Details are ahead.\n* Other speed improvements. Refactor core logic using **MRC**, improve std::vector `push_back()` speed by using **move constructors** & move assignments.\n* `+[MMKV setMMKVBasePath:]` & `+[MMKV setLogLevel:]` are marked **deprecated**. You should use `+[MMKV initializeMMKV:]` or `+[MMKV initializeMMKV: logLevel:]` instead.\n* The `MMKVLogLevel` enum has been improved in Swift. It can be used like `MMKVLogLevel.info` and so on.\n\n### Android\n* **Improved File Recovery Strategic**. Cuts the file corruption rate down by almost half. Details are above.\n* **Efficiency Improvement**. Encryption & error checking speed are up by one order higher on armv8 devices with the `arm64-v8a` abi. Encryption on `armeabi` & `armeabi-v7a` is improved as well. Details are ahead.\n* Add exception inside core encode & decode logic, making MMKV much more robust.\n* Other speed improvements. Improve std::vector `push_back()` speed by using **move constructors** & move assignments.\n\n### Windows\n* **Improved File Recovery Strategic**. Cuts the file corruption rate down by almost half. Details are above.\n* Add exception inside core encode & decode logic, making MMKV much more robust.\n* Other speed improvements. Improve std::vector `push_back()` speed by using **move constructors** & move assignments.\n\n### POSIX\n* Most things actually work! We have tested MMKV on the latest version of Linux(Ubuntu, Arch Linux, CentOS, Gentoo), and Unix(macOS, FreeBSD, OpenBSD) on the time v1.1.0 is released.\n\n## v1.0.24 / 2020-01-16\n\n### iOS / macOS\nWhat's new  \n\n* Fix a bug that MMKV will fail to save any key-values after calling `-[MMKV clearMemoryCache]` and then `-[MMKV clearAll]`.\n* Add `+[MMKV initializeMMKV:]` for users to init MMKV in the main thread, to avoid an iOS 13 potential crash when accessing `UIApplicationState` in child threads.\n* Fix a potential crash when writing a uniquely constructed string.\n* Fix a performance slow down when acquiring MMKV instances too often.\n* Make the baseline test in MMKVDemo more robust to NSUserDefaults' caches.\n\n### Android\nWhat's new  \n\n* Fix `flock()` bug on ashmem files in Android.\n* Fix a potential crash when writing a uniquely constructed string.\n* Fix a bug that the MMKVDemo might crash when running in a simulator.\n\n### Windows\n* Fix a potential crash when writing a uniquely constructed string or data.\n\n## v1.0.23 / 2019-09-03\n\n### iOS / macOS\nWhat's new  \n\n* Fix a potential security leak on encrypted MMKV.\n\n### Android\nWhat's new  \n\n* Fix a potential security leak on encrypted MMKV.\n* Fix filename bug when compiled on Windows environment.\n* Add option for decoding String Set into other `Set<>` classes other than the default `HashSet<String>`, check `decodeStringSet()` for details.\n* Add `putBytes()` & `getBytes()`, to make function names more clear and consistent.\n* Add notification of content changed by other process, check the new `MMKVContentChangeNotification<>` interface & `checkContentChangedByOuterProcess()` for details.\n\n### Windows\nWhat's new  \n\n* Fix a potential security leak on encrypted MMKV.\n* Fix `CriticalSection` init bug.\n\n## v1.0.22 / 2019-06-10\n\n### iOS / macOS\nWhat's new  \n\n* Fix a bug that MMKV will corrupt while adding just one key-value, and reboot or clear memory cache. This bug was introduced in v1.0.21.\n\n### Android\nWhat's new  \n\n* Fix a bug that MMKV will corrupt while adding just one key-value, and reboot or clear memory cache. This bug was introduced in v1.0.21.\n\n### Windows\nWhat's new  \n\n* Fix a bug that MMKV will corrupt while adding just one key-value, and reboot or clear memory cache. This bug was introduced in v1.0.21.\n\n## v1.0.21 / 2019-06-06\n### iOS / macOS\nWhat's new  \n\n* Fix a bug that MMKV might corrupt while repeatedly adding & removing key-value with specific length. This bug was introduced in v1.0.20.\n\n### Android\nWhat's new  \n\n* Fix a bug that MMKV might corrupt while repeatedly adding & removing key-value with specific length. This bug was introduced in v1.0.20.\n\n### Windows\nWhat's new  \n\n* Fix a bug that MMKV might corrupt while repeatedly adding & removing key-value with specific length. This bug was introduced in v1.0.20.\n\n## v1.0.20 / 2019-06-05\n### iOS / macOS\nWhat's new  \n\n* Fix a bug that MMKV might crash while storing key-value with specific length.\n* Fix a bug that `-[MMKV trim]` might not work properly.\n\n### Android\nWhat's new  \n\n* Migrate to AndroidX library.\n* Fix a bug that MMKV might crash while storing key-value with specific length.\n* Fix a bug that `trim()` might not work properly.\n* Fix a bug that dead-lock might be reported by Android mistakenly.\n* Using `RegisterNatives()` to simplify native method naming.\n\n### Windows\n* Fix a bug that MMKV might crash while storing key-value with specific length.\n* Fix a bug that `trim()` might not work properly.\n* Fix a bug that `clearAll()` might not work properly.\n\n## v1.0.19 / 2019-04-22\n### iOS / macOS\nWhat's new  \n\n* Support Swift 5.\n* Add method to get all keys `-[MMKV allKeys]`;\n* Add method to synchronize to file asynchronously `-[MMKV async]`.\n* Fix a pod configuration bug that might override target project's C++ setting on `CLANG_CXX_LANGUAGE_STANDARD`.\n* Fix a bug that `DEFAULT_MMAP_SIZE` might not be initialized before getting any MMKV instance.\n* Fix a bug that openssl's header files included inside MMKV might mess with target project's own openssl implementation.\n\n### Android\nWhat's new  \n\n* Support Android Q.\n* Add method to synchronize to file asynchronously `void sync()`, or `void apply()` that comes with `SharedPreferences.Editor` interface.\n* Fix a bug that a buffer with length of zero might be returned when the key is not existed.\n* Fix a bug that `DEFAULT_MMAP_SIZE` might not be initialized before getting any MMKV instance.\n\n\n## v1.0.18 / 2019-03-14\n### iOS / macOS\nWhat's new  \n\n* Fix a bug that defaultValue was not returned while decoding a `NSCoding` value.\n* Fix a compile error on static linking MMKV while openssl is static linked too.\n\n### Android\nWhat's new  \n\n* Introducing **Native Buffer**. Checkout [wiki](https://github.com/Tencent/MMKV/wiki/android_advance#native-buffer) for details.\n* Fix a potential crash when trying to recover data from file length error.\n* Protect from mistakenly passing `Context.MODE_MULTI_PROCESS` to init MMKV.\n\n\n### Windows\n* Fix a potential crash when trying to recover data from file length error.\n\n## v1.0.17 / 2019-01-25\n### iOS / macOS\nWhat's new  \n\n* Redirect logging of MMKV is supported now.\n* Dynamically disable logging of MMKV is supported now.\n* Add method `migrateFromUserDefaults ` to import from NSUserDefaults.\n\n### Android\nWhat's new  \n\n* Redirect logging of MMKV is supported now.\n* Dynamically disable logging of MMKV is supported now.  \n  Note: These two are breaking changes for interface `MMKVHandler`, update your implementation with `wantLogRedirecting()` & `mmkvLog()` for v1.0.17. (Interface with default method requires API level 24, sigh...)\n* Add option to use custom library loader `initialize(String rootDir, LibLoader loader)`. If you're facing `System.loadLibrary()` crash on some low API level device, consider using **ReLinker** to load MMKV. Example can be found in **mmkvdemo**.\n* Fix a potential corruption of meta file on multi-process mode.\n* Fix a potential crash when the meta file is not valid on multi-process mode.\n\n\n### Windows\n* Redirect logging of MMKV is supported now.\n* Dynamically disable logging of MMKV is supported now.\n* Fix a potential corruption of meta file on multi-process mode.\n\n## v1.0.16 / 2019-01-04\n### iOS / macOS\nWhat's new  \n\n* Customizing root folder of MMKV is supported now.\n* Customizing folder for specific MMKV is supported now.\n* Add method `getValueSizeForKey:` to get value's size of a key.\n\n### Android\nWhat's new  \n\n* Customizing root folder of MMKV is supported now.\n* Customizing folder for specific MMKV is supported now.\n* Add method `getValueSizeForKey()` to get value's size of a key.\n* Fix a potential crash when the meta file is not valid.\n\n\n### Windows\nMMKV for Windows is released now. Most things actually work!\n\n## v1.0.15 / 2018-12-13\n### iOS / macOS\nWhat's new  \n\n* Storing **NSString/NSData/NSDate** directly by calling `setString`/`getSring`, `setData`/`getData`, `setDate`/`getDate`.\n* Fix a potential crash due to divided by zero.\n\n\n### Android\nWhat's new  \n\n* Fix a stack overflow crash due to the **callback** feature introduced by v1.0.13.\n* Fix a potential crash due to divided by zero.\n\n### Windows\nMMKV for Windows in under construction. Hopefully will come out in next release. For those who are interested, check out branch `dev_Windows` for the latest development.\n\n## v1.0.14 / 2018-11-30\n### iOS / macOS\nWhat's new  \n\n* Setting `nil` value to reset a key is supported now.\n* Rename `boolValue(forKey:)` to `bool(forKey:)` for Swift.\n\n\n### Android\nWhat's new  \n\n* `Parcelable` objects can be stored directly into MMKV now.\n* Setting `null` value to reset a key is supported now.\n* Fix an issue that MMKV's file size might expand unexpectly large in some case.\n* Fix an issue that MMKV might crash on multi-thread removing and getting on the same key.\n\n\n## v1.0.13 / 2018-11-09\n### iOS / macOS\nWhat's new  \n\n* Special chars like `/` are supported in MMKV now. The file name of MMKV with special mmapID will be encoded with md5 and stored in seperate folder.\n* Add **callback** for MMKV error handling. You can make MMKV to recover instead of discard when crc32 check fail happens.\n* Add `trim` and `close` operation. Generally speaking they are not necessary in daily usage. Use them if you worry about disk / memory / fd usage.\n* Fix an issue that MMKV's file size might expand unexpectly large in some case.\n\nKnown Issues\n\n* Setting `nil` value to reset a key will be ignored. Use `remove` instead.\n\n### Android\nWhat's new  \n\n* Add static linked of libc++ to trim AAR size. Use it when there's no other lib in your App embeds `libc++_shared.so`. Or if you already have an older version of `libc++_shared.so` that doesn't agree with MMKV.  \nAdd `implementation 'com.tencent:mmkv-static:1.0.13'` to your App's gradle setting to integrate.\n* Special chars like `/` are supported in MMKV now. The file name of MMKV with special mmapID will be encoded with md5 and stored in seperate folder.\n* Add **callback** for MMKV error handling. You can make MMKV to recover instead of discard when crc32 check fail happens.\n* Add `trim` and `close` operation. Generally speaking they are not necessary in daily usage. Use them if you worry about disk / memory / fd usage.\n\nKnown Issues\n\n* Setting `null` value to reset a key will be ignored. Use `remove` instead.\n* MMKV's file size might expand unexpectly large in some case.\n\n## v1.0.12 / 2018-10-18\n### iOS / macOS\nWhat's new  \n\n* Fix `mlock` fail on some devices\n* Fix a performance issue caused by mistakenly merge of test code\n* Fix CocoaPods integration error of **macOS**\n\n### Android / 2018-10-24\nWhat's new  \n\n* Fix `remove()` causing data inconsistency on `MULTI_PROCESS_MODE`\n\n\n## v1.0.11 / 2018-10-12\n### iOS / macOS\nWhat's new  \n\n* Port to **macOS**\n* Support **NSCoding**  \nYou can  store NSArray/NSDictionary or any object what implements `<NSCoding>` protocol.\n* Redesign Swift interface\n* Some performance improvement\n\nKnown Issues\n\n* MMKV use mmapID as its filename, so don't contain any `/` inside mmapID.\n* Storing a value of `type A` and getting by `type B` may not work. MMKV does type erasure while storing values. That means it's hard for MMKV to do value-type-checking, if not impossible.\n\n### Android \nWhat's new  \n\n* Some performance improvement\n\nKnown Issues\n\n* Getting an MMKV instance with mmapID that contains `/` may fail.  \nMMKV uses mmapID as its filename, so don't contain any `/` inside mmapID.\n* Storing a value of `type A` and getting by `type B` may not work.  \nMMKV does type erasure while storing values. That means it's hard for MMKV to do value-type-checking, if not impossible.\n* `registerOnSharedPreferenceChangeListener` not supported.  \nThis is intended. We believe doing data-change-listener inside a storage framework smells really bad to us. We suggest using something like event-bus to notify any interesting clients.\n\n## v1.0.10 / 2018-09-21  \n\n * Initial Release\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at guoling@tencent.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to MMKV\nWelcome to [report Issues](https://github.com/Tencent/MMKV/issues) or [pull requests](https://github.com/Tencent/MMKV/pulls). It's recommended to read the following Contributing Guide first before contributing. \n\n## Issues\nWe use issues to track public bugs and feature requests.\n\n### Search Known Issues First\nPlease search the existing issues to see if any similar issue or feature request has already been filed. You should make sure your issue isn't redundant.\n\n### Reporting New Issues\nIf you open an issue, the more information the better. Such as detailed description, screenshot or video of your problem, logcat or code blocks for your crash.\n\n## Pull Requests\nWe strongly welcome your pull request to make MMKV better. \n\n### Branch Management\nThere are three main branches here:\n\n1. `master` branch.\n\t1. It is the latest (pre-)release branch. We use `master` for tags, with version number `1.1.0`, `1.2.0`, `1.3.0`...\n\t2. **Don't submit any PR on `master` branch.**\n2. `dev` branch. \n\t1. It is our stable developing branch. After full testing, `dev` will be merged to `master` branch for the next release.\n\t2. **You are recommended to submit bugfix or feature PR on `dev` branch.**\n3. `hotfix` branch. \n\t1. It is the latest tag version for hot fix. If we accept your pull request, we may just tag with version number `1.1.1`, `1.2.3`.\n\t2. **Only submit urgent PR on `hotfix` branch for next specific release.**\n\nNormal bugfix or feature request should be submitted to `dev` branch. After full testing, we will merge them to `master` branch for the next release. \n\nIf you have some urgent bugfixes on a published version, but the `master` branch have already far away with the latest tag version, you can submit a PR on hotfix. And it will be cherry picked to `dev` branch if it is possible.\n\n```\nmaster\n ↑\ndev        <--- hotfix PR\n ↑ \nfeature/bugfix PR\n```  \n\n### Make Pull Requests\nThe code team will monitor all pull request, we run some code check and test on it. After all tests passed, we will accecpt this PR. But it won't merge to `master` branch at once, which have some delay.\n\nBefore submitting a pull request, please make sure the followings are done:\n\n1. Fork the repo and create your branch from `master` or `hotfix`.\n2. Update code or documentation if you have changed APIs.\n3. Add the copyright notice to the top of any new files you've added.\n4. Check your code lints and checkstyles.\n5. Test and test again your code.\n6. Now, you can submit your pull request on `dev` or `hotfix` branch.\n\n## Code Style Guide\nWe choose the `LLVM code style` for MMKV project, with the specialization that using 4 space width for indent, and using tab for ObjC indentation. To make things simple, we have already defined our code style inside [clang-format](./.clang-format).  \nYou can just run `make format_code` on top directory to format all your changes before committing them.  \n\nAdditionly, check out [Code Style](./Android/MMKV/checkstyle.xml) for Java and Android.\n\n## License\nBy contributing to MMKV, you agree that your contributions will be licensed\nunder its [BSD LICENSE](./LICENSE.txt)\n"
  },
  {
    "path": "Core/.gitignore",
    "content": "include/\n"
  },
  {
    "path": "Core/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2019 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets the minimum version of CMake required to build the native library.\n\ncmake_minimum_required(VERSION 3.10.0)\n\nIF(APPLE)\n    # tell ranlib to ignore empty compilation units\n    SET(CMAKE_C_ARCHIVE_FINISH   \"<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>\")\n    SET(CMAKE_CXX_ARCHIVE_FINISH \"<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>\")\n    # prevents ar from invoking ranlib, let CMake do it\n    SET(CMAKE_C_ARCHIVE_CREATE   \"<CMAKE_AR> qc -S <TARGET> <LINK_FLAGS> <OBJECTS>\")\n    SET(CMAKE_CXX_ARCHIVE_CREATE \"<CMAKE_AR> qc -S <TARGET> <LINK_FLAGS> <OBJECTS>\")\n\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\nset(can_use_assembler TRUE)\nenable_language(ASM)\nIF(\"${ANDROID_ABI}\" STREQUAL \"arm64-v8a\")\n    SET(ASM_OPTIONS \"-x assembler-with-cpp\")\n    SET(CMAKE_ASM_FLAGS \"${CFLAGS} ${ASM_OPTIONS} -march=armv8+crypto -D__ANDROID__\")\nENDIF()\n\n#include(CMakePrintHelpers)\n#cmake_print_variables(CMAKE_SYSTEM_PROCESSOR OHOS OHOS_ARCH CFLAGS ASM_OPTIONS CMAKE_ASM_FLAGS)\n\nIF(OHOS)\n    IF(\"${OHOS_ARCH}\" STREQUAL \"arm64-v8a\")\n        SET(ASM_OPTIONS \"-x assembler-with-cpp\")\n        SET(CMAKE_ASM_FLAGS \"${CFLAGS} ${ASM_OPTIONS} -march=armv8+crypto -D__MUSL__\")\n    ENDIF()\nELSEIF(UNIX AND (NOT APPLE))\n    IF(\"${CMAKE_SYSTEM_PROCESSOR}\" STREQUAL \"aarch64\")\n        SET(ASM_OPTIONS \"-x assembler-with-cpp\")\n        SET(CMAKE_ASM_FLAGS \"${CFLAGS} ${ASM_OPTIONS} -march=armv8-a+crypto\")\n    ENDIF()\nENDIF()\n\n\nproject(core)\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\nadd_library(core\n\n        # Sets the library as a shared library.\n        STATIC\n\n        # Provides a relative path to your source file(s).\n        MMKV.h\n        MMKV.cpp\n        MMKV_Android.cpp\n        MMKV_IO.h\n        MMKV_IO.cpp\n        MMKV_OSX.cpp\n        MMKVLog.h\n        MMKVLog.cpp\n        MMKVLog_Android.cpp\n        CodedInputData.h\n        CodedInputData.cpp\n        CodedInputData_OSX.cpp\n        CodedInputDataCrypt.h\n        CodedInputDataCrypt.cpp\n        CodedInputDataCrypt_OSX.cpp\n        CodedOutputData.h\n        CodedOutputData.cpp\n        KeyValueHolder.h\n        KeyValueHolder.cpp\n        PBUtility.h\n        PBUtility.cpp\n        MiniPBCoder.h\n        MiniPBCoder.cpp\n        MiniPBCoder_OSX.cpp\n        MMBuffer.h\n        MMBuffer.cpp\n        InterProcessLock.h\n        InterProcessLock.cpp\n        InterProcessLock_Win32.cpp\n        InterProcessLock_Android.cpp\n        MemoryFile.h\n        MemoryFile.cpp\n        MemoryFile_Android.cpp\n        MemoryFile_Linux.cpp\n        MemoryFile_Win32.cpp\n        MemoryFile_OSX.cpp\n        ThreadLock.h\n        ThreadLock.cpp\n        ThreadLock_Win32.cpp\n        MMKVMetaInfo.hpp\n        aes/AESCrypt.h\n        aes/AESCrypt.cpp\n        aes/openssl/openssl_aes.h\n        aes/openssl/openssl_aes_core.cpp\n        aes/openssl/openssl_aes_locl.h\n        aes/openssl/openssl_cfb128.cpp\n        aes/openssl/openssl_opensslconf.h\n        aes/openssl/openssl_md5_dgst.cpp\n        aes/openssl/openssl_md5_locl.h\n        aes/openssl/openssl_md5_one.cpp\n        aes/openssl/openssl_md5.h\n        aes/openssl/openssl_md32_common.h\n        aes/openssl/openssl_arm_arch.h\n        crc32/Checksum.h\n        crc32/crc32_armv8.cpp\n        crc32/zlib/zconf.h\n        crc32/zlib/zutil.h\n        crc32/zlib/crc32.h\n        crc32/zlib/crc32.cpp\n        MMKVPredef.h\n        )\n\nIF (MSVC OR (CMAKE_SYSTEM_PROCESSOR STREQUAL \"x86_64\"))\n    # .S files is not supported by MSVC.\n    # x86_64 asm not supported in OHOS\nELSE()\n    target_sources(core PRIVATE\n        aes/openssl/openssl_aesv8-armx.S)\nENDIF()\n\ntarget_include_directories(core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})\n\nIF (WIN32)\n    # MMKV can be used only with Unicode on Windows.\n    target_compile_definitions(core PUBLIC UNICODE)\n    target_compile_definitions(core PUBLIC _UNICODE)\nENDIF()\n\nset_target_properties(core PROPERTIES\n        CXX_STANDARD 20\n        CXX_EXTENSIONS OFF\n        POSITION_INDEPENDENT_CODE ON\n        )\n\nfind_library(zlib\n        z\n        )\n\nIF (NOT zlib)\n    target_compile_definitions(core PUBLIC MMKV_EMBED_ZLIB=1)\nELSE()\n    target_link_libraries(core ${zlib})\nENDIF()\n\nfunction(copy_files)\n    list(LENGTH ARGN num_args)\n\n    if(num_args LESS 2)\n        message(FATAL_ERROR \"copy_files function requires at least one source file and one destination directory.\")\n        return()\n    endif()\n\n    # Get the last argument as the destination directory\n    list(GET ARGN -1 destination_dir)\n    # Get all arguments except the last one as the source files\n    list(REMOVE_AT ARGN -1)\n    set(source_files ${ARGN})\n\n    file(MAKE_DIRECTORY ${destination_dir})\n\n    foreach(file ${source_files})\n        get_filename_component(file_dir ${file} DIRECTORY)\n        file(MAKE_DIRECTORY ${destination_dir}/${file_dir})\n        file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${file} DESTINATION ${destination_dir}/${file_dir})\n    endforeach()\nendfunction()\n\n#file(REMOVE_RECURSE ${CMAKE_CURRENT_SOURCE_DIR}/include)\n\ncopy_files(\n        MMKV.h\n        MMKVPredef.h\n        MMBuffer.h\n        MiniPBCoder.h\n        MMKVHandler.h\n        ${CMAKE_CURRENT_SOURCE_DIR}/include/MMKV)\n\n#message(STATUS \"copying headers to ${CMAKE_CURRENT_SOURCE_DIR}/include/MMKV\")\n#message(STATUS \"copying headers with ${OHOS} or ${ANDROID}\")\n\nIF(OHOS OR ANDROID)\n    file(COPY MemoryFile.h MMKVLog.h DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include/MMKV)\nELSE()\n    file(REMOVE include/MMKV/MemoryFile.h include/MMKV/MMKVLog.h)\nENDIF()\n\ntarget_include_directories(core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)\n"
  },
  {
    "path": "Core/CodedInputData.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"CodedInputData.h\"\n#include \"PBUtility.h\"\n#include <stdexcept>\n#include <cstring>\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif // MMKV_APPLE\n\nusing namespace std;\n\nnamespace mmkv {\n\nCodedInputData::CodedInputData(const void *oData, size_t length)\n    : m_ptr((uint8_t *) oData), m_size(length), m_position(0) {\n    MMKV_ASSERT(m_ptr);\n}\n\nvoid CodedInputData::seek(size_t addedSize) {\n    if (m_position + addedSize > m_size) {\n        throw out_of_range(\"OutOfSpace\");\n    }\n    m_position += addedSize;\n}\n\ndouble CodedInputData::readDouble() {\n    return Int64ToFloat64(this->readRawLittleEndian64());\n}\n\nfloat CodedInputData::readFloat() {\n    return Int32ToFloat32(this->readRawLittleEndian32());\n}\n\nint64_t CodedInputData::readInt64() {\n    int32_t shift = 0;\n    int64_t result = 0;\n    while (shift < 64) {\n        int8_t b = this->readRawByte();\n        result |= (int64_t)(b & 0x7f) << shift;\n        if ((b & 0x80) == 0) {\n            return result;\n        }\n        shift += 7;\n    }\n    throw invalid_argument(\"InvalidProtocolBuffer malformedInt64\");\n}\n\nuint64_t CodedInputData::readUInt64() {\n    return static_cast<uint64_t>(readInt64());\n}\n\nint32_t CodedInputData::readInt32() {\n    return this->readRawVarint32();\n}\n\nuint32_t CodedInputData::readUInt32() {\n    return static_cast<uint32_t>(readRawVarint32());\n}\n\nbool CodedInputData::readBool() {\n    return this->readRawVarint32() != 0;\n}\n\nstring CodedInputData::readString() {\n    int32_t size = readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        string result((char *) (m_ptr + m_position), s_size);\n        m_position += s_size;\n        return result;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nvoid CodedInputData::readString(string &s) {\n    int32_t size = readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        s.resize(s_size);\n        memcpy((void *) s.data(), (char *) (m_ptr + m_position), s_size);\n        m_position += s_size;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nstring CodedInputData::readString(KeyValueHolder &kvHolder) {\n    kvHolder.offset = static_cast<uint32_t>(m_position);\n\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        kvHolder.keySize = static_cast<uint16_t>(s_size);\n\n        auto ptr = m_ptr + m_position;\n        string result((char *) ptr, s_size);\n        m_position += s_size;\n        return result;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nMMBuffer CodedInputData::readRealData(mmkv::MMBuffer & data) {\n    CodedInputData input(data.getPtr(), data.length());\n    return input.readData(false, true);\n}\n\nMMBuffer CodedInputData::readData(bool copy, bool exactly) {\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    bool isSizeValid = exactly ? (s_size == m_size - m_position) : (s_size <= m_size - m_position);\n    if (isSizeValid) {\n        size_t pos = m_position;\n        m_position += s_size;\n        auto copyFlag = copy ? MMBufferCopy : MMBufferNoCopy;\n        return MMBuffer(((int8_t *) m_ptr) + pos, s_size, copyFlag);\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nvoid CodedInputData::readData(KeyValueHolder &kvHolder) {\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        kvHolder.computedKVSize = static_cast<uint16_t>(m_position - kvHolder.offset);\n        kvHolder.valueSize = static_cast<uint32_t>(s_size);\n\n        m_position += s_size;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nint32_t CodedInputData::readRawVarint32() {\n    int8_t tmp = this->readRawByte();\n    if (tmp >= 0) {\n        return tmp;\n    }\n    int32_t result = tmp & 0x7f;\n    if ((tmp = this->readRawByte()) >= 0) {\n        result |= tmp << 7;\n    } else {\n        result |= (tmp & 0x7f) << 7;\n        if ((tmp = this->readRawByte()) >= 0) {\n            result |= tmp << 14;\n        } else {\n            result |= (tmp & 0x7f) << 14;\n            if ((tmp = this->readRawByte()) >= 0) {\n                result |= tmp << 21;\n            } else {\n                result |= (tmp & 0x7f) << 21;\n                result |= (tmp = this->readRawByte()) << 28;\n                if (tmp < 0) {\n                    // discard upper 32 bits\n                    for (int i = 0; i < 5; i++) {\n                        if (this->readRawByte() >= 0) {\n                            return result;\n                        }\n                    }\n                    throw invalid_argument(\"InvalidProtocolBuffer malformed varint32\");\n                }\n            }\n        }\n    }\n    return result;\n}\n\nint32_t CodedInputData::readRawLittleEndian32() {\n    int8_t b1 = this->readRawByte();\n    int8_t b2 = this->readRawByte();\n    int8_t b3 = this->readRawByte();\n    int8_t b4 = this->readRawByte();\n    return (((int32_t) b1 & 0xff)) | (((int32_t) b2 & 0xff) << 8) | (((int32_t) b3 & 0xff) << 16) |\n           (((int32_t) b4 & 0xff) << 24);\n}\n\nint64_t CodedInputData::readRawLittleEndian64() {\n    int8_t b1 = this->readRawByte();\n    int8_t b2 = this->readRawByte();\n    int8_t b3 = this->readRawByte();\n    int8_t b4 = this->readRawByte();\n    int8_t b5 = this->readRawByte();\n    int8_t b6 = this->readRawByte();\n    int8_t b7 = this->readRawByte();\n    int8_t b8 = this->readRawByte();\n    return (((int64_t) b1 & 0xff)) | (((int64_t) b2 & 0xff) << 8) | (((int64_t) b3 & 0xff) << 16) |\n           (((int64_t) b4 & 0xff) << 24) | (((int64_t) b5 & 0xff) << 32) | (((int64_t) b6 & 0xff) << 40) |\n           (((int64_t) b7 & 0xff) << 48) | (((int64_t) b8 & 0xff) << 56);\n}\n\nint8_t CodedInputData::readRawByte() {\n    if (m_position == m_size) {\n        auto msg = \"reach end, m_position: \" + to_string(m_position) + \", m_size: \" + to_string(m_size);\n        throw out_of_range(msg);\n    }\n    auto *bytes = (int8_t *) m_ptr;\n    return bytes[m_position++];\n}\n\n#ifdef MMKV_APPLE\n#endif // MMKV_APPLE\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/CodedInputData.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_CODEDINPUTDATA_H\n#define MMKV_CODEDINPUTDATA_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include \"KeyValueHolder.h\"\n#include \"MMBuffer.h\"\n#include <cstdint>\n\nnamespace mmkv {\n\nclass CodedInputData {\n    uint8_t *const m_ptr;\n    size_t m_size;\n    size_t m_position;\n\n    int8_t readRawByte();\n\n    int32_t readRawVarint32();\n\n    int32_t readRawLittleEndian32();\n\n    int64_t readRawLittleEndian64();\n\npublic:\n    CodedInputData(const void *oData, size_t length);\n\n    bool isAtEnd() const { return m_position == m_size; };\n\n    void seek(size_t addedSize);\n\n    bool readBool();\n\n    double readDouble();\n\n    float readFloat();\n\n    int64_t readInt64();\n\n    uint64_t readUInt64();\n\n    int32_t readInt32();\n\n    uint32_t readUInt32();\n\n    // exactly is like getValueSize(actualSize = true)\n    MMBuffer readData(bool copy = true, bool exactly = false);\n    void readData(KeyValueHolder &kvHolder);\n\n    static MMBuffer readRealData(mmkv::MMBuffer & data);\n\n    std::string readString();\n    void readString(std::string &s);\n    std::string readString(KeyValueHolder &kvHolder);\n#ifdef __OBJC__\n    NSString *readNSString();\n    NSString *readNSString(KeyValueHolder &kvHolder);\n    NSData *readNSData();\n#endif\n};\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_CODEDINPUTDATA_H\n"
  },
  {
    "path": "Core/CodedInputDataCrypt.cpp",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#include \"CodedInputDataCrypt.h\"\n#include \"MMKVLog.h\"\n#include \"PBUtility.h\"\n#include <cassert>\n#include <cerrno>\n#include <cstring>\n#include <stdexcept>\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif // MMKV_APPLE\n\n#ifndef MMKV_DISABLE_CRYPT\n\nusing namespace std;\n\nnamespace mmkv {\n\nCodedInputDataCrypt::CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt)\n    : m_ptr((uint8_t *) oData), m_size(length), m_position(0), m_decryptPosition(0), m_decrypter(crypt) {\n    m_decryptBufferSize = AES_IV_LEN * 2;\n    m_decryptBufferPosition = static_cast<size_t>(crypt.m_number);\n    m_decryptBufferDiscardPosition = m_decryptBufferPosition;\n    m_decryptBufferDecryptLength = m_decryptBufferPosition;\n\n    m_decryptBuffer = (uint8_t *) malloc(m_decryptBufferSize);\n    if (!m_decryptBuffer) {\n        throw runtime_error(strerror(errno));\n    }\n}\n\nCodedInputDataCrypt::~CodedInputDataCrypt() {\n    if (m_decryptBuffer) {\n        free(m_decryptBuffer);\n    }\n}\n\nvoid CodedInputDataCrypt::seek(size_t addedSize) {\n    m_position += addedSize;\n    m_decryptPosition += addedSize;\n\n    if (m_position > m_size) {\n        throw out_of_range(\"OutOfSpace\");\n    }\n    assert(m_position % AES_IV_LEN == m_decrypter.m_number);\n}\n\nvoid CodedInputDataCrypt::consumeBytes(size_t length, bool discardPreData) {\n    if (discardPreData) {\n        m_decryptBufferDiscardPosition = m_decryptBufferPosition;\n    }\n    auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;\n    if (decryptedBytesLeft >= length) {\n        return;\n    }\n    length -= decryptedBytesLeft;\n\n    // if there's some data left inside m_decrypter.m_vector, use them first\n    // it will be faster when always decrypt with (n * AES_IV_LEN) bytes\n    if (m_decrypter.m_number != 0) {\n        auto alignDecrypter = AES_IV_LEN - m_decrypter.m_number;\n        // make sure no data left inside m_decrypter.m_vector after decrypt\n        if (length < alignDecrypter) {\n            length = alignDecrypter;\n        } else {\n            length -= alignDecrypter;\n            length = ((length + AES_IV_LEN - 1) / AES_IV_LEN) * AES_IV_LEN;\n            length += alignDecrypter;\n        }\n    } else {\n        length = ((length + AES_IV_LEN - 1) / AES_IV_LEN) * AES_IV_LEN;\n    }\n    auto bytesLeftInSrc = m_size - m_decryptPosition;\n    length = min(bytesLeftInSrc, length);\n\n    auto bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;\n    // try move some space\n    if (bytesLeftInBuffer < length && m_decryptBufferDiscardPosition > 0) {\n        auto posToMove = (m_decryptBufferDiscardPosition / AES_IV_LEN) * AES_IV_LEN;\n        if (posToMove) {\n            auto sizeToMove = m_decryptBufferDecryptLength - posToMove;\n            memmove(m_decryptBuffer, m_decryptBuffer + posToMove, sizeToMove);\n            m_decryptBufferPosition -= posToMove;\n            m_decryptBufferDecryptLength -= posToMove;\n            m_decryptBufferDiscardPosition = 0;\n            bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;\n        }\n    }\n    // still no enough space, try realloc()\n    if (bytesLeftInBuffer < length) {\n        auto newSize = m_decryptBufferSize + length;\n        auto newBuffer = realloc(m_decryptBuffer, newSize);\n        if (!newBuffer) {\n            throw runtime_error(strerror(errno));\n        }\n        m_decryptBuffer = (uint8_t *) newBuffer;\n        m_decryptBufferSize = newSize;\n    }\n    m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength, length);\n    m_decryptPosition += length;\n    m_decryptBufferDecryptLength += length;\n    assert(m_decryptPosition == m_size || m_decrypter.m_number == 0);\n}\n\nvoid CodedInputDataCrypt::skipBytes(size_t length) {\n    m_position += length;\n\n    auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;\n    if (decryptedBytesLeft >= length) {\n        m_decryptBufferPosition += length;\n        return;\n    }\n    length -= decryptedBytesLeft;\n    // if this happens, we need optimization like the alignDecrypter above\n    assert(m_decrypter.m_number == 0);\n\n    size_t alignSize = ((length + AES_IV_LEN - 1) / AES_IV_LEN) * AES_IV_LEN;\n    auto bytesLeftInSrc = m_size - m_decryptPosition;\n    auto size = min(alignSize, bytesLeftInSrc);\n    decryptedBytesLeft = size - length;\n    for (size_t index = 0, round = size / AES_IV_LEN; index < round; index++) {\n        m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, AES_IV_LEN);\n        m_decryptPosition += AES_IV_LEN;\n        size -= AES_IV_LEN;\n    }\n    if (size) {\n        m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, size);\n        m_decryptPosition += size;\n        m_decryptBufferPosition = size - decryptedBytesLeft;\n        m_decryptBufferDecryptLength = size;\n    } else {\n        m_decryptBufferPosition = AES_IV_LEN - decryptedBytesLeft;\n        m_decryptBufferDecryptLength = AES_IV_LEN;\n    }\n    assert(m_decryptBufferPosition <= m_decryptBufferDecryptLength);\n    assert(m_decryptPosition - m_decryptBufferDecryptLength + m_decryptBufferPosition == m_position);\n}\n\ninline void CodedInputDataCrypt::statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status) {\n    rollbackSize += m_decryptBufferDecryptLength - m_decryptBufferPosition;\n    m_decrypter.statusBeforeDecrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength,\n                                    rollbackSize, status);\n}\n\nint8_t CodedInputDataCrypt::readRawByte() {\n    assert(m_position <= m_decryptPosition);\n    if (m_position == m_size) {\n        auto msg = \"reach end, m_position: \" + to_string(m_position) + \", m_size: \" + to_string(m_size);\n        throw out_of_range(msg);\n    }\n    m_position++;\n\n    assert(m_decryptBufferPosition < m_decryptBufferSize);\n    auto *bytes = (int8_t *) m_decryptBuffer;\n    return bytes[m_decryptBufferPosition++];\n}\n\nint32_t CodedInputDataCrypt::readRawVarint32(bool discardPreData) {\n    consumeBytes(10, discardPreData);\n\n    int8_t tmp = this->readRawByte();\n    if (tmp >= 0) {\n        return tmp;\n    }\n    int32_t result = tmp & 0x7f;\n    if ((tmp = this->readRawByte()) >= 0) {\n        result |= tmp << 7;\n    } else {\n        result |= (tmp & 0x7f) << 7;\n        if ((tmp = this->readRawByte()) >= 0) {\n            result |= tmp << 14;\n        } else {\n            result |= (tmp & 0x7f) << 14;\n            if ((tmp = this->readRawByte()) >= 0) {\n                result |= tmp << 21;\n            } else {\n                result |= (tmp & 0x7f) << 21;\n                result |= (tmp = this->readRawByte()) << 28;\n                if (tmp < 0) {\n                    // discard upper 32 bits\n                    for (int i = 0; i < 5; i++) {\n                        if (this->readRawByte() >= 0) {\n                            return result;\n                        }\n                    }\n                    throw invalid_argument(\"InvalidProtocolBuffer malformed varint32\");\n                }\n            }\n        }\n    }\n    return result;\n}\n\nint32_t CodedInputDataCrypt::readInt32() {\n    return this->readRawVarint32();\n}\n\nstring CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) {\n    kvHolder.offset = static_cast<uint32_t>(m_position);\n\n    int32_t size = this->readRawVarint32(true);\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        consumeBytes(s_size);\n\n        kvHolder.keySize = static_cast<uint16_t>(s_size);\n\n        string result((char *) (m_decryptBuffer + m_decryptBufferPosition), s_size);\n        m_position += s_size;\n        m_decryptBufferPosition += s_size;\n        return result;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nvoid CodedInputDataCrypt::readData(KeyValueHolderCrypt &kvHolder) {\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        if (KeyValueHolderCrypt::isValueStoredAsOffset(s_size)) {\n            kvHolder.type = KeyValueHolderType_Offset;\n            kvHolder.valueSize = static_cast<uint32_t>(s_size);\n            kvHolder.pbKeyValueSize =\n                static_cast<uint8_t>(pbRawVarint32Size(kvHolder.valueSize) + pbRawVarint32Size(kvHolder.keySize));\n\n            size_t rollbackSize = kvHolder.pbKeyValueSize + kvHolder.keySize;\n            statusBeforeDecrypt(rollbackSize, kvHolder.cryptStatus);\n\n            skipBytes(s_size);\n        } else {\n            consumeBytes(s_size);\n\n            kvHolder.type = KeyValueHolderType_Direct;\n            kvHolder = KeyValueHolderCrypt(m_decryptBuffer + m_decryptBufferPosition, s_size);\n            m_decryptBufferPosition += s_size;\n            m_position += s_size;\n        }\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\n} // namespace mmkv\n\n#endif // MMKV_DISABLE_CRYPT\n"
  },
  {
    "path": "Core/CodedInputDataCrypt.h",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#ifndef CodedInputDataCrypt_h\n#define CodedInputDataCrypt_h\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include \"KeyValueHolder.h\"\n#include \"MMBuffer.h\"\n#include \"aes/AESCrypt.h\"\n#include <cstdint>\n\n#ifdef MMKV_DISABLE_CRYPT\n\nnamespace mmkv {\nclass CodedInputDataCrypt;\n}\n\n#else\n\nnamespace mmkv {\n\nclass CodedInputDataCrypt {\n    uint8_t *const m_ptr;\n    size_t m_size;\n    size_t m_position;\n    size_t m_decryptPosition; // position of text that has beed decrypted\n\n    AESCrypt &m_decrypter;\n    uint8_t *m_decryptBuffer; // internal decrypt buffer, grows by (n * AES_IV_LEN) bytes\n    size_t m_decryptBufferSize;\n    size_t m_decryptBufferPosition; // reader position in the buffer, synced with m_position\n    size_t m_decryptBufferDecryptLength; // length of the buffer that has been used\n    size_t m_decryptBufferDiscardPosition; // recycle position, any data before that can be discarded\n\n    void consumeBytes(size_t length, bool discardPreData = false);\n    void skipBytes(size_t length);\n    void statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status);\n\n    int8_t readRawByte();\n\n    int32_t readRawVarint32(bool discardPreData = false);\n\npublic:\n    CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt);\n\n    ~CodedInputDataCrypt();\n\n    bool isAtEnd() { return m_position == m_size; };\n\n    void seek(size_t addedSize);\n\n    int32_t readInt32();\n\n    void readData(KeyValueHolderCrypt &kvHolder);\n\n    std::string readString(KeyValueHolderCrypt &kvHolder);\n#ifdef __OBJC__\n    NSString *readNSString(KeyValueHolderCrypt &kvHolder);\n#endif\n};\n\n} // namespace mmkv\n\n#endif // MMKV_DISABLE_CRYPT\n#endif // __cplusplus\n#endif /* CodedInputDataCrypt_h */\n"
  },
  {
    "path": "Core/CodedInputDataCrypt_OSX.cpp",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#include \"CodedInputDataCrypt.h\"\n\n#if defined(MMKV_APPLE) && !defined(MMKV_DISABLE_CRYPT)\n\n#    include \"PBUtility.h\"\n#    include <stdexcept>\n\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n\nusing namespace std;\n\nnamespace mmkv {\n\nNSString *CodedInputDataCrypt::readNSString(KeyValueHolderCrypt &kvHolder) {\n    kvHolder.offset = static_cast<uint32_t>(m_position);\n\n    int32_t size = this->readRawVarint32(true);\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        consumeBytes(s_size);\n\n        kvHolder.keySize = static_cast<uint16_t>(s_size);\n\n        auto ptr = m_decryptBuffer + m_decryptBufferPosition;\n        NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];\n        m_position += s_size;\n        m_decryptBufferPosition += s_size;\n        return [result autorelease];\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\n} // namespace mmkv\n\n#endif // MMKV_APPLE && !MMKV_DISABLE_CRYPT\n"
  },
  {
    "path": "Core/CodedInputData_OSX.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"CodedInputData.h\"\n\n#ifdef MMKV_APPLE\n\n#    include \"PBUtility.h\"\n#    include <stdexcept>\n\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n\nusing namespace std;\n\nnamespace mmkv {\n\nNSString *CodedInputData::readNSString() {\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        auto ptr = m_ptr + m_position;\n        NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];\n        m_position += s_size;\n        return [result autorelease];\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nNSString *CodedInputData::readNSString(KeyValueHolder &kvHolder) {\n    kvHolder.offset = static_cast<uint32_t>(m_position);\n\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        kvHolder.keySize = static_cast<uint16_t>(s_size);\n\n        auto ptr = m_ptr + m_position;\n        NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];\n        m_position += s_size;\n        return [result autorelease];\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\nNSData *CodedInputData::readNSData() {\n    int32_t size = this->readRawVarint32();\n    if (size < 0) {\n        throw length_error(\"InvalidProtocolBuffer negativeSize\");\n    }\n\n    auto s_size = static_cast<size_t>(size);\n    if (s_size <= m_size - m_position) {\n        NSData *result = [NSData dataWithBytes:(m_ptr + m_position) length:s_size];\n        m_position += s_size;\n        return result;\n    } else {\n        throw out_of_range(\"InvalidProtocolBuffer truncatedMessage\");\n    }\n}\n\n} // namespace mmkv\n\n#endif // MMKV_APPLE\n"
  },
  {
    "path": "Core/CodedOutputData.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"CodedOutputData.h\"\n#include \"PBUtility.h\"\n#include <cstring>\n#include <stdexcept>\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif // MMKV_APPLE\n\nusing namespace std;\n\nnamespace mmkv {\n\nCodedOutputData::CodedOutputData(void *ptr, size_t len) : m_ptr((uint8_t *) ptr), m_size(len), m_position(0) {\n    MMKV_ASSERT(m_ptr);\n}\n\nuint8_t *CodedOutputData::curWritePointer() {\n    return m_ptr + m_position;\n}\n\nvoid CodedOutputData::writeDouble(double value) {\n    this->writeRawLittleEndian64(Float64ToInt64(value));\n}\n\nvoid CodedOutputData::writeFloat(float value) {\n    this->writeRawLittleEndian32(Float32ToInt32(value));\n}\n\nvoid CodedOutputData::writeInt64(int64_t value) {\n    this->writeRawVarint64(value);\n}\n\nvoid CodedOutputData::writeUInt64(uint64_t value) {\n    writeRawVarint64(static_cast<int64_t>(value));\n}\n\nvoid CodedOutputData::writeInt32(int32_t value) {\n    if (value >= 0) {\n        this->writeRawVarint32(value);\n    } else {\n        this->writeRawVarint64(value);\n    }\n}\n\nvoid CodedOutputData::writeUInt32(uint32_t value) {\n    writeRawVarint32(static_cast<int32_t>(value));\n}\n\nvoid CodedOutputData::writeBool(bool value) {\n    this->writeRawByte(static_cast<uint8_t>(value ? 1 : 0));\n}\n\nvoid CodedOutputData::writeData(const MMBuffer &value) {\n    this->writeRawVarint32((int32_t) value.length());\n    this->writeRawData(value);\n}\n\nvoid CodedOutputData::writeString(const string &value) {\n    size_t numberOfBytes = value.size();\n    this->writeRawVarint32((int32_t) numberOfBytes);\n    if (m_position + numberOfBytes > m_size) {\n        auto msg = \"m_position: \" + to_string(m_position) + \", numberOfBytes: \" + to_string(numberOfBytes) +\n                   \", m_size: \" + to_string(m_size);\n        throw out_of_range(msg);\n    }\n    memcpy(m_ptr + m_position, ((uint8_t *) value.data()), numberOfBytes);\n    m_position += numberOfBytes;\n}\n\nsize_t CodedOutputData::spaceLeft() {\n    if (m_size <= m_position) {\n        return 0;\n    }\n    return m_size - m_position;\n}\n\nvoid CodedOutputData::seek(size_t addedSize) {\n    m_position += addedSize;\n\n    if (m_position > m_size) {\n        throw out_of_range(\"OutOfSpace\");\n    }\n}\n\nvoid CodedOutputData::reset() {\n    m_position = 0;\n}\n\nsize_t CodedOutputData::getPosition() {\n    return m_position;\n}\n\nvoid CodedOutputData::setPosition(size_t position) {\n    m_position = position;\n}\n\nvoid CodedOutputData::writeRawByte(uint8_t value) {\n    if (m_position == m_size) {\n        throw out_of_range(\"m_position: \" + to_string(m_position) + \" m_size: \" + to_string(m_size));\n        return;\n    }\n\n    m_ptr[m_position++] = value;\n}\n\nvoid CodedOutputData::writeRawData(const MMBuffer &data) {\n    size_t numberOfBytes = data.length();\n    if (m_position + numberOfBytes > m_size) {\n        auto msg = \"m_position: \" + to_string(m_position) + \", numberOfBytes: \" + to_string(numberOfBytes) +\n                   \", m_size: \" + to_string(m_size);\n        throw out_of_range(msg);\n    }\n    memcpy(m_ptr + m_position, data.getPtr(), numberOfBytes);\n    m_position += numberOfBytes;\n}\n\nvoid CodedOutputData::writeRawVarint32(int32_t value) {\n    while (true) {\n        if ((value & ~0x7f) == 0) {\n            this->writeRawByte(static_cast<uint8_t>(value));\n            return;\n        } else {\n            this->writeRawByte(static_cast<uint8_t>((value & 0x7F) | 0x80));\n            value = logicalRightShift32(value, 7);\n        }\n    }\n}\n\nvoid CodedOutputData::writeRawVarint64(int64_t value) {\n    while (true) {\n        if ((value & ~0x7f) == 0) {\n            this->writeRawByte(static_cast<uint8_t>(value));\n            return;\n        } else {\n            this->writeRawByte(static_cast<uint8_t>((value & 0x7f) | 0x80));\n            value = logicalRightShift64(value, 7);\n        }\n    }\n}\n\nvoid CodedOutputData::writeRawLittleEndian32(int32_t value) {\n    this->writeRawByte(static_cast<uint8_t>((value) &0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 8) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 16) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 24) & 0xff));\n}\n\nvoid CodedOutputData::writeRawLittleEndian64(int64_t value) {\n    this->writeRawByte(static_cast<uint8_t>((value) &0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 8) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 16) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 24) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 32) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 40) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 48) & 0xff));\n    this->writeRawByte(static_cast<uint8_t>((value >> 56) & 0xff));\n}\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/CodedOutputData.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_CODEDOUTPUTDATA_H\n#define MMKV_CODEDOUTPUTDATA_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include \"MMBuffer.h\"\n#include <cstdint>\n\nnamespace mmkv {\n\nclass CodedOutputData {\n    uint8_t *const m_ptr;\n    size_t m_size;\n    size_t m_position;\n\npublic:\n    CodedOutputData(void *ptr, size_t len);\n\n    size_t spaceLeft();\n\n    uint8_t *curWritePointer();\n\n    void seek(size_t addedSize);\n\n    void reset();\n\n    size_t getPosition();\n\n    void setPosition(size_t position);\n\n    void writeRawByte(uint8_t value);\n\n    void writeRawLittleEndian32(int32_t value);\n\n    void writeRawLittleEndian64(int64_t value);\n\n    void writeRawVarint32(int32_t value);\n\n    void writeRawVarint64(int64_t value);\n\n    void writeRawData(const MMBuffer &data);\n\n    void writeDouble(double value);\n\n    void writeFloat(float value);\n\n    void writeInt64(int64_t value);\n\n    void writeUInt64(uint64_t value);\n\n    void writeInt32(int32_t value);\n\n    void writeUInt32(uint32_t value);\n\n    void writeBool(bool value);\n\n    void writeData(const MMBuffer &value);\n\n    void writeString(const std::string &value);\n};\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_CODEDOUTPUTDATA_H\n"
  },
  {
    "path": "Core/Core.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tCB0798F02F6A4B3C00B74BB8 /* MMKVHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB0798EF2F6A4A6D00B74BB8 /* MMKVHandler.h */; };\n\t\tCB0798F12F6A4B4200B74BB8 /* MMKVHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB0798EF2F6A4A6D00B74BB8 /* MMKVHandler.h */; };\n\t\tCB0DCC45242B384E009AFE59 /* openssl_md5.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640223AB2E9100ACCD39 /* openssl_md5.h */; };\n\t\tCB0DCC46242B3856009AFE59 /* openssl_opensslconf.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640023AB2E9100ACCD39 /* openssl_opensslconf.h */; };\n\t\tCB0DCC6D242B5786009AFE59 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBF3450323B4BABA00168AC7 /* libz.tbd */; };\n\t\tCB30A3EE24987C32007171B1 /* CodedInputDataCrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB30A3EC24987C32007171B1 /* CodedInputDataCrypt.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB30A3EF24987C32007171B1 /* CodedInputDataCrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB30A3EC24987C32007171B1 /* CodedInputDataCrypt.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB30A41F2498CFB7007171B1 /* CodedInputDataCrypt_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB30A41E2498CFB6007171B1 /* CodedInputDataCrypt_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB30A4202498CFB7007171B1 /* CodedInputDataCrypt_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB30A41E2498CFB6007171B1 /* CodedInputDataCrypt_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB550CCC2488E3F20042CD20 /* KeyValueHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB550CCA2488E3F20042CD20 /* KeyValueHolder.cpp */; };\n\t\tCB58B40023AB3035002457F1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB58B3FF23AB3035002457F1 /* Foundation.framework */; };\n\t\tCB6D44C323B308D100F369AA /* MMKVLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F423AB2E9100ACCD39 /* MMKVLog.h */; };\n\t\tCB6F6C0823ACCFEE000351EA /* ScopedLock.hpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F323AB2E9100ACCD39 /* ScopedLock.hpp */; };\n\t\tCB7589D3248E68F100A546B4 /* KeyValueHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB550CCA2488E3F20042CD20 /* KeyValueHolder.cpp */; };\n\t\tCB7C029A24A0F65B008D77E6 /* MMKV_IO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB7C029924A0F65B008D77E6 /* MMKV_IO.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB7C029B24A0F65B008D77E6 /* MMKV_IO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB7C029924A0F65B008D77E6 /* MMKV_IO.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641423AB2E9100ACCD39 /* CodedOutputData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563EF23AB2E9100ACCD39 /* CodedOutputData.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641523AB2E9100ACCD39 /* MemoryFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F023AB2E9100ACCD39 /* MemoryFile.cpp */; };\n\t\tCB95641623AB2E9100ACCD39 /* MMKV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F223AB2E9100ACCD39 /* MMKV.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641723AB2E9100ACCD39 /* MiniPBCoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F523AB2E9100ACCD39 /* MiniPBCoder.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641823AB2E9100ACCD39 /* ThreadLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F723AB2E9100ACCD39 /* ThreadLock.cpp */; };\n\t\tCB95641923AB2E9100ACCD39 /* MMBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641A23AB2E9100ACCD39 /* CodedInputData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F923AB2E9100ACCD39 /* CodedInputData.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB95641B23AB2E9100ACCD39 /* openssl_cfb128.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640123AB2E9100ACCD39 /* openssl_cfb128.cpp */; };\n\t\tCB95641C23AB2E9100ACCD39 /* openssl_aes_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640323AB2E9100ACCD39 /* openssl_aes_core.cpp */; };\n\t\tCB95641E23AB2E9100ACCD39 /* openssl_md5_one.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640723AB2E9100ACCD39 /* openssl_md5_one.cpp */; };\n\t\tCB95641F23AB2E9100ACCD39 /* openssl_md5_dgst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640923AB2E9100ACCD39 /* openssl_md5_dgst.cpp */; };\n\t\tCB95642023AB2E9100ACCD39 /* AESCrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640B23AB2E9100ACCD39 /* AESCrypt.cpp */; };\n\t\tCB95642123AB2E9100ACCD39 /* InterProcessLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641023AB2E9100ACCD39 /* InterProcessLock.cpp */; };\n\t\tCB95642223AB2E9100ACCD39 /* MMKVLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641123AB2E9100ACCD39 /* MMKVLog.cpp */; };\n\t\tCB95642323AB2E9100ACCD39 /* PBUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641223AB2E9100ACCD39 /* PBUtility.cpp */; };\n\t\tCB95642423AB2F7200ACCD39 /* MMKV.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563ED23AB2E9100ACCD39 /* MMKV.h */; };\n\t\tCB95642523AB2FA800ACCD39 /* MMBuffer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563FA23AB2E9100ACCD39 /* MMBuffer.h */; };\n\t\tCB95642723AB2FB800ACCD39 /* PBUtility.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F623AB2E9100ACCD39 /* PBUtility.h */; };\n\t\tCB95642823AB2FB800ACCD39 /* ThreadLock.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640D23AB2E9100ACCD39 /* ThreadLock.h */; };\n\t\tCB95642923AB2FC100ACCD39 /* MMKVPredef.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563EE23AB2E9100ACCD39 /* MMKVPredef.h */; };\n\t\tCBC7A01123C7231600CCC492 /* openssl_aesv8-armx.S in Sources */ = {isa = PBXBuildFile; fileRef = CBC7A01023C7231600CCC492 /* openssl_aesv8-armx.S */; };\n\t\tCBD723BF23B5C22800D3CDAF /* MMKV_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723BE23B5C22800D3CDAF /* MMKV_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBD723F323B5E59800D3CDAF /* MemoryFile_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F223B5E59800D3CDAF /* MemoryFile_OSX.cpp */; };\n\t\tCBD723F523B5E76200D3CDAF /* MiniPBCoder_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F423B5E76200D3CDAF /* MiniPBCoder_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBD723F723B5FD9E00D3CDAF /* CodedInputData_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F623B5FD9E00D3CDAF /* CodedInputData_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBE668A32D5C8746005C184E /* MiniPBCoder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F123AB2E9100ACCD39 /* MiniPBCoder.h */; };\n\t\tCBE668A42D5C9467005C184E /* MiniPBCoder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F123AB2E9100ACCD39 /* MiniPBCoder.h */; };\n\t\tCBF19050243D70BA001C82ED /* ThreadLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F723AB2E9100ACCD39 /* ThreadLock.cpp */; };\n\t\tCBF19051243D70BA001C82ED /* MemoryFile_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F223B5E59800D3CDAF /* MemoryFile_OSX.cpp */; };\n\t\tCBF19052243D70BA001C82ED /* MMKV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F223AB2E9100ACCD39 /* MMKV.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19053243D70BA001C82ED /* MiniPBCoder_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F423B5E76200D3CDAF /* MiniPBCoder_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19054243D70BA001C82ED /* AESCrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640B23AB2E9100ACCD39 /* AESCrypt.cpp */; };\n\t\tCBF19055243D70BA001C82ED /* openssl_cfb128.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640123AB2E9100ACCD39 /* openssl_cfb128.cpp */; };\n\t\tCBF19056243D70BA001C82ED /* CodedOutputData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563EF23AB2E9100ACCD39 /* CodedOutputData.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19057243D70BA001C82ED /* openssl_md5_dgst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640923AB2E9100ACCD39 /* openssl_md5_dgst.cpp */; };\n\t\tCBF1905A243D70BA001C82ED /* MemoryFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F023AB2E9100ACCD39 /* MemoryFile.cpp */; };\n\t\tCBF1905B243D70BA001C82ED /* CodedInputData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F923AB2E9100ACCD39 /* CodedInputData.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF1905C243D70BA001C82ED /* MMKVLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641123AB2E9100ACCD39 /* MMKVLog.cpp */; };\n\t\tCBF1905D243D70BA001C82ED /* PBUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641223AB2E9100ACCD39 /* PBUtility.cpp */; };\n\t\tCBF1905E243D70BA001C82ED /* MiniPBCoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F523AB2E9100ACCD39 /* MiniPBCoder.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF1905F243D70BA001C82ED /* openssl_md5_one.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640723AB2E9100ACCD39 /* openssl_md5_one.cpp */; };\n\t\tCBF19060243D70BA001C82ED /* openssl_aes_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640323AB2E9100ACCD39 /* openssl_aes_core.cpp */; };\n\t\tCBF19061243D70BA001C82ED /* MMBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19062243D70BA001C82ED /* CodedInputData_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723F623B5FD9E00D3CDAF /* CodedInputData_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19063243D70BA001C82ED /* openssl_aesv8-armx.S in Sources */ = {isa = PBXBuildFile; fileRef = CBC7A01023C7231600CCC492 /* openssl_aesv8-armx.S */; };\n\t\tCBF19064243D70BA001C82ED /* MMKV_OSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBD723BE23B5C22800D3CDAF /* MMKV_OSX.cpp */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19065243D70BA001C82ED /* InterProcessLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95641023AB2E9100ACCD39 /* InterProcessLock.cpp */; };\n\t\tCBF19067243D70BA001C82ED /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBF3450323B4BABA00168AC7 /* libz.tbd */; };\n\t\tCBF19068243D70BA001C82ED /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB58B3FF23AB3035002457F1 /* Foundation.framework */; };\n\t\tCBF1906A243D70BA001C82ED /* openssl_opensslconf.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640023AB2E9100ACCD39 /* openssl_opensslconf.h */; };\n\t\tCBF1906B243D70BA001C82ED /* openssl_md5.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640223AB2E9100ACCD39 /* openssl_md5.h */; };\n\t\tCBF1906C243D70BA001C82ED /* MMKVLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F423AB2E9100ACCD39 /* MMKVLog.h */; };\n\t\tCBF1906D243D70BA001C82ED /* ScopedLock.hpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F323AB2E9100ACCD39 /* ScopedLock.hpp */; };\n\t\tCBF1906E243D70BA001C82ED /* MMKVPredef.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563EE23AB2E9100ACCD39 /* MMKVPredef.h */; };\n\t\tCBF1906F243D70BA001C82ED /* PBUtility.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563F623AB2E9100ACCD39 /* PBUtility.h */; };\n\t\tCBF19070243D70BA001C82ED /* ThreadLock.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB95640D23AB2E9100ACCD39 /* ThreadLock.h */; };\n\t\tCBF19071243D70BA001C82ED /* MMBuffer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563FA23AB2E9100ACCD39 /* MMBuffer.h */; };\n\t\tCBF19072243D70BA001C82ED /* MMKV.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB9563ED23AB2E9100ACCD39 /* MMKV.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tCB9563D623AB2D9500ACCD39 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"include/$(PRODUCT_NAME)\";\n\t\t\tdstSubfolderSpec = 16;\n\t\t\tfiles = (\n\t\t\t\tCB0798F02F6A4B3C00B74BB8 /* MMKVHandler.h in CopyFiles */,\n\t\t\t\tCBE668A32D5C8746005C184E /* MiniPBCoder.h in CopyFiles */,\n\t\t\t\tCB0DCC46242B3856009AFE59 /* openssl_opensslconf.h in CopyFiles */,\n\t\t\t\tCB0DCC45242B384E009AFE59 /* openssl_md5.h in CopyFiles */,\n\t\t\t\tCB6D44C323B308D100F369AA /* MMKVLog.h in CopyFiles */,\n\t\t\t\tCB6F6C0823ACCFEE000351EA /* ScopedLock.hpp in CopyFiles */,\n\t\t\t\tCB95642923AB2FC100ACCD39 /* MMKVPredef.h in CopyFiles */,\n\t\t\t\tCB95642723AB2FB800ACCD39 /* PBUtility.h in CopyFiles */,\n\t\t\t\tCB95642823AB2FB800ACCD39 /* ThreadLock.h in CopyFiles */,\n\t\t\t\tCB95642523AB2FA800ACCD39 /* MMBuffer.h in CopyFiles */,\n\t\t\t\tCB95642423AB2F7200ACCD39 /* MMKV.h in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19069243D70BA001C82ED /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"include/$(PRODUCT_NAME)\";\n\t\t\tdstSubfolderSpec = 16;\n\t\t\tfiles = (\n\t\t\t\tCB0798F12F6A4B4200B74BB8 /* MMKVHandler.h in CopyFiles */,\n\t\t\t\tCBE668A42D5C9467005C184E /* MiniPBCoder.h in CopyFiles */,\n\t\t\t\tCBF1906A243D70BA001C82ED /* openssl_opensslconf.h in CopyFiles */,\n\t\t\t\tCBF1906B243D70BA001C82ED /* openssl_md5.h in CopyFiles */,\n\t\t\t\tCBF1906C243D70BA001C82ED /* MMKVLog.h in CopyFiles */,\n\t\t\t\tCBF1906D243D70BA001C82ED /* ScopedLock.hpp in CopyFiles */,\n\t\t\t\tCBF1906E243D70BA001C82ED /* MMKVPredef.h in CopyFiles */,\n\t\t\t\tCBF1906F243D70BA001C82ED /* PBUtility.h in CopyFiles */,\n\t\t\t\tCBF19070243D70BA001C82ED /* ThreadLock.h in CopyFiles */,\n\t\t\t\tCBF19071243D70BA001C82ED /* MMBuffer.h in CopyFiles */,\n\t\t\t\tCBF19072243D70BA001C82ED /* MMKV.h in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tCB0798EF2F6A4A6D00B74BB8 /* MMKVHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMKVHandler.h; sourceTree = \"<group>\"; };\n\t\tCB0DCC70242B57E5009AFE59 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = \"libc++.tbd\"; path = \"Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib/libc++.tbd\"; sourceTree = DEVELOPER_DIR; };\n\t\tCB30A3EC24987C32007171B1 /* CodedInputDataCrypt.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = CodedInputDataCrypt.cpp; sourceTree = \"<group>\"; };\n\t\tCB30A3ED24987C32007171B1 /* CodedInputDataCrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CodedInputDataCrypt.h; sourceTree = \"<group>\"; };\n\t\tCB30A41E2498CFB6007171B1 /* CodedInputDataCrypt_OSX.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = CodedInputDataCrypt_OSX.cpp; sourceTree = \"<group>\"; };\n\t\tCB467F862431D3ED00FD7421 /* MMKV_OSX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKV_OSX.h; sourceTree = \"<group>\"; };\n\t\tCB550CCA2488E3F20042CD20 /* KeyValueHolder.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = KeyValueHolder.cpp; sourceTree = \"<group>\"; };\n\t\tCB550CCB2488E3F20042CD20 /* KeyValueHolder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyValueHolder.h; sourceTree = \"<group>\"; };\n\t\tCB58B3FF23AB3035002457F1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };\n\t\tCB7C029924A0F65B008D77E6 /* MMKV_IO.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = MMKV_IO.cpp; sourceTree = \"<group>\"; };\n\t\tCB7C029E24A0FBC2008D77E6 /* MMKV_IO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKV_IO.h; sourceTree = \"<group>\"; };\n\t\tCB9563D823AB2D9500ACCD39 /* libMMKVCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMMKVCore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB9563E423AB2E9100ACCD39 /* CodedInputData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodedInputData.h; sourceTree = \"<group>\"; };\n\t\tCB9563EB23AB2E9100ACCD39 /* Checksum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Checksum.h; sourceTree = \"<group>\"; };\n\t\tCB9563EC23AB2E9100ACCD39 /* MMKVMetaInfo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MMKVMetaInfo.hpp; sourceTree = \"<group>\"; };\n\t\tCB9563ED23AB2E9100ACCD39 /* MMKV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMKV.h; sourceTree = \"<group>\"; };\n\t\tCB9563EE23AB2E9100ACCD39 /* MMKVPredef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMKVPredef.h; sourceTree = \"<group>\"; };\n\t\tCB9563EF23AB2E9100ACCD39 /* CodedOutputData.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = CodedOutputData.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F023AB2E9100ACCD39 /* MemoryFile.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MemoryFile.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F123AB2E9100ACCD39 /* MiniPBCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MiniPBCoder.h; sourceTree = \"<group>\"; };\n\t\tCB9563F223AB2E9100ACCD39 /* MMKV.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MMKV.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F323AB2E9100ACCD39 /* ScopedLock.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScopedLock.hpp; sourceTree = \"<group>\"; };\n\t\tCB9563F423AB2E9100ACCD39 /* MMKVLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMKVLog.h; sourceTree = \"<group>\"; };\n\t\tCB9563F523AB2E9100ACCD39 /* MiniPBCoder.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MiniPBCoder.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F623AB2E9100ACCD39 /* PBUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBUtility.h; sourceTree = \"<group>\"; };\n\t\tCB9563F723AB2E9100ACCD39 /* ThreadLock.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = ThreadLock.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MMBuffer.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563F923AB2E9100ACCD39 /* CodedInputData.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = CodedInputData.cpp; sourceTree = \"<group>\"; };\n\t\tCB9563FA23AB2E9100ACCD39 /* MMBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMBuffer.h; sourceTree = \"<group>\"; };\n\t\tCB9563FB23AB2E9100ACCD39 /* InterProcessLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InterProcessLock.h; sourceTree = \"<group>\"; };\n\t\tCB9563FD23AB2E9100ACCD39 /* AESCrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESCrypt.h; sourceTree = \"<group>\"; };\n\t\tCB9563FF23AB2E9100ACCD39 /* openssl_md32_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_md32_common.h; sourceTree = \"<group>\"; };\n\t\tCB95640023AB2E9100ACCD39 /* openssl_opensslconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_opensslconf.h; sourceTree = \"<group>\"; };\n\t\tCB95640123AB2E9100ACCD39 /* openssl_cfb128.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = openssl_cfb128.cpp; sourceTree = \"<group>\"; };\n\t\tCB95640223AB2E9100ACCD39 /* openssl_md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_md5.h; sourceTree = \"<group>\"; };\n\t\tCB95640323AB2E9100ACCD39 /* openssl_aes_core.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = openssl_aes_core.cpp; sourceTree = \"<group>\"; };\n\t\tCB95640523AB2E9100ACCD39 /* openssl_aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_aes.h; sourceTree = \"<group>\"; };\n\t\tCB95640623AB2E9100ACCD39 /* openssl_md5_locl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_md5_locl.h; sourceTree = \"<group>\"; };\n\t\tCB95640723AB2E9100ACCD39 /* openssl_md5_one.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = openssl_md5_one.cpp; sourceTree = \"<group>\"; };\n\t\tCB95640923AB2E9100ACCD39 /* openssl_md5_dgst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = openssl_md5_dgst.cpp; sourceTree = \"<group>\"; };\n\t\tCB95640A23AB2E9100ACCD39 /* openssl_aes_locl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_aes_locl.h; sourceTree = \"<group>\"; };\n\t\tCB95640B23AB2E9100ACCD39 /* AESCrypt.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = AESCrypt.cpp; sourceTree = \"<group>\"; };\n\t\tCB95640C23AB2E9100ACCD39 /* CodedOutputData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodedOutputData.h; sourceTree = \"<group>\"; };\n\t\tCB95640D23AB2E9100ACCD39 /* ThreadLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadLock.h; sourceTree = \"<group>\"; };\n\t\tCB95640E23AB2E9100ACCD39 /* MemoryFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryFile.h; sourceTree = \"<group>\"; };\n\t\tCB95640F23AB2E9100ACCD39 /* PBEncodeItem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PBEncodeItem.hpp; sourceTree = \"<group>\"; };\n\t\tCB95641023AB2E9100ACCD39 /* InterProcessLock.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = InterProcessLock.cpp; sourceTree = \"<group>\"; };\n\t\tCB95641123AB2E9100ACCD39 /* MMKVLog.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MMKVLog.cpp; sourceTree = \"<group>\"; };\n\t\tCB95641223AB2E9100ACCD39 /* PBUtility.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = PBUtility.cpp; sourceTree = \"<group>\"; };\n\t\tCBC7A01023C7231600CCC492 /* openssl_aesv8-armx.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = \"openssl_aesv8-armx.S\"; sourceTree = \"<group>\"; };\n\t\tCBC7A01223C7235000CCC492 /* openssl_arm_arch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = openssl_arm_arch.h; sourceTree = \"<group>\"; };\n\t\tCBD723BE23B5C22800D3CDAF /* MMKV_OSX.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = MMKV_OSX.cpp; sourceTree = \"<group>\"; };\n\t\tCBD723F223B5E59800D3CDAF /* MemoryFile_OSX.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp.preprocessed; path = MemoryFile_OSX.cpp; sourceTree = \"<group>\"; };\n\t\tCBD723F423B5E76200D3CDAF /* MiniPBCoder_OSX.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp.preprocessed; path = MiniPBCoder_OSX.cpp; sourceTree = \"<group>\"; };\n\t\tCBD723F623B5FD9E00D3CDAF /* CodedInputData_OSX.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp.preprocessed; path = CodedInputData_OSX.cpp; sourceTree = \"<group>\"; };\n\t\tCBF19076243D70BA001C82ED /* libMMKVCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMMKVCore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCBF3450323B4BABA00168AC7 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tCB9563D523AB2D9500ACCD39 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB0DCC6D242B5786009AFE59 /* libz.tbd in Frameworks */,\n\t\t\t\tCB58B40023AB3035002457F1 /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19066243D70BA001C82ED /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF19067243D70BA001C82ED /* libz.tbd in Frameworks */,\n\t\t\t\tCBF19068243D70BA001C82ED /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tCB58B3FE23AB3035002457F1 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB0DCC70242B57E5009AFE59 /* libc++.tbd */,\n\t\t\t\tCBF3450323B4BABA00168AC7 /* libz.tbd */,\n\t\t\t\tCB58B3FF23AB3035002457F1 /* Foundation.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB9563CF23AB2D9500ACCD39 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB9563FC23AB2E9100ACCD39 /* aes */,\n\t\t\t\tCB9563E523AB2E9100ACCD39 /* crc32 */,\n\t\t\t\tCB30A41E2498CFB6007171B1 /* CodedInputDataCrypt_OSX.cpp */,\n\t\t\t\tCB30A3EC24987C32007171B1 /* CodedInputDataCrypt.cpp */,\n\t\t\t\tCB30A3ED24987C32007171B1 /* CodedInputDataCrypt.h */,\n\t\t\t\tCBD723F623B5FD9E00D3CDAF /* CodedInputData_OSX.cpp */,\n\t\t\t\tCB9563F923AB2E9100ACCD39 /* CodedInputData.cpp */,\n\t\t\t\tCB9563E423AB2E9100ACCD39 /* CodedInputData.h */,\n\t\t\t\tCB9563EF23AB2E9100ACCD39 /* CodedOutputData.cpp */,\n\t\t\t\tCB95640C23AB2E9100ACCD39 /* CodedOutputData.h */,\n\t\t\t\tCB95641023AB2E9100ACCD39 /* InterProcessLock.cpp */,\n\t\t\t\tCB9563FB23AB2E9100ACCD39 /* InterProcessLock.h */,\n\t\t\t\tCB550CCB2488E3F20042CD20 /* KeyValueHolder.h */,\n\t\t\t\tCB550CCA2488E3F20042CD20 /* KeyValueHolder.cpp */,\n\t\t\t\tCBD723F223B5E59800D3CDAF /* MemoryFile_OSX.cpp */,\n\t\t\t\tCB9563F023AB2E9100ACCD39 /* MemoryFile.cpp */,\n\t\t\t\tCB95640E23AB2E9100ACCD39 /* MemoryFile.h */,\n\t\t\t\tCBD723F423B5E76200D3CDAF /* MiniPBCoder_OSX.cpp */,\n\t\t\t\tCB9563F523AB2E9100ACCD39 /* MiniPBCoder.cpp */,\n\t\t\t\tCB9563F123AB2E9100ACCD39 /* MiniPBCoder.h */,\n\t\t\t\tCB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */,\n\t\t\t\tCB9563FA23AB2E9100ACCD39 /* MMBuffer.h */,\n\t\t\t\tCB9563ED23AB2E9100ACCD39 /* MMKV.h */,\n\t\t\t\tCB9563F223AB2E9100ACCD39 /* MMKV.cpp */,\n\t\t\t\tCB7C029E24A0FBC2008D77E6 /* MMKV_IO.h */,\n\t\t\t\tCB7C029924A0F65B008D77E6 /* MMKV_IO.cpp */,\n\t\t\t\tCB467F862431D3ED00FD7421 /* MMKV_OSX.h */,\n\t\t\t\tCBD723BE23B5C22800D3CDAF /* MMKV_OSX.cpp */,\n\t\t\t\tCB0798EF2F6A4A6D00B74BB8 /* MMKVHandler.h */,\n\t\t\t\tCB95641123AB2E9100ACCD39 /* MMKVLog.cpp */,\n\t\t\t\tCB9563F423AB2E9100ACCD39 /* MMKVLog.h */,\n\t\t\t\tCB9563EC23AB2E9100ACCD39 /* MMKVMetaInfo.hpp */,\n\t\t\t\tCB9563EE23AB2E9100ACCD39 /* MMKVPredef.h */,\n\t\t\t\tCB95640F23AB2E9100ACCD39 /* PBEncodeItem.hpp */,\n\t\t\t\tCB95641223AB2E9100ACCD39 /* PBUtility.cpp */,\n\t\t\t\tCB9563F623AB2E9100ACCD39 /* PBUtility.h */,\n\t\t\t\tCB9563F323AB2E9100ACCD39 /* ScopedLock.hpp */,\n\t\t\t\tCB9563F723AB2E9100ACCD39 /* ThreadLock.cpp */,\n\t\t\t\tCB95640D23AB2E9100ACCD39 /* ThreadLock.h */,\n\t\t\t\tCB58B3FE23AB3035002457F1 /* Frameworks */,\n\t\t\t\tCB9563D923AB2D9500ACCD39 /* Products */,\n\t\t\t);\n\t\t\tindentWidth = 4;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB9563D923AB2D9500ACCD39 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB9563D823AB2D9500ACCD39 /* libMMKVCore.a */,\n\t\t\t\tCBF19076243D70BA001C82ED /* libMMKVCore.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB9563E523AB2E9100ACCD39 /* crc32 */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB9563EB23AB2E9100ACCD39 /* Checksum.h */,\n\t\t\t);\n\t\t\tpath = crc32;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB9563FC23AB2E9100ACCD39 /* aes */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB9563FD23AB2E9100ACCD39 /* AESCrypt.h */,\n\t\t\t\tCB95640B23AB2E9100ACCD39 /* AESCrypt.cpp */,\n\t\t\t\tCB9563FE23AB2E9100ACCD39 /* openssl */,\n\t\t\t);\n\t\t\tpath = aes;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB9563FE23AB2E9100ACCD39 /* openssl */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB95640323AB2E9100ACCD39 /* openssl_aes_core.cpp */,\n\t\t\t\tCB95640A23AB2E9100ACCD39 /* openssl_aes_locl.h */,\n\t\t\t\tCB95640523AB2E9100ACCD39 /* openssl_aes.h */,\n\t\t\t\tCBC7A01023C7231600CCC492 /* openssl_aesv8-armx.S */,\n\t\t\t\tCBC7A01223C7235000CCC492 /* openssl_arm_arch.h */,\n\t\t\t\tCB95640123AB2E9100ACCD39 /* openssl_cfb128.cpp */,\n\t\t\t\tCB95640923AB2E9100ACCD39 /* openssl_md5_dgst.cpp */,\n\t\t\t\tCB95640623AB2E9100ACCD39 /* openssl_md5_locl.h */,\n\t\t\t\tCB95640723AB2E9100ACCD39 /* openssl_md5_one.cpp */,\n\t\t\t\tCB95640223AB2E9100ACCD39 /* openssl_md5.h */,\n\t\t\t\tCB9563FF23AB2E9100ACCD39 /* openssl_md32_common.h */,\n\t\t\t\tCB95640023AB2E9100ACCD39 /* openssl_opensslconf.h */,\n\t\t\t);\n\t\t\tpath = openssl;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tCB9563D723AB2D9500ACCD39 /* MMKVCore */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB9563E123AB2D9500ACCD39 /* Build configuration list for PBXNativeTarget \"MMKVCore\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB9563D423AB2D9500ACCD39 /* Sources */,\n\t\t\t\tCB9563D523AB2D9500ACCD39 /* Frameworks */,\n\t\t\t\tCB9563D623AB2D9500ACCD39 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MMKVCore;\n\t\t\tproductName = Core;\n\t\t\tproductReference = CB9563D823AB2D9500ACCD39 /* libMMKVCore.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tCBF1904E243D70BA001C82ED /* MMKVWatchCore */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBF19073243D70BA001C82ED /* Build configuration list for PBXNativeTarget \"MMKVWatchCore\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBF1904F243D70BA001C82ED /* Sources */,\n\t\t\t\tCBF19066243D70BA001C82ED /* Frameworks */,\n\t\t\t\tCBF19069243D70BA001C82ED /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MMKVWatchCore;\n\t\t\tproductName = Core;\n\t\t\tproductReference = CBF19076243D70BA001C82ED /* libMMKVCore.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tCB9563D023AB2D9500ACCD39 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastUpgradeCheck = 1530;\n\t\t\t\tORGANIZATIONNAME = lingol;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tCB9563D723AB2D9500ACCD39 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = CB9563D323AB2D9500ACCD39 /* Build configuration list for PBXProject \"Core\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = CB9563CF23AB2D9500ACCD39;\n\t\t\tproductRefGroup = CB9563D923AB2D9500ACCD39 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tCB9563D723AB2D9500ACCD39 /* MMKVCore */,\n\t\t\t\tCBF1904E243D70BA001C82ED /* MMKVWatchCore */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tCB9563D423AB2D9500ACCD39 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB95641823AB2E9100ACCD39 /* ThreadLock.cpp in Sources */,\n\t\t\t\tCBD723F323B5E59800D3CDAF /* MemoryFile_OSX.cpp in Sources */,\n\t\t\t\tCB550CCC2488E3F20042CD20 /* KeyValueHolder.cpp in Sources */,\n\t\t\t\tCB95641623AB2E9100ACCD39 /* MMKV.cpp in Sources */,\n\t\t\t\tCB30A41F2498CFB7007171B1 /* CodedInputDataCrypt_OSX.cpp in Sources */,\n\t\t\t\tCBD723F523B5E76200D3CDAF /* MiniPBCoder_OSX.cpp in Sources */,\n\t\t\t\tCB95642023AB2E9100ACCD39 /* AESCrypt.cpp in Sources */,\n\t\t\t\tCB95641B23AB2E9100ACCD39 /* openssl_cfb128.cpp in Sources */,\n\t\t\t\tCB95641423AB2E9100ACCD39 /* CodedOutputData.cpp in Sources */,\n\t\t\t\tCB95641F23AB2E9100ACCD39 /* openssl_md5_dgst.cpp in Sources */,\n\t\t\t\tCB30A3EE24987C32007171B1 /* CodedInputDataCrypt.cpp in Sources */,\n\t\t\t\tCB95641523AB2E9100ACCD39 /* MemoryFile.cpp in Sources */,\n\t\t\t\tCB95641A23AB2E9100ACCD39 /* CodedInputData.cpp in Sources */,\n\t\t\t\tCB95642223AB2E9100ACCD39 /* MMKVLog.cpp in Sources */,\n\t\t\t\tCB95642323AB2E9100ACCD39 /* PBUtility.cpp in Sources */,\n\t\t\t\tCB95641723AB2E9100ACCD39 /* MiniPBCoder.cpp in Sources */,\n\t\t\t\tCB7C029A24A0F65B008D77E6 /* MMKV_IO.cpp in Sources */,\n\t\t\t\tCB95641E23AB2E9100ACCD39 /* openssl_md5_one.cpp in Sources */,\n\t\t\t\tCB95641C23AB2E9100ACCD39 /* openssl_aes_core.cpp in Sources */,\n\t\t\t\tCB95641923AB2E9100ACCD39 /* MMBuffer.cpp in Sources */,\n\t\t\t\tCBD723F723B5FD9E00D3CDAF /* CodedInputData_OSX.cpp in Sources */,\n\t\t\t\tCBC7A01123C7231600CCC492 /* openssl_aesv8-armx.S in Sources */,\n\t\t\t\tCBD723BF23B5C22800D3CDAF /* MMKV_OSX.cpp in Sources */,\n\t\t\t\tCB95642123AB2E9100ACCD39 /* InterProcessLock.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF1904F243D70BA001C82ED /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF19050243D70BA001C82ED /* ThreadLock.cpp in Sources */,\n\t\t\t\tCBF19051243D70BA001C82ED /* MemoryFile_OSX.cpp in Sources */,\n\t\t\t\tCB7589D3248E68F100A546B4 /* KeyValueHolder.cpp in Sources */,\n\t\t\t\tCBF19052243D70BA001C82ED /* MMKV.cpp in Sources */,\n\t\t\t\tCB30A4202498CFB7007171B1 /* CodedInputDataCrypt_OSX.cpp in Sources */,\n\t\t\t\tCBF19053243D70BA001C82ED /* MiniPBCoder_OSX.cpp in Sources */,\n\t\t\t\tCBF19054243D70BA001C82ED /* AESCrypt.cpp in Sources */,\n\t\t\t\tCBF19055243D70BA001C82ED /* openssl_cfb128.cpp in Sources */,\n\t\t\t\tCBF19056243D70BA001C82ED /* CodedOutputData.cpp in Sources */,\n\t\t\t\tCBF19057243D70BA001C82ED /* openssl_md5_dgst.cpp in Sources */,\n\t\t\t\tCB30A3EF24987C32007171B1 /* CodedInputDataCrypt.cpp in Sources */,\n\t\t\t\tCBF1905A243D70BA001C82ED /* MemoryFile.cpp in Sources */,\n\t\t\t\tCBF1905B243D70BA001C82ED /* CodedInputData.cpp in Sources */,\n\t\t\t\tCBF1905C243D70BA001C82ED /* MMKVLog.cpp in Sources */,\n\t\t\t\tCBF1905D243D70BA001C82ED /* PBUtility.cpp in Sources */,\n\t\t\t\tCBF1905E243D70BA001C82ED /* MiniPBCoder.cpp in Sources */,\n\t\t\t\tCB7C029B24A0F65B008D77E6 /* MMKV_IO.cpp in Sources */,\n\t\t\t\tCBF1905F243D70BA001C82ED /* openssl_md5_one.cpp in Sources */,\n\t\t\t\tCBF19060243D70BA001C82ED /* openssl_aes_core.cpp in Sources */,\n\t\t\t\tCBF19061243D70BA001C82ED /* MMBuffer.cpp in Sources */,\n\t\t\t\tCBF19062243D70BA001C82ED /* CodedInputData_OSX.cpp in Sources */,\n\t\t\t\tCBF19063243D70BA001C82ED /* openssl_aesv8-armx.S in Sources */,\n\t\t\t\tCBF19064243D70BA001C82ED /* MMKV_OSX.cpp in Sources */,\n\t\t\t\tCBF19065243D70BA001C82ED /* InterProcessLock.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tCB9563DF23AB2D9500ACCD39 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t\tXROS_DEPLOYMENT_TARGET = 1.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB9563E023AB2D9500ACCD39 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t\tXROS_DEPLOYMENT_TARGET = 1.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB9563E223AB2D9500ACCD39 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tGCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;\n\t\t\t\tGCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;\n\t\t\t\tGCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;\n\t\t\t\tGCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;\n\t\t\t\tGCC_WARN_SHADOW = YES;\n\t\t\t\tGCC_WARN_SIGN_COMPARE = YES;\n\t\t\t\tGCC_WARN_UNUSED_PARAMETER = NO;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = \"\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx appletvsimulator appletvos xrsimulator xros\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB9563E323AB2D9500ACCD39 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = \"NDEBUG=1\";\n\t\t\t\tGCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;\n\t\t\t\tGCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;\n\t\t\t\tGCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;\n\t\t\t\tGCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;\n\t\t\t\tGCC_WARN_SHADOW = YES;\n\t\t\t\tGCC_WARN_SIGN_COMPARE = YES;\n\t\t\t\tGCC_WARN_UNUSED_PARAMETER = NO;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = \"\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx appletvsimulator appletvos xrsimulator xros\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBF19074243D70BA001C82ED /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tGCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;\n\t\t\t\tGCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;\n\t\t\t\tGCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;\n\t\t\t\tGCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;\n\t\t\t\tGCC_WARN_SHADOW = YES;\n\t\t\t\tGCC_WARN_SIGN_COMPARE = YES;\n\t\t\t\tGCC_WARN_UNUSED_PARAMETER = NO;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = MMKVCore;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"watchsimulator watchos\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBF19075243D70BA001C82ED /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = \"NDEBUG=1\";\n\t\t\t\tGCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;\n\t\t\t\tGCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;\n\t\t\t\tGCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;\n\t\t\t\tGCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;\n\t\t\t\tGCC_WARN_SHADOW = YES;\n\t\t\t\tGCC_WARN_SIGN_COMPARE = YES;\n\t\t\t\tGCC_WARN_UNUSED_PARAMETER = NO;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\tPRODUCT_NAME = MMKVCore;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"watchsimulator watchos\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tCB9563D323AB2D9500ACCD39 /* Build configuration list for PBXProject \"Core\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB9563DF23AB2D9500ACCD39 /* Debug */,\n\t\t\t\tCB9563E023AB2D9500ACCD39 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB9563E123AB2D9500ACCD39 /* Build configuration list for PBXNativeTarget \"MMKVCore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB9563E223AB2D9500ACCD39 /* Debug */,\n\t\t\t\tCB9563E323AB2D9500ACCD39 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBF19073243D70BA001C82ED /* Build configuration list for PBXNativeTarget \"MMKVWatchCore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBF19074243D70BA001C82ED /* Debug */,\n\t\t\t\tCBF19075243D70BA001C82ED /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = CB9563D023AB2D9500ACCD39 /* Project object */;\n}\n"
  },
  {
    "path": "Core/Core.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:Core.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Core/Core.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Core/Core.xcodeproj/xcshareddata/xcschemes/Core.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB9563D723AB2D9500ACCD39\"\n               BuildableName = \"libMMKVCore.a\"\n               BlueprintName = \"MMKVCore\"\n               ReferencedContainer = \"container:Core.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB9563D723AB2D9500ACCD39\"\n            BuildableName = \"libMMKVCore.a\"\n            BlueprintName = \"MMKVCore\"\n            ReferencedContainer = \"container:Core.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Core/Core.xcodeproj/xcshareddata/xcschemes/MMKVWatchCore.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBF1904E243D70BA001C82ED\"\n               BuildableName = \"libMMKVCore.a\"\n               BlueprintName = \"MMKVWatchCore\"\n               ReferencedContainer = \"container:Core.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF1904E243D70BA001C82ED\"\n            BuildableName = \"libMMKVCore.a\"\n            BlueprintName = \"MMKVWatchCore\"\n            ReferencedContainer = \"container:Core.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Core/InterProcessLock.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"InterProcessLock.h\"\n#include \"MMKVLog.h\"\n\n#ifndef MMKV_WIN32\n#    include <sys/file.h>\n#endif\n\nnamespace mmkv {\n\nFileLock::~FileLock() {\n    if (isFileLockValid()) {\n        if (m_exclusiveLockCount > 0) {\n#ifdef MMKV_WIN32\n            // only win32 file lock requires double unlock\n            if (m_sharedLockCount > 0) {\n                platformUnLock(true);\n            }\n#endif\n            m_sharedLockCount = 0;\n            m_exclusiveLockCount = 0;\n            platformUnLock(false);\n        } else if (m_sharedLockCount > 0) {\n            m_sharedLockCount = 0;\n            platformUnLock(false);\n        }\n    }\n}\n\nbool FileLock::lock(LockType lockType) {\n    return doLock(lockType, true);\n}\n\nbool FileLock::try_lock(LockType lockType, bool *tryAgain) {\n    return doLock(lockType, false, tryAgain);\n}\n\nbool FileLock::doLock(LockType lockType, bool wait, bool *tryAgain) {\n    if (!isFileLockValid()) {\n        return false;\n    }\n    bool unLockFirstIfNeeded = false;\n\n    if (lockType == SharedLockType) {\n        // don't want shared-lock to break any existing locks\n        if (m_sharedLockCount > 0 || m_exclusiveLockCount > 0) {\n            m_sharedLockCount++;\n            return true;\n        }\n    } else {\n        // don't want exclusive-lock to break existing exclusive-locks\n        if (m_exclusiveLockCount > 0) {\n            m_exclusiveLockCount++;\n            return true;\n        }\n        // prevent deadlock\n        if (m_sharedLockCount > 0) {\n            unLockFirstIfNeeded = true;\n        }\n    }\n\n    auto ret = platformLock(lockType, wait, unLockFirstIfNeeded, tryAgain);\n    if (ret) {\n        if (lockType == SharedLockType) {\n            m_sharedLockCount++;\n        } else {\n            m_exclusiveLockCount++;\n        }\n    }\n    return ret;\n}\n\n#ifndef MMKV_WIN32\n\nstatic int32_t LockType2FlockType(LockType lockType) {\n    switch (lockType) {\n        case SharedLockType:\n            return LOCK_SH;\n        case ExclusiveLockType:\n            return LOCK_EX;\n    }\n    return LOCK_EX;\n}\n\nbool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain) {\n#    ifdef MMKV_ANDROID\n    if (m_useFcntlLock) {\n        return fcntlLock(lockType, wait, unLockFirstIfNeeded, tryAgain);\n    }\n#    endif\n    auto realLockType = LockType2FlockType(lockType);\n    auto cmd = wait ? realLockType : (realLockType | LOCK_NB);\n    if (unLockFirstIfNeeded) {\n        // try lock\n        auto ret = flock(m_fd, realLockType | LOCK_NB);\n        if (ret == 0) {\n            return true;\n        }\n        // let's be gentleman: unlock my shared-lock to prevent deadlock\n        ret = flock(m_fd, LOCK_UN);\n        if (ret != 0) {\n            MMKVError(\"fail to try unlock first fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n        }\n    }\n\n    auto ret = flock(m_fd, cmd);\n    if (ret != 0) {\n        if (tryAgain) {\n            *tryAgain = (errno == EWOULDBLOCK);\n        }\n        if (wait) {\n            MMKVError(\"fail to lock fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n        }\n        // try recover my shared-lock\n        if (unLockFirstIfNeeded) {\n            ret = flock(m_fd, LockType2FlockType(SharedLockType));\n            if (ret != 0) {\n                // let's hope this never happen\n                MMKVError(\"fail to recover shared-lock fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n            }\n        }\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool FileLock::platformUnLock(bool unlockToSharedLock) {\n#    ifdef MMKV_ANDROID\n    if (m_useFcntlLock) {\n        return fcntlUnLock(unlockToSharedLock);\n    }\n#    endif\n    int cmd = unlockToSharedLock ? LOCK_SH : LOCK_UN;\n    if (flock(m_fd, cmd) != 0) {\n        MMKVError(\"fail to unlock fd=%d, error:%d(%s)\", m_fd, errno, strerror(errno));\n        return false;\n    }\n    return true;\n}\n\n#endif // MMKV_WIN32\n\nbool FileLock::unlock(LockType lockType) {\n    if (!isFileLockValid()) {\n        return false;\n    }\n    bool unlockToSharedLock = false;\n\n    if (lockType == SharedLockType) {\n        if (m_sharedLockCount == 0) {\n            return false;\n        }\n        // don't want shared-lock to break any existing locks\n        if (m_sharedLockCount > 1 || m_exclusiveLockCount > 0) {\n            m_sharedLockCount--;\n            return true;\n        }\n    } else {\n        if (m_exclusiveLockCount == 0) {\n            return false;\n        }\n        if (m_exclusiveLockCount > 1) {\n            m_exclusiveLockCount--;\n            return true;\n        }\n        // restore shared-lock when all exclusive-locks are done\n        if (m_sharedLockCount > 0) {\n            unlockToSharedLock = true;\n        }\n    }\n\n    auto ret = platformUnLock(unlockToSharedLock);\n    if (ret) {\n        if (lockType == SharedLockType) {\n            m_sharedLockCount--;\n        } else {\n            m_exclusiveLockCount--;\n        }\n    }\n    return ret;\n}\n\nvoid FileLock::destroyAndUnLock() {\n    platformUnLock(false);\n\n    m_sharedLockCount = 0;\n    m_exclusiveLockCount = 0;\n    m_fd = MMKVFileHandleInvalidValue;\n}\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/InterProcessLock.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_INTERPROCESSLOCK_H\n#define MMKV_INTERPROCESSLOCK_H\n#ifdef __cplusplus\n\n#    include \"MMKVPredef.h\"\n\n#    include <fcntl.h>\n\nnamespace mmkv {\n\nenum LockType {\n    SharedLockType,\n    ExclusiveLockType,\n};\n\n// a recursive POSIX file-lock wrapper\n// handles lock upgrade & downgrade correctly\nclass FileLock {\n    MMKVFileHandle_t m_fd;\n    size_t m_sharedLockCount;\n    size_t m_exclusiveLockCount;\n\n    bool doLock(LockType lockType, bool wait, bool *tryAgain = nullptr);\n    bool platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain);\n    bool platformUnLock(bool unLockFirstIfNeeded);\n\n#    ifndef MMKV_WIN32\n    bool isFileLockValid() const { return m_fd >= 0; }\n#        ifdef MMKV_ANDROID\n    const bool m_useFcntlLock; // fcntl(F_OFD_SETLK)\n    const bool m_isAshmem; // fcntl(F_SETLK)\n    struct flock m_lockInfo;\n    bool fcntlLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain);\n    bool fcntlUnLock(bool unLockFirstIfNeeded);\n#        endif\n\n#    else  // defined(MMKV_WIN32)\n    OVERLAPPED m_overLapped;\n\n    bool isFileLockValid() const { return m_fd != INVALID_HANDLE_VALUE; }\n#    endif // MMKV_WIN32\n\npublic:\n#    ifndef MMKV_WIN32\n#        ifndef MMKV_ANDROID\n    explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0) {}\n#        else\n    // locking with pos & len only works in fcntl lock type\n    explicit FileLock(MMKVFileHandle_t fd, bool useFcntlLock = false, bool isAshmem = false, int64_t lockPos = 0, int64_t lockLen = 0);\n#        endif // MMKV_ANDROID\n#    else      // defined(MMKV_WIN32)\n    explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0), m_overLapped{} {}\n#    endif     // MMKV_WIN32\n    ~FileLock();\n\n    bool lock(LockType lockType);\n\n    bool try_lock(LockType lockType, bool *tryAgain);\n\n    bool unlock(LockType lockType);\n\n    // unlock all and destroy file lock\n    void destroyAndUnLock();\n\n    // just forbid it for possibly misuse\n    explicit FileLock(const FileLock &other) = delete;\n    FileLock &operator=(const FileLock &other) = delete;\n};\n\nclass InterProcessLock {\n    FileLock *m_fileLock;\n    LockType m_lockType;\n\npublic:\n    InterProcessLock(FileLock *fileLock, LockType lockType)\n        : m_fileLock(fileLock), m_lockType(lockType), m_enable(true) {\n        MMKV_ASSERT(m_fileLock);\n    }\n\n    bool m_enable;\n\n    void lock() {\n        if (m_enable) {\n            m_fileLock->lock(m_lockType);\n        }\n    }\n\n    bool try_lock(bool *tryAgain = nullptr) {\n        if (m_enable) {\n            return m_fileLock->try_lock(m_lockType, tryAgain);\n        }\n        return false;\n    }\n\n    void unlock() {\n        if (m_enable) {\n            m_fileLock->unlock(m_lockType);\n        }\n    }\n};\n\n} // namespace mmkv\n\n#endif // __cplusplus\n#endif // MMKV_INTERPROCESSLOCK_H\n"
  },
  {
    "path": "Core/InterProcessLock_Android.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"InterProcessLock.h\"\n\n#ifdef MMKV_ANDROID\n#    include \"MMKVLog.h\"\n#    include <sys/file.h>\n#    include <unistd.h>\n#    include <cassert>\n\nnamespace mmkv {\n\nFileLock::FileLock(MMKVFileHandle_t fd, bool useFcntlLock, bool isAshmem, int64_t lockPos, int64_t lockLen)\n    : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0), m_useFcntlLock(useFcntlLock), m_isAshmem(isAshmem),\n        m_lockInfo() {\n    m_lockInfo.l_type = F_WRLCK;\n    m_lockInfo.l_start = lockPos;\n    m_lockInfo.l_whence = SEEK_SET;\n    m_lockInfo.l_len = lockLen;\n    m_lockInfo.l_pid = 0;\n}\n\nstatic short LockType2FlockType(LockType lockType) {\n    switch (lockType) {\n        case SharedLockType:\n            return F_RDLCK;\n        case ExclusiveLockType:\n            return F_WRLCK;\n    }\n}\n\nbool FileLock::fcntlLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain) {\n    m_lockInfo.l_type = LockType2FlockType(lockType);\n    auto FcntlLockOp = m_isAshmem ? F_SETLK : F_OFD_SETLK;\n    auto FcntlLockOpW = m_isAshmem ? F_SETLKW : F_OFD_SETLKW;\n    if (unLockFirstIfNeeded) {\n        // try lock\n        auto ret = fcntl(m_fd, FcntlLockOp, &m_lockInfo);\n        if (ret == 0) {\n            return true;\n        }\n        // lets be gentleman: unlock my shared-lock to prevent deadlock\n        auto type = m_lockInfo.l_type;\n        m_lockInfo.l_type = F_UNLCK;\n        ret = fcntl(m_fd, FcntlLockOp, &m_lockInfo);\n        if (ret != 0) {\n            MMKVError(\"fail to try unlock first fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n        }\n        m_lockInfo.l_type = type;\n    }\n\n    int cmd = wait ? FcntlLockOpW : FcntlLockOp;\n    auto ret = fcntl(m_fd, cmd, &m_lockInfo);\n    if (ret != 0) {\n        if (tryAgain) {\n            *tryAgain = (errno == EAGAIN);\n        }\n        if (wait) {\n            MMKVError(\"fail to lock fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n        }\n        // try recover my shared-lock\n        if (unLockFirstIfNeeded) {\n            m_lockInfo.l_type = LockType2FlockType(SharedLockType);\n            ret = fcntl(m_fd, cmd, &m_lockInfo);\n            if (ret != 0) {\n                // let's hope this never happen\n                MMKVError(\"fail to recover shared-lock fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n            }\n        }\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool FileLock::fcntlUnLock(bool unlockToSharedLock) {\n    m_lockInfo.l_type = static_cast<short>(unlockToSharedLock ? F_RDLCK : F_UNLCK);\n    auto FcntlLockOp = m_isAshmem ? F_SETLK : F_OFD_SETLK;\n    auto ret = fcntl(m_fd, FcntlLockOp, &m_lockInfo);\n    if (ret != 0) {\n        MMKVError(\"fail to unlock fd=%d, ret=%d, error:%s\", m_fd, ret, strerror(errno));\n        return false;\n    } else {\n        return true;\n    }\n}\n\n} // namespace mmkv\n\n#endif // MMKV_ANDROID\n"
  },
  {
    "path": "Core/InterProcessLock_Win32.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"InterProcessLock.h\"\n\n#ifdef MMKV_WIN32\n#    include \"MMKVLog.h\"\n\nnamespace mmkv {\n\nstatic DWORD LockType2Flag(LockType lockType) {\n    DWORD flag = 0;\n    switch (lockType) {\n        case SharedLockType:\n            flag = 0;\n            break;\n        case ExclusiveLockType:\n            flag = LOCKFILE_EXCLUSIVE_LOCK;\n            break;\n    }\n    return flag;\n}\n\nbool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded, bool *tryAgain) {\n    auto realLockType = LockType2Flag(lockType);\n    auto flag = wait ? realLockType : (realLockType | LOCKFILE_FAIL_IMMEDIATELY);\n    if (unLockFirstIfNeeded) {\n        /* try exclusive-lock above shared-lock will always fail in Win32\n\t\tauto ret = LockFileEx(m_fd, realLockType | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &m_overLapped);\n\t\tif (ret) {\n\t\t\treturn true;\n\t\t}*/\n        // let's be gentleman: unlock my shared-lock to prevent deadlock\n        auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);\n        if (!ret) {\n            auto lastError = GetLastError();\n            if (lastError != ERROR_NOT_LOCKED) {\n                MMKVError(\"fail to try unlock first fd=%p, error:%d\", m_fd, lastError);\n            }\n        }\n    }\n\n    auto ret = LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped);\n    if (!ret) {\n        if (tryAgain) {\n            *tryAgain = (GetLastError() == ERROR_LOCK_VIOLATION);\n        }\n        if (wait) {\n            MMKVError(\"fail to lock fd=%p, error:%d\", m_fd, GetLastError());\n        }\n        // try recover my shared-lock\n        if (unLockFirstIfNeeded) {\n            ret = LockFileEx(m_fd, LockType2Flag(SharedLockType), 0, 1, 0, &m_overLapped);\n            if (!ret) {\n                // let's hope this never happen\n                MMKVError(\"fail to recover shared-lock fd=%p, error:%d\", m_fd, GetLastError());\n            }\n        }\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool FileLock::platformUnLock(bool unlockToSharedLock) {\n    /* quote from MSDN:\n    * If the same range is locked with an exclusive and a shared lock,\n    * two unlock operations are necessary to unlock the region;\n    * the first unlock operation unlocks the exclusive lock,\n    * the second unlock operation unlocks the shared lock.\n    */\n    if (unlockToSharedLock) {\n        auto flag = LockType2Flag(SharedLockType);\n        if (!LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped)) {\n            MMKVError(\"fail to roll back to shared-lock, error:%d\", GetLastError());\n        }\n    }\n    auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);\n    if (!ret) {\n        auto lastError = GetLastError();\n        if (lastError != ERROR_NOT_LOCKED) {\n            MMKVError(\"fail to unlock fd=%p, error:%d\", m_fd, lastError);\n            return false;\n        }\n    }\n    return true;\n}\n\n} // namespace mmkv\n\n#endif // MMKV_WIN32\n"
  },
  {
    "path": "Core/KeyValueHolder.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"KeyValueHolder.h\"\n#include \"PBUtility.h\"\n#include \"aes/AESCrypt.h\"\n#include <cerrno>\n#include <cstring>\n#include <stdexcept>\n\nnamespace mmkv {\n\nKeyValueHolder::KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t off)\n    : keySize(static_cast<uint16_t>(keyLength)), valueSize(valueLength), offset(off) {\n    computedKVSize = keySize + static_cast<uint16_t>(pbRawVarint32Size(keySize));\n    computedKVSize += static_cast<uint16_t>(pbRawVarint32Size(valueSize));\n}\n\nMMBuffer KeyValueHolder::toMMBuffer(const void *basePtr) const {\n    auto realPtr = (uint8_t *) basePtr + offset;\n    realPtr += computedKVSize;\n    return MMBuffer(realPtr, valueSize, MMBufferNoCopy);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nKeyValueHolderCrypt::KeyValueHolderCrypt(const void *src, size_t length) {\n    if (length <= SmallBufferSize()) {\n        type = KeyValueHolderType_Direct;\n        paddedSize = static_cast<uint8_t>(length);\n        memcpy(paddedValue, src, length);\n    } else {\n        type = KeyValueHolderType_Memory;\n        memSize = static_cast<uint32_t>(length);\n        memPtr = malloc(length);\n        if (!memPtr) {\n            throw std::runtime_error(strerror(errno));\n        }\n        memcpy(memPtr, src, memSize);\n    }\n}\n\nKeyValueHolderCrypt::KeyValueHolderCrypt(MMBuffer &&data) {\n    if (data.type == MMBuffer::MMBufferType_Small) {\n        static_assert(SmallBufferSize() >= MMBuffer::SmallBufferSize(), \"KeyValueHolderCrypt can't hold MMBuffer\");\n\n        type = KeyValueHolderType_Direct;\n        paddedSize = static_cast<uint8_t>(data.length());\n        memcpy(paddedValue, data.getPtr(), data.length());\n    } else {\n        type = KeyValueHolderType_Memory;\n        memSize = static_cast<uint32_t>(data.length());\n#    ifdef MMKV_APPLE\n        if (data.m_data != nil) {\n            memPtr = malloc(memSize);\n            if (!memPtr) {\n                throw std::runtime_error(strerror(errno));\n            }\n            memcpy(memPtr, data.getPtr(), memSize);\n            return;\n        }\n#    endif\n        memPtr = data.getPtr();\n        data.detach();\n    }\n}\n\nKeyValueHolderCrypt::KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t off)\n    : type(KeyValueHolderType_Offset), keySize(static_cast<uint16_t>(keyLength)), valueSize(valueLength), offset(off) {\n\n    pbKeyValueSize = static_cast<uint8_t>(pbRawVarint32Size(keySize) + pbRawVarint32Size(valueSize));\n}\n\nKeyValueHolderCrypt::KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept {\n    this->move(std::move(other));\n}\n\nKeyValueHolderCrypt &KeyValueHolderCrypt::operator=(KeyValueHolderCrypt &&other) noexcept {\n    if (type == KeyValueHolderType_Memory && memPtr) {\n        free(memPtr);\n    }\n    this->move(std::move(other));\n    return *this;\n}\n\nvoid KeyValueHolderCrypt::move(KeyValueHolderCrypt &&other) noexcept {\n    if (other.type == KeyValueHolderType_Direct || other.type == KeyValueHolderType_Offset) {\n        memcpy(reinterpret_cast<void*>(this), &other, sizeof(other));\n    } else if (other.type == KeyValueHolderType_Memory) {\n        type = KeyValueHolderType_Memory;\n        memSize = other.memSize;\n        memPtr = other.memPtr;\n        other.memPtr = nullptr;\n    }\n}\n\nKeyValueHolderCrypt::~KeyValueHolderCrypt() {\n    if (type == KeyValueHolderType_Memory && memPtr) {\n        free(memPtr);\n    }\n}\n\nuint32_t KeyValueHolderCrypt::realValueSize() const {\n    switch (type) {\n        case KeyValueHolderType_Direct:\n            return paddedSize;\n        case KeyValueHolderType_Offset:\n            return valueSize;\n        case KeyValueHolderType_Memory:\n            return memSize;\n    }\n    return 0;\n}\n\n// get decrypt data with [position, -1)\nstatic MMBuffer decryptBuffer(AESCrypt &crypter, const MMBuffer &inputBuffer, size_t position) {\n    size_t smallBuffer[16 / sizeof(size_t)];\n    auto basePtr = (uint8_t *) inputBuffer.getPtr();\n    auto ptr = basePtr;\n    for (size_t index = sizeof(smallBuffer); index < position; index += sizeof(smallBuffer)) {\n        crypter.decrypt(ptr, smallBuffer, sizeof(smallBuffer));\n        ptr += sizeof(smallBuffer);\n    }\n    if (ptr < basePtr + position) {\n        crypter.decrypt(ptr, smallBuffer, static_cast<size_t>(basePtr + position - ptr));\n        ptr = basePtr + position;\n    }\n    size_t length = inputBuffer.length() - position;\n    MMBuffer tmp(length);\n\n    auto input = ptr;\n    auto output = tmp.getPtr();\n    crypter.decrypt(input, output, length);\n\n    return tmp;\n}\n\nMMBuffer KeyValueHolderCrypt::toMMBuffer(const void *basePtr, const AESCrypt *crypter) const {\n    if (type == KeyValueHolderType_Direct) {\n        return MMBuffer((void *) paddedValue, paddedSize, MMBufferNoCopy);\n    } else if (type == KeyValueHolderType_Memory) {\n        return MMBuffer(memPtr, memSize, MMBufferNoCopy);\n    } else {\n        auto realPtr = (uint8_t *) basePtr + offset;\n        auto position = static_cast<uint32_t>(pbKeyValueSize + keySize);\n        auto realSize = position + valueSize;\n        auto kvBuffer = MMBuffer(realPtr, realSize, MMBufferNoCopy);\n        auto decrypter = crypter->cloneWithStatus(cryptStatus);\n        return decryptBuffer(decrypter, kvBuffer, position);\n    }\n}\n\n#endif // MMKV_DISABLE_CRYPT\n\n} // namespace mmkv\n\n#if !defined(MMKV_DISABLE_CRYPT) && defined(MMKV_DEBUG)\n#    include \"CodedInputData.h\"\n#    include \"CodedOutputData.h\"\n#    include \"MMKVLog.h\"\n#    include <ctime>\n\nusing namespace std;\n\nnamespace mmkv {\n\nvoid KeyValueHolderCrypt::testAESToMMBuffer() {\n    const uint8_t plainText[] = \"Hello, OpenSSL-mmkv::KeyValueHolderCrypt::testAESToMMBuffer() with AES CFB 256.\";\n    constexpr size_t textLength = sizeof(plainText) - 1;\n\n    const uint8_t key[] = \"TheVeryLooooooooongAESKey\";\n    constexpr size_t keyLength = sizeof(key) - 1;\n    auto aes256 = (keyLength > AES_KEY_LEN);\n\n    uint8_t iv[AES_IV_LEN];\n    srand((unsigned) time(nullptr));\n    for (uint32_t i = 0; i < AES_IV_LEN; i++) {\n        iv[i] = (uint8_t) rand();\n    }\n    AESCrypt crypt1(key, keyLength, iv, sizeof(iv), aes256);\n\n    auto encryptText = new uint8_t[DEFAULT_MMAP_SIZE];\n    memset(encryptText, 0, DEFAULT_MMAP_SIZE);\n    CodedOutputData output(encryptText, DEFAULT_MMAP_SIZE);\n    output.writeData(MMBuffer((void *) key, keyLength, MMBufferNoCopy));\n    auto lengthOfValue = textLength + pbRawVarint32Size((uint32_t) textLength);\n    output.writeRawVarint32((int32_t) lengthOfValue);\n    output.writeData(MMBuffer((void *) plainText, textLength, MMBufferNoCopy));\n    crypt1.encrypt(encryptText, encryptText, (size_t)(output.curWritePointer() - encryptText));\n\n    AESCrypt decrypt(key, keyLength, iv, sizeof(iv), aes256);\n    uint8_t smallBuffer[32];\n    decrypt.decrypt(encryptText, smallBuffer, 5);\n    auto keySize = CodedInputData(smallBuffer, 5).readUInt32();\n    auto sizeOfKeySize = pbRawVarint32Size(keySize);\n    auto position = sizeOfKeySize;\n    decrypt.decrypt(encryptText + 5, smallBuffer + 5, static_cast<size_t>(sizeOfKeySize + keySize - 5));\n    position += keySize;\n    decrypt.decrypt(encryptText + position, smallBuffer + position, 5);\n    auto valueSize = CodedInputData(smallBuffer + position, 5).readUInt32();\n    // auto sizeOfValueSize = pbRawVarint32Size(valueSize);\n    KeyValueHolderCrypt kvHolder(keySize, valueSize, 0);\n    auto rollbackSize = position + 5;\n    decrypt.statusBeforeDecrypt(encryptText + rollbackSize, smallBuffer + rollbackSize, rollbackSize,\n                                kvHolder.cryptStatus);\n    auto value = kvHolder.toMMBuffer(encryptText, &decrypt);\n#    ifdef MMKV_APPLE\n    MMKVInfo(\"testAESToMMBuffer: %@\", CodedInputData((char *) value.getPtr(), value.length()).readNSString());\n#    else\n    MMKVInfo(\"testAESToMMBuffer: %s\", CodedInputData((char *) value.getPtr(), value.length()).readString().c_str());\n#    endif\n    MMKVInfo(\"MMBuffer::SmallBufferSize() = %u, KeyValueHolderCrypt::SmallBufferSize() = %u\",\n             MMBuffer::SmallBufferSize(), KeyValueHolderCrypt::SmallBufferSize());\n\n    delete[] encryptText;\n}\n\n} // namespace mmkv\n\n#endif\n"
  },
  {
    "path": "Core/KeyValueHolder.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef KeyValueHolder_hpp\n#define KeyValueHolder_hpp\n#ifdef __cplusplus\n\n#include \"MMBuffer.h\"\n#include \"aes/AESCrypt.h\"\n\nnamespace mmkv {\n\n#pragma pack(push, 1)\n\nstruct KeyValueHolder {\n    uint16_t computedKVSize; // internal use only\n    uint16_t keySize;\n    uint32_t valueSize;\n    uint32_t offset;\n\n    KeyValueHolder() = default;\n    KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t offset);\n\n    MMBuffer toMMBuffer(const void *basePtr) const;\n};\n\n#ifndef MMKV_DISABLE_CRYPT\n\nenum KeyValueHolderType : uint8_t {\n    KeyValueHolderType_Direct, // store value directly\n    KeyValueHolderType_Memory, // store value in the heap memory\n    KeyValueHolderType_Offset, // store value by offset\n};\n\n// kv holder for encrypted mmkv\nstruct KeyValueHolderCrypt {\n    KeyValueHolderType type = KeyValueHolderType_Direct;\n\n    union {\n        // store value by offset\n        struct {\n            uint8_t pbKeyValueSize; // size needed to encode keySize & valueSize\n            uint16_t keySize;\n            uint32_t valueSize;\n            uint32_t offset;\n            AESCryptStatus cryptStatus;\n        };\n        // store value directly\n        struct {\n            uint8_t paddedSize;\n            uint8_t paddedValue[1];\n        };\n        // store value in the heap memory\n        struct {\n            uint32_t memSize;\n            void *memPtr;\n        };\n    };\n\n    static constexpr size_t SmallBufferSize() {\n        return sizeof(KeyValueHolderCrypt) - offsetof(KeyValueHolderCrypt, paddedValue);\n    }\n\n    static constexpr size_t MediumBufferSize() {\n        return 256;\n    }\n\n    static bool isValueStoredAsOffset(size_t valueSize) { return valueSize > MediumBufferSize(); }\n\n    KeyValueHolderCrypt() = default;\n    KeyValueHolderCrypt(const void *valuePtr, size_t valueLength);\n    explicit KeyValueHolderCrypt(MMBuffer &&data);\n    KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t offset);\n\n    KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept;\n    KeyValueHolderCrypt &operator=(KeyValueHolderCrypt &&other) noexcept;\n    void move(KeyValueHolderCrypt &&other) noexcept;\n\n    ~KeyValueHolderCrypt();\n\n    uint32_t realValueSize() const;\n\n    MMBuffer toMMBuffer(const void *basePtr, const AESCrypt *crypter) const;\n\n    std::tuple<uint32_t, uint32_t, AESCryptStatus *> toTuple() {\n        return std::make_tuple(offset, pbKeyValueSize + keySize + valueSize, &cryptStatus);\n    }\n\n    // those are expensive, just forbid it for possibly misuse\n    explicit KeyValueHolderCrypt(const KeyValueHolderCrypt &other) = delete;\n    KeyValueHolderCrypt &operator=(const KeyValueHolderCrypt &other) = delete;\n\n#ifdef MMKV_DEBUG\n    static void testAESToMMBuffer();\n#endif\n};\n\n#endif // MMKV_DISABLE_CRYPT\n\n#pragma pack(pop)\n\n} // namespace mmkv\n\n#endif\n#endif /* KeyValueHolder_hpp */\n"
  },
  {
    "path": "Core/MMBuffer.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define NOMINMAX // undefine max/min\n\n#include \"MMBuffer.h\"\n#include <cerrno>\n#include <cstdlib>\n#include <cstring>\n#include <utility>\n#include <stdexcept>\n#include <algorithm>\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif\n\nusing namespace std;\n\nnamespace mmkv {\n\nMMBuffer::MMBuffer(size_t length) {\n    if (length > SmallBufferSize()) {\n        type = MMBufferType_Normal;\n        isNoCopy = MMBufferCopy;\n        size = length;\n        ptr = malloc(size);\n        if (!ptr) {\n            throw std::runtime_error(strerror(errno));\n        }\n#ifdef MMKV_APPLE\n        m_data = nil;\n#endif\n    } else {\n        type = MMBufferType_Small;\n        paddedSize = static_cast<uint8_t>(length);\n    }\n}\n\nMMBuffer::MMBuffer(void *source, size_t length, MMBufferCopyFlag flag) : isNoCopy(flag) {\n    if (isNoCopy == MMBufferCopy) {\n        if (length > SmallBufferSize()) {\n            type = MMBufferType_Normal;\n            size = length;\n            ptr = malloc(size);\n            if (!ptr) {\n                throw std::runtime_error(strerror(errno));\n            }\n            memcpy(ptr, source, size);\n#ifdef MMKV_APPLE\n            m_data = nil;\n#endif\n        } else {\n            type = MMBufferType_Small;\n            paddedSize = static_cast<uint8_t>(length);\n            memcpy(paddedBuffer, source, length);\n        }\n    } else {\n        type = MMBufferType_Normal;\n        size = length;\n        ptr = source;\n#ifdef MMKV_APPLE\n        m_data = nil;\n#endif\n    }\n}\n\nbool MMBuffer::operator==(const MMBuffer& other) const {\n    if (this->length() != other.length()) {\n        return false;\n    }\n    return !memcmp((uint8_t*)this->getPtr(), (uint8_t*)other.getPtr(), this->length());\n}\n\n#ifdef MMKV_APPLE\nMMBuffer::MMBuffer(NSData *data, MMBufferCopyFlag flag)\n    : type(MMBufferType_Normal), ptr((void *) data.bytes), size(data.length), isNoCopy(flag) {\n    if (isNoCopy == MMBufferCopy) {\n        m_data = [data retain];\n    } else {\n        m_data = data;\n    }\n}\n#endif\n\nMMBuffer::MMBuffer(MMBuffer &&other) noexcept : type(other.type) {\n    if (type == MMBufferType_Normal) {\n        size = other.size;\n        ptr = other.ptr;\n        isNoCopy = other.isNoCopy;\n#ifdef MMKV_APPLE\n        m_data = other.m_data;\n#endif\n        other.detach();\n    } else {\n        paddedSize = other.paddedSize;\n        memcpy(paddedBuffer, other.paddedBuffer, paddedSize);\n    }\n}\n\nMMBuffer::MMBuffer(MMBuffer &&other, size_t length) noexcept : type(other.type) {\n    if (type == MMBufferType_Normal) {\n        size = std::min(other.size, length);\n        ptr = other.ptr;\n        isNoCopy = other.isNoCopy;\n#ifdef MMKV_APPLE\n        m_data = other.m_data;\n#endif\n        other.detach();\n    } else {\n        paddedSize = std::min(other.paddedSize, static_cast<uint8_t>(length));\n        memcpy(paddedBuffer, other.paddedBuffer, paddedSize);\n    }\n}\n\nMMBuffer &MMBuffer::operator=(MMBuffer &&other) noexcept {\n    if (type == MMBufferType_Normal) {\n        if (other.type == MMBufferType_Normal) {\n            std::swap(isNoCopy, other.isNoCopy);\n            std::swap(size, other.size);\n            std::swap(ptr, other.ptr);\n#ifdef MMKV_APPLE\n            std::swap(m_data, other.m_data);\n#endif\n        } else {\n            type = MMBufferType_Small;\n            if (isNoCopy == MMBufferCopy) {\n#ifdef MMKV_APPLE\n                if (m_data) {\n                    [m_data release];\n                } else if (ptr) {\n                    free(ptr);\n                }\n#else\n                if (ptr) {\n                    free(ptr);\n                }\n#endif\n            }\n            paddedSize = other.paddedSize;\n            memcpy(paddedBuffer, other.paddedBuffer, paddedSize);\n        }\n    } else {\n        if (other.type == MMBufferType_Normal) {\n            type = MMBufferType_Normal;\n            isNoCopy = other.isNoCopy;\n            size = other.size;\n            ptr = other.ptr;\n#ifdef MMKV_APPLE\n            m_data = other.m_data;\n#endif\n            other.detach();\n        } else {\n            paddedSize = other.paddedSize;\n            memcpy(paddedBuffer, other.paddedBuffer, other.paddedSize);\n        }\n    }\n\n    return *this;\n}\n\nMMBuffer::~MMBuffer() {\n    if (isStoredOnStack()) {\n        return;\n    }\n\n#ifdef MMKV_APPLE\n    if (m_data) {\n        if (isNoCopy == MMBufferCopy) {\n            [m_data release];\n        }\n        return;\n    }\n#endif\n\n    if (isNoCopy == MMBufferCopy && ptr) {\n        free(ptr);\n    }\n}\n\nvoid MMBuffer::detach() {\n    // type = MMBufferType_Small;\n    // paddedSize = 0;\n    auto memsetPtr = (size_t *) &type;\n    *memsetPtr = 0;\n}\n\n#ifdef MMKV_APPLE\nNSData *MMBuffer::toNSData(bool transferOwnerShip) {\n    if (!transferOwnerShip) {\n        if (m_data) {\n            return m_data;\n        } else {\n            return [NSData dataWithBytesNoCopy:getPtr() length:length() freeWhenDone:NO];\n        }\n    }\n    if (m_data != nil) {\n        if (isNoCopy == MMBufferNoCopy) {\n            return m_data;\n        } else {\n            auto result = [m_data autorelease];\n            m_data = nil;\n            return result;\n        }\n    }\n    if (isStoredOnStack()) {\n        return [NSData dataWithBytes:getPtr() length:length()];\n    } else {\n        if (isNoCopy == MMBufferNoCopy) {\n            return [NSData dataWithBytesNoCopy:getPtr() length:length() freeWhenDone:NO];\n        } else {\n            auto result = [NSData dataWithBytesNoCopy:getPtr() length:length()];\n            detach();\n            return result;\n        }\n    }\n}\n#endif\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/MMBuffer.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MMBUFFER_H\n#define MMKV_MMBUFFER_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include <cstdint>\n#include <cstdlib>\n#include <cstddef>\n\nnamespace mmkv {\n\nenum MMBufferCopyFlag : bool {\n    MMBufferCopy = false,\n    MMBufferNoCopy = true,\n};\n\n#pragma pack(push, 1)\n\n#ifndef MMKV_DISABLE_CRYPT\nstruct KeyValueHolderCrypt;\n#endif\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\nusing NSDataType = NSData;\n#else\nusing NSDataType = void;\n#endif\n#endif // MMKV_APPLE\n\nclass MMKV_EXPORT MMBuffer {\n    enum MMBufferType : uint8_t {\n        MMBufferType_Small,  // store small buffer in stack memory\n        MMBufferType_Normal, // store in heap memory\n    };\n    MMBufferType type;\n\n    union {\n        struct {\n            MMBufferCopyFlag isNoCopy;\n            size_t size;\n            void *ptr;\n#ifdef MMKV_APPLE\n            NSDataType *m_data;\n#endif\n        };\n        struct {\n            uint8_t paddedSize;\n            // make at least 10 bytes to hold all primitive types (negative int32, int64, double etc.) on 32 bit device\n            // on 64 bit device it's guaranteed larger than 10 bytes\n            uint8_t paddedBuffer[10];\n        };\n    };\n\n    static constexpr size_t SmallBufferSize() {\n        return sizeof(MMBuffer) - offsetof(MMBuffer, paddedBuffer);\n    }\n\npublic:\n    explicit MMBuffer(size_t length = 0);\n    MMBuffer(void *source, size_t length, MMBufferCopyFlag flag = MMBufferCopy);\n#ifdef MMKV_APPLE\n    explicit MMBuffer(NSDataType *data, MMBufferCopyFlag flag = MMBufferCopy);\n\n    NSDataType *toNSData(bool transferOwnerShip);\n#endif\n\n    MMBuffer(MMBuffer &&other) noexcept;\n    MMBuffer(MMBuffer &&other, size_t length) noexcept;\n    MMBuffer &operator=(MMBuffer &&other) noexcept;\n\n    ~MMBuffer();\n\n    bool isStoredOnStack() const { return (type == MMBufferType_Small); }\n\n    void *getPtr() const { return isStoredOnStack() ? (void *) paddedBuffer : ptr; }\n\n    size_t length() const { return isStoredOnStack() ? paddedSize : size; }\n\n    // transfer ownership to others\n    void detach();\n\n    // compare two MMBuffer\n    bool operator==(const MMBuffer& other) const;\n\n    // those are expensive, just forbid it for possibly misuse\n    explicit MMBuffer(const MMBuffer &other) = delete;\n    MMBuffer &operator=(const MMBuffer &other) = delete;\n\n#ifndef MMKV_DISABLE_CRYPT\n    friend KeyValueHolderCrypt;\n#endif\n};\n\n#pragma pack(pop)\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_MMBUFFER_H\n"
  },
  {
    "path": "Core/MMKV.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"CodedInputData.h\"\n#include \"CodedOutputData.h\"\n#include \"InterProcessLock.h\"\n#include \"KeyValueHolder.h\"\n#include \"MMBuffer.h\"\n#include \"MMKVLog.h\"\n#include \"MMKVMetaInfo.hpp\"\n#include \"MMKV_IO.h\"\n#include \"MMKV_OSX.h\"\n#include \"MemoryFile.h\"\n#include \"MiniPBCoder.h\"\n#include \"PBUtility.h\"\n#include \"ScopedLock.hpp\"\n#include \"ThreadLock.h\"\n#include \"aes/AESCrypt.h\"\n#include \"aes/openssl/openssl_aes.h\"\n#include \"aes/openssl/openssl_md5.h\"\n#include \"crc32/Checksum.h\"\n#include <algorithm>\n#include <cstdio>\n#include <cstring>\n#include <unordered_set>\n#include <cassert>\n\n#if defined(__aarch64__) && defined(__linux__) && !defined (MMKV_OHOS)\n#    include <asm/hwcap.h>\n#    include <sys/auxv.h>\n#endif\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#    include \"MMKV_OSX.h\"\n#endif // MMKV_APPLE\n\nusing namespace std;\nusing namespace mmkv;\n\nunordered_map<string, MMKV *> *g_instanceDic;\nThreadLock *g_instanceLock;\nMMKVPath_t g_rootDir;\nMMKVPath_t g_realRootDir;\nstatic ThreadLock *g_namespaceLock;\nstatic unordered_map<MMKVPath_t, MMKVPath_t> g_realRootMap;\nsize_t mmkv::DEFAULT_MMAP_SIZE;\n\nMMKV_NAMESPACE_BEGIN\n\nstatic MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir);\nbool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix);\nMMKVPath_t filename(const MMKVPath_t &path);\n\n#ifndef MMKV_ANDROID\nMMKV::MMKV(const string &mmapID, const MMKVConfig &config)\n    : m_mmapID(mmapID)\n    , m_mode(config.mode)\n    , m_path(mappedKVPathWithID(m_mmapID, config.rootPath, true))\n    , m_crcPath(crcPathWithPath(m_path))\n    , m_dic(nullptr)\n    , m_dicCrypt(nullptr)\n    , m_expectedCapacity(std::max<size_t>(DEFAULT_MMAP_SIZE, roundUp<size_t>(config.expectedCapacity, DEFAULT_MMAP_SIZE)))\n    , m_file(new MemoryFile(m_path, m_expectedCapacity, isReadOnly(), true))\n    , m_metaFile(new MemoryFile(m_crcPath, 0, isReadOnly(), !isMultiProcess()))\n    , m_metaInfo(new MMKVMetaInfo())\n    , m_crypter(nullptr)\n    , m_lock(new ThreadLock())\n    , m_fileLock(new FileLock(isMultiProcess() ? m_metaFile->getFd() : MMKVFileHandleInvalidValue))\n    , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))\n    , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))\n{\n    m_actualSize = 0;\n    m_output = nullptr;\n\n#    ifndef MMKV_DISABLE_CRYPT\n    auto cryptKey = config.cryptKey;\n    if (cryptKey && !cryptKey->empty()) {\n        m_dicCrypt = new MMKVMapCrypt();\n        m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length(), nullptr, 0, config.aes256);\n    } else {\n        m_dic = new MMKVMap();\n    }\n#    else\n    m_dic = new MMKVMap();\n#    endif\n\n    m_needLoadFromFile = true;\n    m_hasFullWriteback = false;\n\n    m_crcDigest = 0;\n\n    m_lock->initialize();\n    m_sharedProcessLock->m_enable = isMultiProcess();\n    m_exclusiveProcessLock->m_enable = isMultiProcess();\n\n    m_recoverStrategic = config.recover;\n    m_itemSizeLimit = config.itemSizeLimit;\n\n    if (config.enableKeyExpire.has_value()) {\n        configAutoExipreIfNeeded(config);\n    }\n\n    if (config.enableCompareBeforeSet) {\n        enableCompareBeforeSet();\n    }\n}\n#endif\n\nMMKV::~MMKV() {\n    clearMemoryCache();\n\n    delete m_dic;\n#ifndef MMKV_DISABLE_CRYPT\n    delete m_dicCrypt;\n    delete m_crypter;\n#endif\n    delete m_metaInfo;\n    delete m_lock;\n    delete m_fileLock;\n    delete m_sharedProcessLock;\n    delete m_exclusiveProcessLock;\n#ifdef MMKV_ANDROID\n#ifndef MMKV_OHOS\n    delete m_sharedProcessModeLock;\n    delete m_exclusiveProcessModeLock;\n    delete m_fileModeLock;\n#endif // !MMKV_OHOS\n    delete m_sharedMigrationLock;\n    delete m_fileMigrationLock;\n#endif // MMKV_ANDROID\n    delete m_metaFile;\n    delete m_file;\n\n    MMKVInfo(\"destruct [%s]\", m_mmapID.c_str());\n}\n\nMMKV *MMKV::defaultMMKV(MMKVMode mode, const string *cryptKey, bool aes256) {\n    auto config = MMKVConfig();\n    config.mode = mode;\n    config.aes256 = aes256;\n    config.cryptKey = cryptKey;\n    return mmkvWithID(DEFAULT_MMAP_ID, config);\n}\n\nMMKV *MMKV::defaultMMKV(const MMKVConfig &config) {\n    return mmkvWithID(DEFAULT_MMAP_ID, config);\n}\n\nstatic void initialize() {\n    g_instanceDic = new unordered_map<string, MMKV *>;\n    g_instanceLock = new ThreadLock();\n    g_instanceLock->initialize();\n\n    mmkv::DEFAULT_MMAP_SIZE = mmkv::getPageSize();\n    MMKVInfo(\"version %s, page size %d, arch %s\", MMKV_VERSION, DEFAULT_MMAP_SIZE, MMKV_ABI);\n\n    // get CPU status of ARMv8 extensions (CRC32, AES)\n#if defined(__aarch64__) && defined(__linux__) && !defined (MMKV_OHOS)\n    auto hwcaps = getauxval(AT_HWCAP);\n#    ifndef MMKV_DISABLE_CRYPT\n    if (hwcaps & HWCAP_AES) {\n        openssl::AES_set_encrypt_key = openssl_aes_arm_set_encrypt_key;\n        openssl::AES_set_decrypt_key = openssl_aes_arm_set_decrypt_key;\n        openssl::AES_encrypt = openssl_aes_arm_encrypt;\n        openssl::AES_decrypt = openssl_aes_arm_decrypt;\n        MMKVInfo(\"armv8 AES instructions is supported\");\n    } else {\n        MMKVInfo(\"armv8 AES instructions is not supported\");\n    }\n#    endif // MMKV_DISABLE_CRYPT\n#    ifdef MMKV_USE_ARMV8_CRC32\n    if (hwcaps & HWCAP_CRC32) {\n        CRC32 = mmkv::armv8_crc32;\n        MMKVInfo(\"armv8 CRC32 instructions is supported\");\n    } else {\n        MMKVInfo(\"armv8 CRC32 instructions is not supported\");\n    }\n#    endif // MMKV_USE_ARMV8_CRC32\n#endif     // __aarch64__ && defined(__linux__) && !defined (MMKV_OHOS)\n\n#if defined(MMKV_DEBUG) && !defined(MMKV_DISABLE_CRYPT)\n    // AESCrypt::testAESCrypt();\n    // KeyValueHolderCrypt::testAESToMMBuffer();\n#endif\n}\n\nstatic void ensureMinimalInitialize() {\n    static ThreadOnceToken_t once_control = ThreadOnceUninitialized;\n    ThreadLock::ThreadOnce(&once_control, initialize);\n}\n\nvoid MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel, mmkv::MMKVHandler *handler) {\n    g_currentLogLevel = logLevel;\n    g_handler = handler;\n\n    ensureMinimalInitialize();\n\n#ifdef MMKV_APPLE\n    // crc32 instruction requires A10 chip, aka iPhone 7 or iPad 6th generation\n    int device = 0, version = 0;\n    GetAppleMachineInfo(device, version);\n    MMKVInfo(\"Apple Device: %d, version: %d\", device, version);\n#endif\n\n    if (g_rootDir.empty()) {\n        g_rootDir = rootDir;\n        // avoid operating g_realRootMap directly\n        g_realRootDir = nameSpace(rootDir).getRootDir();\n        mkPath(g_realRootDir);\n    }\n    const auto &rootDirStr = MMKVPath_t2String(g_realRootDir);\n    MMKVInfo(\"root dir: %s\", rootDirStr.c_str());\n}\n\nconst MMKVPath_t &MMKV::getRootDir() {\n    // for backword consistency we can't return g_realRootDir\n    return g_rootDir;\n}\n\n#ifndef MMKV_ANDROID\nMMKV *MMKV::getMMKVWithID(const std::string &mmapID, const MMKVConfig &config) {\n    if (mmapID.empty() || !g_instanceLock) {\n        return nullptr;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    auto rootPath = config.rootPath;\n    auto mmapKey = mmapedKVKey(mmapID, rootPath, true);\n    auto itr = g_instanceDic->find(mmapKey);\n    if (itr != g_instanceDic->end()) {\n        MMKV *kv = itr->second;\n        return kv;\n    }\n\n    if (rootPath && (rootPath != &g_realRootDir) && !(config.mode & MMKV_READ_ONLY)) {\n        MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n        if (!isFileExist(specialPath)) {\n            mkPath(specialPath);\n        }\n    }\n    auto theRootDir = rootPath ? rootPath : &g_realRootDir;\n    const auto &theRoot = MMKVPath_t2String(*theRootDir);\n    MMKVInfo(\"prepare to load %s (id %s) from rootPath %s\", mmapID.c_str(), mmapKey.c_str(), theRoot.c_str());\n\n    auto kv = new MMKV(mmapID, config);\n    kv->m_mmapKey = mmapKey;\n    (*g_instanceDic)[mmapKey] = kv;\n    return kv;\n}\n#endif\n\nMMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, const string *cryptKey, const MMKVPath_t *rootPath, size_t expectedCapacity, bool aes256) {\n    MMKVConfig config;\n    config.mode = mode;\n#ifndef MMKV_DISABLE_CRYPT\n    config.aes256 = aes256;\n    config.cryptKey = cryptKey;\n#endif\n    config.rootPath = rootPath;\n    config.expectedCapacity = expectedCapacity;\n\n    return mmkvWithID(mmapID, config);\n}\n\nMMKV *MMKV::mmkvWithID(const std::string &mmapID, const MMKVConfig &config) {\n    if (mmapID.empty() || !g_instanceLock) {\n        return nullptr;\n    }\n    auto ns = config.rootPath ? nameSpace(*config.rootPath) : defaultNameSpace();\n\n    auto newConfig = config;\n    newConfig.rootPath = &ns.m_rootDir;\n    return getMMKVWithID(mmapID, newConfig);\n}\n\nvoid MMKV::onExit() {\n    if (!g_instanceLock) {\n        return;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    for (auto &pair : *g_instanceDic) {\n        MMKV *kv = pair.second;\n        kv->sync();\n        kv->clearMemoryCache();\n        delete kv;\n        pair.second = nullptr;\n    }\n\n    delete g_instanceDic;\n    g_instanceDic = nullptr;\n}\n\nconst string &MMKV::mmapID() const {\n    return m_mmapID;\n}\n\nvoid MMKV::notifyContentChanged() {\n    if (g_handler) {\n        g_handler->onContentChangedByOuterProcess(m_mmapID);\n    }\n}\n\nvoid MMKV::notifyContentLoaded() {\n    if (g_handler) {\n        g_handler->onMMKVContentLoadSuccessfully(m_mmapID);\n    }\n}\n\nvoid MMKV::checkContentChanged() {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n}\n\nvoid MMKV::clearMemoryCache(bool keepSpace) {\n    SCOPED_LOCK(m_lock);\n    if (m_needLoadFromFile) {\n        return;\n    }\n    MMKVInfo(\"clearMemoryCache [%s]\", m_mmapID.c_str());\n    m_needLoadFromFile = true;\n    m_hasFullWriteback = false;\n\n    clearDictionary(m_dic);\n#ifndef MMKV_DISABLE_CRYPT\n    clearDictionary(m_dicCrypt);\n    if (m_crypter) {\n        if (m_metaInfo->m_version >= MMKVVersionRandomIV) {\n            m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));\n        } else {\n            m_crypter->resetIV();\n        }\n    }\n#endif\n\n    delete m_output;\n    m_output = nullptr;\n\n    if (!keepSpace) {\n        m_file->clearMemoryCache();\n    }\n    // inter-process lock rely on MetaFile's fd, never close it\n    // m_metaFile->clearMemoryCache();\n    m_actualSize = 0;\n    m_metaInfo->m_crcDigest = 0;\n}\n\nvoid MMKV::close() {\n    MMKVInfo(\"close [%s]\", m_mmapID.c_str());\n    SCOPED_LOCK(g_instanceLock);\n    m_lock->lock();\n\n    auto itr = g_instanceDic->find(m_mmapKey);\n    if (itr != g_instanceDic->end()) {\n        g_instanceDic->erase(itr);\n    }\n    delete this;\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nstring MMKV::cryptKey() const {\n    SCOPED_LOCK(m_lock);\n\n    if (m_crypter) {\n        char key[AES256_KEY_LEN];\n        m_crypter->getKey(key);\n        return {key, strnlen(key, AES256_KEY_LEN)};\n    }\n    return \"\";\n}\n\nvoid MMKV::checkReSetCryptKey(const string *cryptKey, bool aes256) {\n    SCOPED_LOCK(m_lock);\n\n    if (m_crypter) {\n        if (cryptKey && !cryptKey->empty()) {\n            string oldKey = this->cryptKey();\n            if (oldKey != *cryptKey) {\n                MMKVInfo(\"setting new aes key\");\n                delete m_crypter;\n                auto ptr = cryptKey->data();\n                m_crypter = new AESCrypt(ptr, cryptKey->length(), nullptr, 0, aes256);\n\n                checkLoadData();\n            } else {\n                // nothing to do\n            }\n        } else {\n            MMKVInfo(\"reset aes key\");\n            delete m_crypter;\n            m_crypter = nullptr;\n\n            checkLoadData();\n        }\n    } else {\n        if (cryptKey && !cryptKey->empty()) {\n            MMKVInfo(\"setting new aes key\");\n            auto ptr = cryptKey->data();\n            m_crypter = new AESCrypt(ptr, cryptKey->length(), nullptr, 0, aes256);\n\n            checkLoadData();\n        } else {\n            // nothing to do\n        }\n    }\n}\n\n#endif // MMKV_DISABLE_CRYPT\n\nbool MMKV::isFileValid() {\n    return m_file->isFileValid();\n}\n\n// crc\n\n// assuming m_file is valid\nbool MMKV::checkFileCRCValid(size_t actualSize, uint32_t crcDigest) {\n    auto ptr = (uint8_t *) m_file->getMemory();\n    if (ptr) {\n        m_crcDigest = (uint32_t) CRC32(0, (const uint8_t *) ptr + Fixed32Size, (uint32_t) actualSize);\n\n        if (m_crcDigest == crcDigest) {\n            return true;\n        }\n        MMKVError(\"check crc [%s] fail, crc32:%u, m_crcDigest:%u\", m_mmapID.c_str(), crcDigest, m_crcDigest);\n    }\n    return false;\n}\n\nvoid MMKV::recalculateCRCDigestWithIV(const void *iv) {\n    auto ptr = (const uint8_t *) m_file->getMemory();\n    if (ptr) {\n        m_crcDigest = 0;\n        m_crcDigest = (uint32_t) CRC32(0, ptr + Fixed32Size, (uint32_t) m_actualSize);\n        writeActualSize(m_actualSize, m_crcDigest, iv, IncreaseSequence);\n    }\n}\n\nvoid MMKV::recalculateCRCDigestOnly() {\n    auto ptr = (const uint8_t *) m_file->getMemory();\n    if (ptr) {\n        m_crcDigest = 0;\n        m_crcDigest = (uint32_t) CRC32(0, ptr + Fixed32Size, (uint32_t) m_actualSize);\n        writeActualSize(m_actualSize, m_crcDigest, nullptr, KeepSequence);\n    }\n}\n\nvoid MMKV::updateCRCDigest(const uint8_t *ptr, size_t length) {\n    if (ptr == nullptr) {\n        return;\n    }\n    m_crcDigest = (uint32_t) CRC32(m_crcDigest, ptr, (uint32_t) length);\n\n    writeActualSize(m_actualSize, m_crcDigest, nullptr, KeepSequence);\n}\n\n// set & get\n\nbool MMKV::set(bool value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(bool value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbBoolSize() : pbBoolSize();\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeBool(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(int32_t value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(int32_t value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbInt32Size(value) : pbInt32Size(value);\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeInt32(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(uint32_t value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(uint32_t value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbUInt32Size(value) : pbUInt32Size(value);\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeUInt32(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(int64_t value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(int64_t value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbInt64Size(value) : pbInt64Size(value);\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeInt64(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(uint64_t value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(uint64_t value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbUInt64Size(value) : pbUInt64Size(value);\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeUInt64(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(float value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(float value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbFloatSize() : pbFloatSize();\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeFloat(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::set(double value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(double value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    size_t size = mmkv_unlikely(m_enableKeyExpire) ? Fixed32Size + pbDoubleSize() : pbDoubleSize();\n    MMBuffer data(size);\n    CodedOutputData output(data.getPtr(), size);\n    output.writeDouble(value);\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n    } else {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n    }\n\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::setDataForKey(mmkv::MMBuffer &&data, MMKV::MMKVKey_t key, uint32_t expireDuration) {\n    if (mmkv_likely(!m_enableKeyExpire)) {\n        assert(expireDuration == ExpireNever && \"setting expire duration without calling enableAutoKeyExpire() first\");\n        return setDataForKey(std::move(data), key, true);\n    } else {\n        auto tmp = MMBuffer(pbMMBufferSize(data) + Fixed32Size);\n        CodedOutputData output(tmp.getPtr(), tmp.length());\n        output.writeData(data);\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        output.writeRawLittleEndian32(UInt32ToInt32(time));\n        return setDataForKey(std::move(tmp), key);\n    }\n}\n\nbool MMKV::set(const char *value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(const char *value, MMKVKey_t key, uint32_t expireDuration) {\n    if (!value) {\n        removeValueForKey(key);\n        return true;\n    }\n    return setDataForKey(MMBuffer((void *) value, strlen(value), MMBufferNoCopy), key, expireDuration);\n}\n\nbool MMKV::set(const string &value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(const string &value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    return setDataForKey(MMBuffer((void *) value.data(), value.length(), MMBufferNoCopy), key, expireDuration);\n}\n\nbool MMKV::set(string_view value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(string_view value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    return setDataForKey(MMBuffer((void *) value.data(), value.length(), MMBufferNoCopy), key, expireDuration);\n}\n\nbool MMKV::set(const MMBuffer &value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(const MMBuffer &value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    return setDataForKey(MMBuffer(value.getPtr(), value.length(), MMBufferNoCopy), key, expireDuration);\n}\n\nbool MMKV::set(const vector<string> &value, MMKVKey_t key) {\n    return set(value, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(const vector<string> &v, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n#ifdef MMKV_HAS_CPP20\n    auto data = MiniPBCoder::encodeDataWithObject(std::span(v));\n#else\n    auto data = MiniPBCoder::encodeDataWithObject(v);\n#endif\n    if (mmkv_unlikely(m_enableKeyExpire) && data.length() > 0) {\n        auto tmp = MMBuffer(data.length() + Fixed32Size);\n        auto ptr = (uint8_t *) tmp.getPtr();\n        memcpy(ptr, data.getPtr(), data.length());\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        memcpy(ptr + data.length(), &time, Fixed32Size);\n        data = std::move(tmp);\n    }\n    return setDataForKey(std::move(data), key);\n}\n\nbool MMKV::getString(MMKVKey_t key, string &result, bool inplaceModification) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (inplaceModification) {\n                input.readString(result);\n            } else {\n                result = input.readString();\n            }\n            return true;\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    return false;\n}\n\nbool MMKV::getBytes(MMKVKey_t key, mmkv::MMBuffer &result) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            result = input.readData();\n            return true;\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    return false;\n}\n\nMMBuffer MMKV::getBytes(MMKVKey_t key) {\n    if (isKeyEmpty(key)) {\n        return MMBuffer();\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            return input.readData();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    return MMBuffer();\n}\n\nbool MMKV::getVector(MMKVKey_t key, vector<string> &result) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            result = MiniPBCoder::decodeVector(data);\n            return true;\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    return false;\n}\n\nvoid MMKV::shared_lock() {\n    m_lock->lock();\n    m_sharedProcessLock->lock();\n}\n\nvoid MMKV::shared_unlock() {\n    m_sharedProcessLock->unlock();\n    m_lock->unlock();\n}\n\nbool MMKV::getBool(MMKVKey_t key, bool defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readBool();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nint32_t MMKV::getInt32(MMKVKey_t key, int32_t defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readInt32();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nuint32_t MMKV::getUInt32(MMKVKey_t key, uint32_t defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readUInt32();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nint64_t MMKV::getInt64(MMKVKey_t key, int64_t defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readInt64();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nuint64_t MMKV::getUInt64(MMKVKey_t key, uint64_t defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readUInt64();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nfloat MMKV::getFloat(MMKVKey_t key, float defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readFloat();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\ndouble MMKV::getDouble(MMKVKey_t key, double defaultValue, bool *hasValue) {\n    if (isKeyEmpty(key)) {\n        if (hasValue != nullptr) {\n            *hasValue = false;\n        }\n        return defaultValue;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            if (hasValue != nullptr) {\n                *hasValue = true;\n            }\n            return input.readDouble();\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    if (hasValue != nullptr) {\n        *hasValue = false;\n    }\n    return defaultValue;\n}\n\nsize_t MMKV::getValueSize(MMKVKey_t key, bool actualSize) {\n    if (isKeyEmpty(key)) {\n        return 0;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (actualSize) {\n        try {\n            CodedInputData input(data.getPtr(), data.length());\n            auto length = input.readInt32();\n            if (length >= 0) {\n                auto s_length = static_cast<size_t>(length);\n                if (pbRawVarint32Size(length) + s_length == data.length()) {\n                    return s_length;\n                }\n            }\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n    return data.length();\n}\n\nint32_t MMKV::writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size) {\n    if (isKeyEmpty(key) || size < 0) {\n        return -1;\n    }\n    auto s_size = static_cast<size_t>(size);\n\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    try {\n        CodedInputData input(data.getPtr(), data.length());\n        auto length = input.readInt32();\n        auto offset = pbRawVarint32Size(length);\n        if (length >= 0) {\n            auto s_length = static_cast<size_t>(length);\n            if (offset + s_length == data.length()) {\n                if (s_length <= s_size) {\n                    memcpy(ptr, (uint8_t *) data.getPtr() + offset, s_length);\n                    return length;\n                }\n            } else {\n                if (data.length() <= s_size) {\n                    memcpy(ptr, data.getPtr(), data.length());\n                    return static_cast<int32_t>(data.length());\n                }\n            }\n        }\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"encode fail\");\n    }\n    return -1;\n}\n\n// enumerate\n\nbool MMKV::containsKey(MMKVKey_t key) {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n\n    if (mmkv_likely(!m_enableKeyExpire)) {\n        if (m_crypter) {\n            return m_dicCrypt->find(key) != m_dicCrypt->end();\n        } else {\n            return m_dic->find(key) != m_dic->end();\n        }\n    }\n    auto raw = getDataWithoutMTimeForKey(key);\n    return raw.length() != 0;\n}\n\nsize_t MMKV::count(bool filterExpire) {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n\n    if (mmkv_unlikely(filterExpire && m_enableKeyExpire)) {\n        SCOPED_LOCK(m_exclusiveProcessLock);\n        fullWriteback(nullptr, true);\n    }\n\n    if (m_crypter) {\n        return m_dicCrypt->size();\n    } else {\n        return m_dic->size();\n    }\n}\n\nsize_t MMKV::totalSize() {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n    return m_file->getFileSize();\n}\n\nsize_t MMKV::actualSize() {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n    return m_actualSize;\n}\n\nbool MMKV::removeValueForKey(MMKVKey_t key) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n\n    return removeDataForKey(key);\n}\n\n#ifndef MMKV_APPLE\n\nvector<string> MMKV::allKeys(bool filterExpire) {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n\n    if (mmkv_unlikely(filterExpire && m_enableKeyExpire)) {\n        SCOPED_LOCK(m_exclusiveProcessLock);\n        fullWriteback(nullptr, true);\n    }\n\n    vector<string> keys;\n    if (m_crypter) {\n        for (const auto &itr : *m_dicCrypt) {\n            keys.push_back(itr.first);\n        }\n    } else {\n        for (const auto &itr : *m_dic) {\n            keys.push_back(itr.first);\n        }\n    }\n    return keys;\n}\n\nbool MMKV::removeValuesForKeys(const vector<string> &arrKeys) {\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    if (arrKeys.empty()) {\n        return true;\n    }\n    if (arrKeys.size() == 1) {\n        return removeValueForKey(arrKeys[0]);\n    }\n\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n\n    size_t deleteCount = 0;\n    if (m_crypter) {\n        for (const auto &key : arrKeys) {\n            auto itr = m_dicCrypt->find(key);\n            if (itr != m_dicCrypt->end()) {\n                m_dicCrypt->erase(itr);\n                deleteCount++;\n            }\n        }\n    } else {\n        for (const auto &key : arrKeys) {\n            auto itr = m_dic->find(key);\n            if (itr != m_dic->end()) {\n                m_dic->erase(itr);\n                deleteCount++;\n            }\n        }\n    }\n    if (deleteCount > 0) {\n        m_hasFullWriteback = false;\n\n        return fullWriteback();\n    }\n    return true;\n}\n\n#endif // MMKV_APPLE\n\n// file\n\nvoid MMKV::sync(SyncFlag flag) {\n    MMKVInfo(\"MMKV::sync, SyncFlag = %d\", flag);\n    SCOPED_LOCK(m_lock);\n    if (m_needLoadFromFile || !isFileValid()) {\n        return;\n    }\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    m_file->msync(flag);\n    m_metaFile->msync(flag);\n}\n\nvoid MMKV::lock() {\n    SCOPED_LOCK(m_lock);\n    m_exclusiveProcessLock->lock();\n}\nvoid MMKV::unlock() {\n    SCOPED_LOCK(m_lock);\n    m_exclusiveProcessLock->unlock();\n}\nbool MMKV::try_lock() {\n    SCOPED_LOCK(m_lock);\n    return m_exclusiveProcessLock->try_lock();\n}\n\n#ifndef MMKV_WIN32\nvoid MMKV::lock_thread() {\n    m_lock->lock();\n}\nvoid MMKV::unlock_thread() {\n    m_lock->unlock();\n}\nbool MMKV::try_lock_thread() {\n    return m_lock->try_lock();\n}\n#endif\n\n// backup\n\nstatic bool backupOneToDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    File crcFile(srcPath, OpenFlag::ReadOnly);\n    if (!crcFile.isFileValid()) {\n        return false;\n    }\n\n    bool ret;\n    {\n        const auto &dstUTF8Path = MMKVPath_t2String(dstPath);\n        MMKVInfo(\"backup one mmkv[%s] from [%s] to [%s]\", mmapKey.c_str(), crcFile.getUTF8Path().c_str(),\n                 dstUTF8Path.c_str());\n        FileLock fileLock(crcFile.getFd());\n        InterProcessLock lock(&fileLock, SharedLockType);\n        SCOPED_LOCK(&lock);\n\n        ret = copyFile(srcPath, dstPath);\n        if (ret) {\n            auto srcCRCPath = srcPath + CRC_SUFFIX;\n            auto dstCRCPath = dstPath + CRC_SUFFIX;\n            ret = copyFile(srcCRCPath, dstCRCPath);\n        }\n        MMKVInfo(\"finish backup one mmkv[%s]\", mmapKey.c_str());\n    }\n    return ret;\n}\n\nbool MMKV::backupOneToDirectory(const string &mmapKey, const MMKVPath_t &dstPath, const MMKVPath_t &srcPath, bool compareFullPath) {\n    if (!g_instanceLock) {\n        return false;\n    }\n    // we have to lock the creation of MMKV instance, regardless of in cache or not\n    SCOPED_LOCK(g_instanceLock);\n    MMKV *kv = nullptr;\n    if (!compareFullPath) {\n        auto itr = g_instanceDic->find(mmapKey);\n        if (itr != g_instanceDic->end()) {\n            kv = itr->second;\n        }\n    } else {\n        // mmapKey is actually filename, we can't simply call find()\n        for (auto &pair : *g_instanceDic) {\n            if (pair.second->m_path == srcPath) {\n                kv = pair.second;\n                break;\n            }\n        }\n    }\n    // get one in cache, do it the easy way\n    if (kv) {\n        const auto &srcUTF8Path = MMKVPath_t2String(srcPath);\n        const auto &dstUTF8Path = MMKVPath_t2String(dstPath);\n        MMKVInfo(\"backup one cached mmkv[%s] from [%s] to [%s]\", mmapKey.c_str(), srcUTF8Path.c_str(),\n                 dstUTF8Path.c_str());\n        SCOPED_LOCK(kv->m_lock);\n        SCOPED_LOCK(kv->m_sharedProcessLock);\n\n        kv->sync();\n        auto ret = copyFile(kv->m_path, dstPath);\n        if (ret) {\n            auto dstCRCPath = dstPath + CRC_SUFFIX;\n            ret = copyFile(kv->m_crcPath, dstCRCPath);\n        }\n        MMKVInfo(\"finish backup one mmkv[%s], ret: %d\", mmapKey.c_str(), ret);\n        return ret;\n    }\n\n    // no luck with cache, do it the hard way\n    bool ret = backupOneToDirectoryByFilePath(mmapKey, srcPath, dstPath);\n    return ret;\n}\n\nbool MMKV::backupOneToDirectory(const string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {\n    auto rootPath = srcDir ? srcDir : &g_realRootDir;\n    if (*rootPath == dstDir) {\n        return true;\n    }\n    mkPath(dstDir);\n    auto dstPath = mappedKVPathWithID(mmapID, &dstDir);\n    auto ns = nameSpace(*rootPath);\n    rootPath = &ns.getRootDir();\n    string  mmapKey = mmapedKVKey(mmapID, rootPath, true);\n#ifdef MMKV_ANDROID\n    string srcPath;\n    switch (tryMigrateLegacyMMKVFile(mmapID, rootPath, true)) {\n        case MigrateStatus::OldToNewMigrateFail: {\n            auto legacyID = legacyMmapedKVKey(mmapID, rootPath);\n            srcPath = mappedKVPathWithID(legacyID, rootPath, MMKV_MULTI_PROCESS, true);\n            break;\n        }\n        case MigrateStatus::NoneExist:\n            MMKVWarning(\"file with ID [%s] not exist in path [%s]\", mmapID.c_str(), rootPath->c_str());\n            return false;\n        default:\n            srcPath = mappedKVPathWithID(mmapID, rootPath, MMKV_MULTI_PROCESS, true);\n            break;\n    }\n#else\n    auto srcPath = mappedKVPathWithID(mmapID, rootPath, true);\n#endif\n    return backupOneToDirectory(mmapKey, dstPath, srcPath, false);\n}\n\nbool endsWith(const MMKVPath_t &str, const MMKVPath_t &suffix) {\n    if (str.length() >= suffix.length()) {\n        return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;\n    } else {\n        return false;\n    }\n}\n\nMMKVPath_t filename(const MMKVPath_t &path) {\n    auto startPos = path.rfind(MMKV_PATH_SLASH);\n    startPos++; // don't need to check for npos, because npos+1 == 0\n    auto filename = path.substr(startPos);\n    return filename;\n}\n\nsize_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t &srcDir, bool isInSpecialDir) {\n    unordered_set<MMKVPath_t> mmapIDSet;\n    unordered_set<MMKVPath_t> mmapIDCRCSet;\n    walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {\n        if (endsWith(filePath, CRC_SUFFIX)) {\n            mmapIDCRCSet.insert(filePath);\n        } else {\n            mmapIDSet.insert(filePath);\n        }\n    });\n\n    size_t count = 0;\n    if (!mmapIDSet.empty()) {\n        mkPath(dstDir);\n        auto compareFullPath = isInSpecialDir;\n        for (auto &srcPath : mmapIDSet) {\n            auto srcCRCPath = srcPath + CRC_SUFFIX;\n            if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {\n                const auto &utf8SrcCRCPath = MMKVPath_t2String(srcCRCPath);\n                MMKVWarning(\"crc not exist [%s]\", utf8SrcCRCPath.c_str());\n                continue;\n            }\n            auto basename = filename(srcPath);\n            const auto &strBasename = MMKVPath_t2String(basename);\n            auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &srcDir);\n            auto dstPath = dstDir + MMKV_PATH_SLASH;\n            dstPath += basename;\n            if (backupOneToDirectory(mmapKey, dstPath, srcPath, compareFullPath)) {\n                count++;\n            }\n        }\n    }\n    return count;\n}\n\nsize_t MMKV::backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t *srcDir) {\n    auto rootPath = srcDir ? srcDir : &g_realRootDir;\n    if (*rootPath == dstDir) {\n        return true;\n    }\n    auto count = backupAllToDirectory(dstDir, *rootPath, false);\n\n    auto specialSrcDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n    if (isFileExist(specialSrcDir)) {\n        auto specialDstDir = dstDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n        count += backupAllToDirectory(specialDstDir, specialSrcDir, true);\n    }\n    return count;\n}\n\n// restore\n\nstatic bool restoreOneFromDirectoryByFilePath(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    auto dstCRCPath = dstPath + CRC_SUFFIX;\n    File dstCRCFile(std::move(dstCRCPath), OpenFlag::ReadWrite | OpenFlag::Create);\n    if (!dstCRCFile.isFileValid()) {\n        return false;\n    }\n\n    bool ret;\n    {\n        const auto &srcUTF8Path = MMKVPath_t2String(srcPath);\n        const auto &dstUTF8Path = MMKVPath_t2String(dstPath);\n        MMKVInfo(\"restore one mmkv[%s] from [%s] to [%s]\", mmapKey.c_str(), srcUTF8Path.c_str(), dstUTF8Path.c_str());\n        FileLock fileLock(dstCRCFile.getFd());\n        InterProcessLock lock(&fileLock, ExclusiveLockType);\n        SCOPED_LOCK(&lock);\n\n        ret = copyFileContent(srcPath, dstPath);\n        if (ret) {\n            auto srcCRCPath = srcPath + CRC_SUFFIX;\n            ret = copyFileContent(srcCRCPath, dstCRCFile.getFd());\n        }\n        MMKVInfo(\"finish restore one mmkv[%s]\", mmapKey.c_str());\n    }\n    return ret;\n}\n\n// We can't simply replace the existing file, because other processes might have already open it.\n// They won't know a difference when the file has been replaced.\n// We have to let them know by overriding the existing file with new content.\nbool MMKV::restoreOneFromDirectory(const string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath, bool compareFullPath) {\n    if (!g_instanceLock) {\n        return false;\n    }\n    // we have to lock the creation of MMKV instance, regardless of in cache or not\n    SCOPED_LOCK(g_instanceLock);\n    MMKV *kv = nullptr;\n    if (!compareFullPath) {\n        auto itr = g_instanceDic->find(mmapKey);\n        if (itr != g_instanceDic->end()) {\n            kv = itr->second;\n        }\n    } else {\n        // mmapKey is actually filename, we can't simply call find()\n        for (auto &pair : *g_instanceDic) {\n            if (pair.second->m_path == dstPath) {\n                kv = pair.second;\n                break;\n            }\n        }\n    }\n    // get one in cache, do it the easy way\n    if (kv) {\n        const auto &srcUTF8Path = MMKVPath_t2String(srcPath);\n        const auto &dstUTF8Path = MMKVPath_t2String(dstPath);\n        MMKVInfo(\"restore one cached mmkv[%s] from [%s] to [%s]\", mmapKey.c_str(), srcUTF8Path.c_str(),\n                 dstUTF8Path.c_str());\n        SCOPED_LOCK(kv->m_lock);\n        SCOPED_LOCK(kv->m_exclusiveProcessLock);\n\n        kv->sync();\n        auto ret = copyFileContent(srcPath, kv->m_file->getFd());\n        kv->m_file->cleanMayflyFD();\n        if (ret) {\n            auto srcCRCPath = srcPath + CRC_SUFFIX;\n            // ret = copyFileContent(srcCRCPath, kv->m_metaFile->getFd());\n            // kv->m_metaFile->cleanMayflyFD();\n#ifndef MMKV_ANDROID\n            MemoryFile srcCRCFile(srcCRCPath);\n#else\n            MemoryFile srcCRCFile(srcCRCPath, MMFILE_TYPE_FILE);\n#endif\n            if (srcCRCFile.isFileValid()) {\n                memcpy(kv->m_metaFile->getMemory(), srcCRCFile.getMemory(), sizeof(MMKVMetaInfo));\n            } else {\n                ret = false;\n            }\n        }\n\n        // reload data after restore\n        kv->clearMemoryCache();\n        kv->loadFromFile();\n        if (kv->isMultiProcess()) {\n            kv->notifyContentChanged();\n        }\n\n        MMKVInfo(\"finish restore one mmkv[%s], ret: %d\", mmapKey.c_str(), ret);\n        return ret;\n    }\n\n    // no luck with cache, do it the hard way\n    bool ret = restoreOneFromDirectoryByFilePath(mmapKey, srcPath, dstPath);\n    return ret;\n}\n\nbool MMKV::restoreOneFromDirectory(const string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {\n    auto rootPath = dstDir ? dstDir : &g_realRootDir;\n    if (*rootPath == srcDir) {\n        return true;\n    }\n    mkPath(*rootPath);\n    auto ns = nameSpace(*rootPath);\n    rootPath = &ns.getRootDir();\n    auto mmapKey = mmapedKVKey(mmapID, rootPath, true);\n#ifdef MMKV_ANDROID\n    auto srcPath = mappedKVPathWithID(mmapID, &srcDir, MMKV_MULTI_PROCESS, true);\n    string dstPath;\n    if (tryMigrateLegacyMMKVFile(mmapID, rootPath, true) == MigrateStatus::OldToNewMigrateFail) {\n        auto legacyID = legacyMmapedKVKey(mmapID, rootPath);\n        dstPath = mappedKVPathWithID(legacyID, rootPath, MMKV_MULTI_PROCESS, true);\n    } else {\n        dstPath = mappedKVPathWithID(mmapID, rootPath, MMKV_MULTI_PROCESS, true);\n    }\n#else\n    auto srcPath = mappedKVPathWithID(mmapID, &srcDir, true);\n    auto dstPath = mappedKVPathWithID(mmapID, rootPath, true);\n#endif\n    return restoreOneFromDirectory(mmapKey, srcPath, dstPath, false);\n}\n\nsize_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t &dstDir, bool isInSpecialDir) {\n    unordered_set<MMKVPath_t> mmapIDSet;\n    unordered_set<MMKVPath_t> mmapIDCRCSet;\n    walkInDir(srcDir, WalkFile, [&](const MMKVPath_t &filePath, WalkType) {\n        if (endsWith(filePath, CRC_SUFFIX)) {\n            mmapIDCRCSet.insert(filePath);\n        } else {\n            mmapIDSet.insert(filePath);\n        }\n    });\n\n    size_t count = 0;\n    if (!mmapIDSet.empty()) {\n        mkPath(dstDir);\n        auto compareFullPath = isInSpecialDir;\n        for (auto &srcPath : mmapIDSet) {\n            auto srcCRCPath = srcPath + CRC_SUFFIX;\n            if (mmapIDCRCSet.find(srcCRCPath) == mmapIDCRCSet.end()) {\n                const auto &utf8SrcCRCPath = MMKVPath_t2String(srcCRCPath);\n                MMKVWarning(\"crc not exist [%s]\", utf8SrcCRCPath.c_str());\n                continue;\n            }\n            auto basename = filename(srcPath);\n            const auto &strBasename = MMKVPath_t2String(basename);\n            auto mmapKey = isInSpecialDir ? strBasename : mmapedKVKey(strBasename, &dstDir);\n            auto dstPath = dstDir + MMKV_PATH_SLASH;\n            dstPath += basename;\n            if (restoreOneFromDirectory(mmapKey, srcPath, dstPath, compareFullPath)) {\n                count++;\n            }\n        }\n    }\n    return count;\n}\n\nsize_t MMKV::restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t *dstDir) {\n    auto rootPath = dstDir ? dstDir : &g_realRootDir;\n    if (*rootPath == srcDir) {\n        return true;\n    }\n    auto count = restoreAllFromDirectory(srcDir, *rootPath, true);\n\n    auto specialSrcDir = srcDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n    if (isFileExist(specialSrcDir)) {\n        auto specialDstDir = *rootPath + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n        count += restoreAllFromDirectory(specialSrcDir, specialDstDir, false);\n    }\n    return count;\n}\n\n// callbacks\n\nvoid MMKV::registerHandler(mmkv::MMKVHandler *handler) {\n    if (!g_instanceLock) {\n        return;\n    }\n    SCOPED_LOCK(g_instanceLock);\n    g_handler = handler;\n}\n\nvoid MMKV::unRegisterHandler() {\n    if (!g_instanceLock) {\n        return;\n    }\n    SCOPED_LOCK(g_instanceLock);\n    g_handler = nullptr;\n}\n\nvoid MMKV::setLogLevel(MMKVLogLevel level) {\n    if (!g_instanceLock) {\n        return;\n    }\n    SCOPED_LOCK(g_instanceLock);\n    g_currentLogLevel = level;\n}\n\nstatic void mkSpecialCharacterFileDirectory() {\n    MMKVPath_t path = g_realRootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n    mkPath(path);\n}\n\ntemplate <typename T>\nstatic string md5(const basic_string<T> &value) {\n    uint8_t md[MD5_DIGEST_LENGTH] = {};\n    char tmp[3] = {}, buf[33] = {};\n    openssl::MD5((const uint8_t *) value.c_str(), value.size() * (sizeof(T) / sizeof(uint8_t)), md);\n    for (auto ch : md) {\n        snprintf(tmp, sizeof(tmp), \"%2.2x\", ch);\n        strcat(buf, tmp);\n    }\n    return {buf};\n}\n\nstatic MMKVPath_t encodeFilePath(const string &mmapID) {\n    const char *specialCharacters = \"\\\\/:*?\\\"<>|\";\n    string encodedID;\n    bool hasSpecialCharacter = false;\n    for (auto ch : mmapID) {\n        if (strchr(specialCharacters, ch) != nullptr) {\n            encodedID = md5(mmapID);\n            hasSpecialCharacter = true;\n            break;\n        }\n    }\n    if (hasSpecialCharacter) {\n        static ThreadOnceToken_t once = ThreadOnceUninitialized;\n        ThreadLock::ThreadOnce(&once, mkSpecialCharacterFileDirectory);\n        return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);\n    } else {\n        return string2MMKVPath_t(mmapID);\n    }\n}\n\nstatic MMKVPath_t encodeFilePath(const string &mmapID, const MMKVPath_t &rootDir) {\n    const char *specialCharacters = \"\\\\/:*?\\\"<>|\";\n    string encodedID;\n    bool hasSpecialCharacter = false;\n    for (auto ch : mmapID) {\n        if (strchr(specialCharacters, ch) != nullptr) {\n            encodedID = md5(mmapID);\n            hasSpecialCharacter = true;\n            break;\n        }\n    }\n    if (hasSpecialCharacter) {\n        MMKVPath_t path = rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n        mkPath(path);\n\n        return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);\n    } else {\n        return string2MMKVPath_t(mmapID);\n    }\n}\n\nstring mmapedKVKey(const string &mmapID, const MMKVPath_t *rootPath, bool alreadyAbsolute) {\n    MMKVPath_t path;\n    // compare by pointer to speedup a bit, it's OK false detecting\n    if (rootPath && (rootPath != &g_realRootDir)) {\n        auto tmp = *rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID);\n        if (alreadyAbsolute) {\n            path = std::move(tmp);\n        } else {\n            path = absolutePath(tmp);\n        }\n    } else {\n        path = g_realRootDir + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID);\n    }\n    return md5(path);\n}\n\nstring legacyMmapedKVKey(const string &mmapID, const MMKVPath_t *rootPath) {\n    if (rootPath && (*rootPath != g_rootDir)) {\n        return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));\n    }\n    return mmapID;\n}\n\n#ifndef MMKV_ANDROID\nMMKVPath_t mappedKVPathWithID(const string &mmapID, const MMKVPath_t *rootPath, bool alreadyAbsolute) {\n    if (rootPath && (rootPath != &g_realRootDir)) {\n        auto path = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID, *rootPath);\n        if (alreadyAbsolute) {\n            return path;\n        } else {\n            return absolutePath(path);\n        }\n    }\n    auto path = g_realRootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);\n    return path;\n}\n#else\nMMKVPath_t mappedKVPathWithID(const string &mmapID, const MMKVPath_t *rootPath, MMKVMode mode, bool alreadyAbsolute) {\n    if (mode & MMKV_ASHMEM) {\n        return ashmemMMKVPathWithID(encodeFilePath(mmapID));\n    } else if (rootPath && (rootPath != &g_realRootDir)) {\n        auto path = *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID, *rootPath);\n        if (alreadyAbsolute) {\n            return path;\n        } else {\n            return absolutePath(path);\n        }\n    }\n    auto path = g_realRootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);\n    return path;\n}\n#endif\n\nMMKVPath_t crcPathWithPath(const MMKVPath_t &kvPath) {\n    return kvPath + CRC_SUFFIX;\n}\n\nMMKVRecoverStrategic onMMKVCRCCheckFail(const string &mmapID) {\n    if (g_handler) {\n        return g_handler->onMMKVCRCCheckFail(mmapID);\n    }\n    return OnErrorDiscard;\n}\n\nMMKVRecoverStrategic onMMKVFileLengthError(const string &mmapID) {\n    if (g_handler) {\n        return g_handler->onMMKVFileLengthError(mmapID);\n    }\n    return OnErrorDiscard;\n}\n\n// NameSpace\n\nNameSpace MMKV::nameSpace(const MMKVPath_t &rootDir) {\n    if (!g_instanceLock) {\n        ensureMinimalInitialize();\n    }\n\n    static ThreadOnceToken_t once = ThreadOnceUninitialized;\n    ThreadLock::ThreadOnce(&once, []{\n        g_namespaceLock = new ThreadLock;\n        g_namespaceLock->initialize();\n    });\n    SCOPED_LOCK(g_namespaceLock);\n\n    auto itr = g_realRootMap.find(rootDir);\n    if (itr == g_realRootMap.end()) {\n        auto realRoot = absolutePath(rootDir);\n        if (realRoot.ends_with(MMKV_PATH_SLASH)) {\n            realRoot.erase(realRoot.size() - 1);\n        }\n        itr = g_realRootMap.emplace(rootDir, realRoot).first;\n    }\n    return NameSpace(itr->second);\n}\n\nNameSpace MMKV::defaultNameSpace() {\n    if (g_rootDir.empty()) {\n        MMKVWarning(\"MMKV has not been initialized, there's no default NameSpace.\");\n        return NameSpace(MMKVPath_t());\n    }\n    return NameSpace(g_realRootDir);\n}\n\nMMKV *NameSpace::mmkvWithID(const string &mmapID, MMKVMode mode, const string *cryptKey, size_t expectedCapacity, bool aes256) {\n    MMKVConfig config;\n    config.mode = mode;\n#ifndef MMKV_DISABLE_CRYPT\n    config.aes256 = aes256;\n    config.cryptKey = cryptKey;\n#endif\n    config.rootPath = &m_rootDir;\n    config.expectedCapacity = expectedCapacity;\n    return MMKV::getMMKVWithID(mmapID, config);\n}\n\nMMKV *NameSpace::mmkvWithID(const string &mmapID, const MMKVConfig &config) {\n    if (!config.rootPath || *config.rootPath != m_rootDir) {\n        auto newConfig = config;\n        newConfig.rootPath = &m_rootDir;\n        return MMKV::getMMKVWithID(mmapID, newConfig);\n    }\n    return MMKV::getMMKVWithID(mmapID, config);\n}\n\nbool NameSpace::backupOneToDirectory(const std::string &mmapID, const MMKVPath_t &dstDir) {\n    return MMKV::backupOneToDirectory(mmapID, dstDir, &m_rootDir);\n}\n\nbool NameSpace::restoreOneFromDirectory(const std::string &mmapID, const MMKVPath_t &srcDir) {\n    return MMKV::restoreOneFromDirectory(mmapID, srcDir, &m_rootDir);\n}\n\nsize_t NameSpace::backupAllToDirectory(const MMKVPath_t &dstDir) {\n    return MMKV::backupAllToDirectory(dstDir, &m_rootDir);\n}\n\nsize_t NameSpace::restoreAllFromDirectory(const MMKVPath_t &srcDir) {\n    return MMKV::restoreAllFromDirectory(srcDir, &m_rootDir);\n}\n\nbool NameSpace::isFileValid(const std::string &mmapID) {\n    return MMKV::isFileValid(mmapID, &m_rootDir);\n}\n\nbool NameSpace::removeStorage(const std::string &mmapID) {\n    return MMKV::removeStorage(mmapID, &m_rootDir);\n}\n\nbool NameSpace::checkExist(const std::string &mmapID) {\n    return MMKV::checkExist(mmapID, &m_rootDir);\n}\n\nMMKV_NAMESPACE_END\n"
  },
  {
    "path": "Core/MMKV.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MMKV_H\n#define MMKV_MMKV_H\n#ifdef __cplusplus\n#include \"MMKVPredef.h\"\n\n#ifdef MMKV_APPLE\n\n#  include \"MMBuffer.h\"\n#  ifdef MMKV_HAS_CPP20\n#    include <span>\n#  endif\n\n#endif\n\n#include \"MiniPBCoder.h\"\n#include \"MMKVHandler.h\"\n\n#include <cstdint>\n#include <type_traits>\n#include <cstring>\n#include <optional>\n\nnamespace mmkv {\nclass CodedOutputData;\nclass MemoryFile;\nclass AESCrypt;\nstruct MMKVMetaInfo;\nclass FileLock;\nclass InterProcessLock;\nclass ThreadLock;\nclass NameSpace;\n} // namespace mmkv\n\nMMKV_NAMESPACE_BEGIN\n\nenum MMKVMode : uint32_t {\n    MMKV_SINGLE_PROCESS = 1 << 0,\n    MMKV_MULTI_PROCESS = 1 << 1,\n#ifdef MMKV_ANDROID\n    CONTEXT_MODE_MULTI_PROCESS = 1 << 2, // in case someone mistakenly pass Context.MODE_MULTI_PROCESS\n    MMKV_ASHMEM = 1 << 3,\n    MMKV_BACKUP = 1 << 4,\n#endif\n    MMKV_READ_ONLY = 1 << 5,\n};\n\nstatic inline MMKVMode operator | (MMKVMode one, MMKVMode other) {\n    return static_cast<MMKVMode>(static_cast<uint32_t>(one) | static_cast<uint32_t>(other));\n}\n\n// all-in-one configuration for creating MMKV instance\nstruct MMKVConfig {\n    MMKVMode mode = MMKV_SINGLE_PROCESS;\n\n#ifndef MMKV_DISABLE_CRYPT\n    bool aes256 = false; // using AES-256 key length\n    const std::string *cryptKey = nullptr;\n#endif\n\n    const MMKVPath_t *rootPath = nullptr;\n\n    size_t expectedCapacity = 0; // the initial file size\n\n    std::optional<bool> enableKeyExpire = std::nullopt;\n    uint32_t expiredInSeconds = 0; // ExpireNever = 0\n\n    bool enableCompareBeforeSet = false;\n\n    std::optional<MMKVRecoverStrategic> recover = std::nullopt; // if not set, use the old style callback\n    uint32_t itemSizeLimit = 0; // the size limit of a key-value pair, reject insert if pass limit\n};\n\n#define MMKV_OUT\n\n#ifdef MMKV_HAS_CPP20\ntemplate <class T>\nstruct mmkv_is_vector { static constexpr bool value = false; };\ntemplate <class T, class A>\nstruct mmkv_is_vector<std::vector<T, A>> { static constexpr bool value = true; };\ntemplate <class T, size_t S>\nstruct mmkv_is_vector<std::span<T, S>> { static constexpr bool value = true; };\ntemplate <class T>\ninline constexpr bool mmkv_is_vector_v = mmkv_is_vector<T>::value;\n\ntemplate <class T>\nconcept MMKV_SUPPORTED_PRIMITIVE_VALUE_TYPE = std::is_integral_v<T> || std::is_floating_point_v<T>;\n\ntemplate <class T>\nconcept MMKV_SUPPORTED_POD_VALUE_TYPE = std::is_same_v<T, const char*> || std::is_same_v<T, std::string> ||\n    std::is_same_v<T, mmkv::MMBuffer>;\n\ntemplate <class T>\nconcept MMKV_SUPPORTED_VECTOR_VALUE_TYPE = mmkv_is_vector_v<T> &&\n    (MMKV_SUPPORTED_PRIMITIVE_VALUE_TYPE<typename T::value_type> || MMKV_SUPPORTED_POD_VALUE_TYPE<typename T::value_type>);\n\ntemplate <class T>\nconcept MMKV_SUPPORTED_VALUE_TYPE = MMKV_SUPPORTED_PRIMITIVE_VALUE_TYPE<T> || MMKV_SUPPORTED_POD_VALUE_TYPE<T> ||\n    MMKV_SUPPORTED_VECTOR_VALUE_TYPE<T>;\n#endif // MMKV_HAS_CPP20\n\nclass MMKV_EXPORT MMKV {\n    MMKV(const std::string &mmapID, const MMKVConfig &config);\n#ifdef MMKV_ANDROID\n#ifndef MMKV_OHOS\n    mmkv::FileLock *m_fileModeLock;\n    mmkv::InterProcessLock *m_sharedProcessModeLock;\n    mmkv::InterProcessLock *m_exclusiveProcessModeLock;\n#endif // !MMKV_OHOS\n    mmkv::FileLock *m_fileMigrationLock;\n    mmkv::InterProcessLock *m_sharedMigrationLock;\n\n    MMKV(const std::string &mmapID, int ashmemFD, int ashmemMetaFd, const MMKVConfig &config);\n#endif // MMKV_ANDROID\n\n    ~MMKV();\n\n    std::string m_mmapKey;\n    std::string m_mmapID;\n    const MMKVMode m_mode;\n    MMKVPath_t m_path;\n    MMKVPath_t m_crcPath;\n    mmkv::MMKVMap *m_dic;\n    mmkv::MMKVMapCrypt *m_dicCrypt;\n\n    size_t m_expectedCapacity;\n\n    mmkv::MemoryFile *m_file;\n    size_t m_actualSize;\n    mmkv::CodedOutputData *m_output;\n\n    bool m_needLoadFromFile;\n    bool m_hasFullWriteback;\n\n    uint32_t m_crcDigest;\n    mmkv::MemoryFile *m_metaFile;\n    mmkv::MMKVMetaInfo *m_metaInfo;\n\n    mmkv::AESCrypt *m_crypter;\n\n    mmkv::ThreadLock *m_lock;\n    mmkv::FileLock *m_fileLock;\n    mmkv::InterProcessLock *m_sharedProcessLock;\n    mmkv::InterProcessLock *m_exclusiveProcessLock;\n\n    bool m_enableKeyExpire = false;\n    uint32_t m_expiredInSeconds = ExpireNever;\n\n    bool m_enableCompareBeforeSet = false;\n\n    std::optional<MMKVRecoverStrategic> m_recoverStrategic = std::nullopt;\n\n    uint32_t m_itemSizeLimit = 0;\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\n    using MMKVKey_t = NSString *__unsafe_unretained;\n    static bool isKeyEmpty(MMKVKey_t key) { return key.length <= 0; }\n#  define mmkv_key_length(key) key.length\n#  define mmkv_retain_key(key) [key retain]\n#  define mmkv_release_key(key) [key release]\n#else\n    using MMKVKey_t = std::string_view;\n    static bool isKeyEmpty(MMKVKey_t key) { return key.empty(); }\n#endif // __OBJC__\n#else\n    using MMKVKey_t = std::string_view;\n    static bool isKeyEmpty(MMKVKey_t key) { return key.empty(); }\n#  define mmkv_key_length(key) key.length()\n#  define mmkv_retain_key(key) ((void) 0)\n#  define mmkv_release_key(key) ((void) 0)\n#endif // !MMKV_APPLE\n\n    void loadFromFile();\n\n    void partialLoadFromFile();\n    \n//#if defined(MMKV_APPLE) || defined(MMKV_WIN32)\n// the disk corruption detection is tested in iOS/Win32, but not Android\n// let's assume what works for iOS also works on Android for they are all POSIX\n    bool m_isSecondLoad = false;\n    bool checkFileHasDiskError();\n//#else\n//    bool checkFileHasDiskError() { return false; }\n//#endif\n\n    void loadMetaInfoAndCheck();\n\n    void checkDataValid(bool &loadFromFile, bool &needFullWriteback);\n\n    void checkLoadData();\n\n    bool isFileValid();\n\n    bool checkFileCRCValid(size_t actualSize, uint32_t crcDigest);\n\n    void recalculateCRCDigestWithIV(const void *iv);\n    void recalculateCRCDigestOnly();\n\n    void updateCRCDigest(const uint8_t *ptr, size_t length);\n\n    size_t readActualSize();\n\n    void oldStyleWriteActualSize(size_t actualSize);\n\n    bool writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence);\n\n    bool ensureMemorySize(size_t newSize);\n\n    bool expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t> preparedData, bool needSync = true);\n\n    bool fullWriteback(mmkv::AESCrypt *newCrypter = nullptr, bool onlyWhileExpire = false);\n\n    bool doFullWriteBack(std::pair<mmkv::MMBuffer, size_t> preparedData, mmkv::AESCrypt *newCrypter, bool needSync = true);\n\n    bool doFullWriteBack(mmkv::MMKVVector &&vec);\n\n    mmkv::MMBuffer getRawDataForKey(MMKVKey_t key);\n\n    mmkv::MMBuffer getDataForKey(MMKVKey_t key);\n\n    // isDataHolder: avoid memory copying\n    bool setDataForKey(mmkv::MMBuffer &&data, MMKVKey_t key, bool isDataHolder = false);\n\n    bool setDataForKey(mmkv::MMBuffer &&data, MMKVKey_t key, uint32_t expireDuration);\n\n    bool removeDataForKey(MMKVKey_t key);\n\n    using KVHolderRet_t = std::pair<bool, mmkv::KeyValueHolder>;\n    // isDataHolder: avoid memory copying\n    KVHolderRet_t doAppendDataWithKey(const mmkv::MMBuffer &data, const mmkv::MMBuffer &key, bool isDataHolder, uint32_t keyLength);\n    KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, MMKVKey_t key, bool isDataHolder = false);\n    KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, const mmkv::KeyValueHolder &kvHolder, bool isDataHolder = false);\n\n    KVHolderRet_t doOverrideDataWithKey(const mmkv::MMBuffer &data, const mmkv::MMBuffer &key, bool isDataHolder, uint32_t keyLength);\n    KVHolderRet_t overrideDataWithKey(const mmkv::MMBuffer &data, const mmkv::KeyValueHolder &kvHolder, bool isDataHolder = false);\n    KVHolderRet_t overrideDataWithKey(const mmkv::MMBuffer &data, MMKVKey_t key, bool isDataHolder = false);\n    bool checkSizeForOverride(size_t size);\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\n    mmkv::MMBuffer getDataForKey(std::string_view key);\n    bool setDataForKey(mmkv::MMBuffer &&data, std::string_view key, bool isDataHolder = false);\n#endif\n    KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data,\n                                    MMKVKey_t key,\n                                    const mmkv::KeyValueHolderCrypt &kvHolder,\n                                    bool isDataHolder = false);\n    KVHolderRet_t overrideDataWithKey(const mmkv::MMBuffer &data,\n                                      MMKVKey_t key,\n                                      const mmkv::KeyValueHolderCrypt &kvHolder,\n                                      bool isDataHolder = false);\n#endif\n\n    void notifyContentChanged();\n    void notifyContentLoaded();\n\n#if defined(MMKV_ANDROID) && !defined(MMKV_DISABLE_CRYPT)\n    void checkReSetCryptKey(int fd, int metaFD, const std::string *cryptKey, bool aes256);\n#endif\n    static bool backupOneToDirectory(const std::string &mmapKey, const MMKVPath_t &dstPath, const MMKVPath_t &srcPath, bool compareFullPath);\n    static size_t backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t &srcDir, bool isInSpecialDir);\n    static bool restoreOneFromDirectory(const std::string &mmapKey, const MMKVPath_t &srcPath, const MMKVPath_t &dstPath, bool compareFullPath);\n    static size_t restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t &dstDir, bool isInSpecialDir);\n\n    static uint32_t getCurrentTimeInSecond();\n    uint32_t getExpireTimeForKey(MMKVKey_t key);\n    mmkv::MMBuffer getDataWithoutMTimeForKey(MMKVKey_t key);\n    size_t filterExpiredKeys();\n\n    static constexpr uint32_t ConstFixed32Size = 4;\n    void shared_lock();\n    void shared_unlock();\n\n    // assuming rootPath is absolute\n    static MMKV *getMMKVWithID(const std::string &mmapID, const MMKVConfig &config);\n\n    void configAutoExipreIfNeeded(const MMKVConfig &config);\n\n    bool checkSizeLimit(size_t size, const mmkv::MMBuffer &keyData, uint32_t originKeyLength);\n\npublic:\n    // call this before getting any MMKV instance\n    static void initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel = MMKVLogInfo, mmkv::MMKVHandler *handler = nullptr);\n\n    // a generic purpose instance\n    static MMKV *defaultMMKV(MMKVMode mode = MMKV_SINGLE_PROCESS, const std::string *cryptKey = nullptr, bool aes256 = false);\n    static MMKV *defaultMMKV(const MMKVConfig &config);\n\n    // mmapID: any unique ID (com.tencent.xin.pay, etc.)\n    // if you want a per-user mmkv, you could merge user-id within mmapID\n    static MMKV *mmkvWithID(const std::string &mmapID, const MMKVConfig &config);\n\n    // mmapID: any unique ID (com.tencent.xin.pay, etc.)\n    // if you want a per-user mmkv, you could merge user-id within mmapID\n    // cryptKey: 16 bytes at most\n    static MMKV *mmkvWithID(const std::string &mmapID,\n                            MMKVMode mode = MMKV_SINGLE_PROCESS,\n                            const std::string *cryptKey = nullptr,\n                            const MMKVPath_t *rootPath = nullptr,\n                            size_t expectedCapacity = 0,\n                            bool aes256 = false);\n\n#ifdef MMKV_ANDROID\n    static MMKV *mmkvWithAshmemFD(const std::string &mmapID, int fd, int metaFD, const MMKVConfig &config);\n\n    static MMKV *mmkvWithAshmemFD(const std::string &mmapID, int fd, int metaFD, const std::string *cryptKey = nullptr,\n                                  bool aes256 = false);\n\n    int ashmemFD();\n\n    int ashmemMetaFD();\n#ifndef MMKV_OHOS\n    bool checkProcessMode();\n    static void enableDisableProcessMode(bool enable);\n#endif // !MMKV_OHOS\n#endif // MMKV_ANDROID\n\n    // get a namespace with custom root dir\n    static mmkv::NameSpace nameSpace(const MMKVPath_t &rootDir);\n\n    // identical with the original MMKV with the global root dir\n    static mmkv::NameSpace defaultNameSpace();\n\n    // you can call this on application termination, it's totally fine if you don't call\n    static void onExit();\n\n    const std::string &mmapID() const;\n#ifndef MMKV_ANDROID\n    bool isMultiProcess() const { return  (m_mode & MMKV_MULTI_PROCESS) != 0; }\n#else\n    bool isMultiProcess() const {\n        return (m_mode & MMKV_MULTI_PROCESS) != 0\n            || (m_mode & CONTEXT_MODE_MULTI_PROCESS) != 0\n            || (m_mode & MMKV_ASHMEM) != 0; // ashmem is always multi-process\n    }\n\n    bool isAshmem() const {\n        return (m_mode & MMKV_ASHMEM) != 0;\n    }\n#endif\n    bool isReadOnly() const { return (m_mode & MMKV_READ_ONLY) != 0; }\n\n#ifndef MMKV_DISABLE_CRYPT\n    std::string cryptKey() const;\n\n    // transform plain text into encrypted text, or vice versa with empty cryptKey\n    // you can change existing crypt key with different cryptKey\n    bool reKey(const std::string &cryptKey, bool aes256 = false);\n\n    // just reset cryptKey (will not encrypt or decrypt anything)\n    // usually you should call this method after other process reKey() the multi-process mmkv\n    void checkReSetCryptKey(const std::string *cryptKey, bool aes256 = false);\n#endif\n\n    bool set(bool value, MMKVKey_t key);\n    bool set(bool value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(int32_t value, MMKVKey_t key);\n    bool set(int32_t value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(uint32_t value, MMKVKey_t key);\n    bool set(uint32_t value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(int64_t value, MMKVKey_t key);\n    bool set(int64_t value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(uint64_t value, MMKVKey_t key);\n    bool set(uint64_t value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(float value, MMKVKey_t key);\n    bool set(float value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(double value, MMKVKey_t key);\n    bool set(double value, MMKVKey_t key, uint32_t expireDuration);\n\n#ifdef MMKV_HAS_CPP20\n    // avoid unexpected type conversion (pointer to bool, etc.)\n    template <typename T>\n    requires(!MMKV_SUPPORTED_VALUE_TYPE<T>)\n    bool set(T value, MMKVKey_t key) = delete;\n\n    // avoid unexpected type conversion (pointer to bool, etc.)\n    template <typename T>\n    requires(!MMKV_SUPPORTED_VALUE_TYPE<T>)\n    bool set(T value, MMKVKey_t key, uint32_t expireDuration) = delete;\n#else\n    // avoid unexpected type conversion (pointer to bool, etc.)\n    template <typename T>\n    bool set(T value, MMKVKey_t key, uint32_t expireDuration) = delete;\n#endif\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\n    bool set(bool value, std::string_view key);\n    bool set(bool value, std::string_view key, uint32_t expireDuration);\n\n    bool set(int32_t value, std::string_view key);\n    bool set(int32_t value, std::string_view key, uint32_t expireDuration);\n\n    bool set(uint32_t value, std::string_view key);\n    bool set(uint32_t value, std::string_view key, uint32_t expireDuration);\n\n    bool set(int64_t value, std::string_view key);\n    bool set(int64_t value, std::string_view key, uint32_t expireDuration);\n\n    bool set(uint64_t value, std::string_view key);\n    bool set(uint64_t value, std::string_view key, uint32_t expireDuration);\n\n    bool set(float value, std::string_view key);\n    bool set(float value, std::string_view key, uint32_t expireDuration);\n\n    bool set(double value, std::string_view key);\n    bool set(double value, std::string_view key, uint32_t expireDuration);\n\n    bool set(const char *value, std::string_view key);\n    bool set(const char *value, std::string_view key, uint32_t expireDuration);\n\n    bool set(const std::string &value, std::string_view key);\n    bool set(const std::string &value, std::string_view key, uint32_t expireDuration);\n\n    bool set(std::string_view value, std::string_view key);\n    bool set(std::string_view value, std::string_view key, uint32_t expireDuration);\n\n    bool set(const mmkv::MMBuffer &value, std::string_view key);\n    bool set(const mmkv::MMBuffer &value, std::string_view key, uint32_t expireDuration);\n\n    bool set(const std::vector<std::string> &vector, std::string_view key);\n    bool set(const std::vector<std::string> &vector, std::string_view key, uint32_t expireDuration);\n\n    bool containsKey(std::string_view key);\n\n    bool removeValueForKey(std::string_view key);\n\n    bool getBool(std::string_view key, bool defaultValue = false, MMKV_OUT bool *hasValue = nullptr);\n\n    int32_t getInt32(std::string_view key, int32_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    uint32_t getUInt32(std::string_view key, uint32_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    int64_t getInt64(std::string_view key, int64_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    uint64_t getUInt64(std::string_view key, uint64_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    float getFloat(std::string_view key, float defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    double getDouble(std::string_view key, double defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    bool getString(std::string_view key, std::string &result, bool inplaceModification = true);\n\n    mmkv::MMBuffer getBytes(std::string_view key);\n\n    bool getBytes(std::string_view key, mmkv::MMBuffer &result);\n\n#ifdef MMKV_HAS_CPP20\n    template<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\n    bool getVector(std::string_view key, T &result);\n\n    bool getVector(std::string_view key, std::vector<std::string> &result);\n#endif\n\n    bool set(NSObject<NSCoding> *__unsafe_unretained obj, MMKVKey_t key);\n    bool set(NSObject<NSCoding> *__unsafe_unretained obj, MMKVKey_t key, uint32_t expireDuration);\n\n    NSObject *getObject(MMKVKey_t key, Class cls);\n#endif // __OBJC__\n#endif  // MMKV_APPLE\n    bool set(const char *value, MMKVKey_t key);\n    bool set(const char *value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(const std::string &value, MMKVKey_t key);\n    bool set(const std::string &value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(std::string_view value, MMKVKey_t key);\n    bool set(std::string_view value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(const mmkv::MMBuffer &value, MMKVKey_t key);\n    bool set(const mmkv::MMBuffer &value, MMKVKey_t key, uint32_t expireDuration);\n\n    bool set(const std::vector<std::string> &vector, MMKVKey_t key);\n    bool set(const std::vector<std::string> &vector, MMKVKey_t key, uint32_t expireDuration);\n\n#ifdef MMKV_HAS_CPP20\n    template<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\n    bool set(const T& value, MMKVKey_t key) {\n        return set<T>(value, key, m_expiredInSeconds);\n    }\n\n    template<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\n    bool set(const T& value, MMKVKey_t key, uint32_t expireDuration);\n\n    template<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\n    bool getVector(MMKVKey_t key, T &result);\n#endif // MMKV_HAS_CPP20\n\n    // inplaceModification is recommended for faster speed\n    bool getString(MMKVKey_t key, std::string &result, bool inplaceModification = true);\n\n    mmkv::MMBuffer getBytes(MMKVKey_t key);\n\n    bool getBytes(MMKVKey_t key, mmkv::MMBuffer &result);\n\n    bool getVector(MMKVKey_t key, std::vector<std::string> &result);\n\n    bool getBool(MMKVKey_t key, bool defaultValue = false, MMKV_OUT bool *hasValue = nullptr);\n\n    int32_t getInt32(MMKVKey_t key, int32_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    uint32_t getUInt32(MMKVKey_t key, uint32_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    int64_t getInt64(MMKVKey_t key, int64_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    uint64_t getUInt64(MMKVKey_t key, uint64_t defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    float getFloat(MMKVKey_t key, float defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    double getDouble(MMKVKey_t key, double defaultValue = 0, MMKV_OUT bool *hasValue = nullptr);\n\n    // return the actual size consumption of the key's value\n    // pass actualSize = true to get value's length\n    size_t getValueSize(MMKVKey_t key, bool actualSize);\n\n    // return size written into buffer\n    // return -1 on any error\n    int32_t writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size);\n\n    bool containsKey(MMKVKey_t key);\n\n    // filterExpire: return count of all non-expired keys, keep in mind it comes with cost\n    size_t count(bool filterExpire = false);\n\n    size_t totalSize();\n\n    size_t actualSize();\n\n    static constexpr uint32_t ExpireNever = 0;\n\n    // all keys created (or last modified) longer than expiredInSeconds will be deleted on next full-write-back\n    // expiredInSeconds = MMKV::ExpireNever (0) means no common expiration duration for all keys, aka each key will have it's own expiration duration\n    bool enableAutoKeyExpire(uint32_t expiredInSeconds = 0);\n\n    bool disableAutoKeyExpire();\n\n    // compare value for key before set, to reduce the possibility of file expanding\n    bool enableCompareBeforeSet();\n    bool disableCompareBeforeSet();\n\n    bool isExpirationEnabled() const { return m_enableKeyExpire; }\n    bool isEncryptionEnabled() const { return m_crypter != nullptr; }\n    bool isCompareBeforeSetEnabled() const { return m_enableCompareBeforeSet && !m_enableKeyExpire && !m_crypter; }\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\n    // filterExpire: return all non-expired keys, keep in mind it comes with cost\n    NSArray *allKeysObjC(bool filterExpire = false);\n\n    bool removeValuesForKeys(NSArray *arrKeys);\n\n    typedef void (^EnumerateBlock)(NSString *key, BOOL *stop);\n    void enumerateKeys(EnumerateBlock block);\n#endif // __OBJC__\n    // filterExpire: return all non-expired keys, keep in mind it comes with cost\n    std::vector<std::string> allKeys(bool filterExpire = false);\n\n    bool removeValuesForKeys(const std::vector<std::string> &arrKeys);\n\n#    ifdef MMKV_IOS\n    static void setIsInBackground(bool isInBackground);\n    static bool isInBackground();\n#    endif\n#else  // !defined(MMKV_APPLE)\n    // filterExpire: return all non-expired keys, keep in mind it comes with cost\n    std::vector<std::string> allKeys(bool filterExpire = false);\n\n    bool removeValuesForKeys(const std::vector<std::string> &arrKeys);\n#endif // MMKV_APPLE\n\n    bool removeValueForKey(MMKVKey_t key);\n\n    // keepSpace: remove all keys but keep the file size not changed, running faster\n    void clearAll(bool keepSpace = false);\n\n    // MMKV's size won't reduce after deleting key-values\n    // call this method after lots of deleting if you care about disk usage\n    // note that `clearAll` has the similar effect of `trim`\n    void trim();\n\n    // import all key-value items from source\n    // return count of items imported\n    size_t importFrom(MMKV *src);\n\n    // call this method if the instance is no longer needed in the near future\n    // any subsequent call to the instance is undefined behavior\n    void close();\n\n    // call this method if you are facing memory-warning\n    // any subsequent call to the instance will load all key-values from file again\n    // keepSpace: remove all keys but keep the file size not changed, running faster\n    void clearMemoryCache(bool keepSpace = false);\n\n    // you don't need to call this, really, I mean it\n    // unless you worry about running out of battery\n    void sync(SyncFlag flag = MMKV_SYNC);\n\n    // get exclusive access\n    void lock();\n    void unlock();\n    bool try_lock();\n\n    // get thread lock\n#ifndef MMKV_WIN32\n    void lock_thread();\n    void unlock_thread();\n    bool try_lock_thread();\n#endif\n\n    static const MMKVPath_t &getRootDir();\n\n    // backup one MMKV instance from srcDir to dstDir\n    // if srcDir is null, then backup from the root dir of MMKV\n    static bool backupOneToDirectory(const std::string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t *srcDir = nullptr);\n\n    // restore one MMKV instance from srcDir to dstDir\n    // if dstDir is null, then restore to the root dir of MMKV\n    static bool restoreOneFromDirectory(const std::string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t *dstDir = nullptr);\n\n    // backup all MMKV instance from srcDir to dstDir\n    // if srcDir is null, then backup from the root dir of MMKV\n    // return count of MMKV successfully backuped\n    static size_t backupAllToDirectory(const MMKVPath_t &dstDir, const MMKVPath_t *srcDir = nullptr);\n\n    // restore all MMKV instance from srcDir to dstDir\n    // if dstDir is null, then restore to the root dir of MMKV\n    // return count of MMKV successfully restored\n    static size_t restoreAllFromDirectory(const MMKVPath_t &srcDir, const MMKVPath_t *dstDir = nullptr);\n\n    // check if content been changed by other process\n    void checkContentChanged();\n\n    // register a unified callback handler for MMKV\n    static void registerHandler(mmkv::MMKVHandler *handler);\n    static void unRegisterHandler();\n\n    // MMKVLogInfo by default\n    // pass MMKVLogNone to disable all logging\n    static void setLogLevel(MMKVLogLevel level);\n\n    // detect if the MMKV file is valid or not\n    // Note: Don't use this to check the existence of the instance, the return value is undefined if the file was never created.\n    static bool isFileValid(const std::string &mmapID, const MMKVPath_t *relatePath = nullptr);\n\n    // remove the storage of the MMKV, including the data file & meta file (.crc)\n    // Note: the existing instance (if any) will be closed & destroyed\n    static bool removeStorage(const std::string &mmapID, const MMKVPath_t *relatePath = nullptr);\n\n    // check the existence of the MMKV file\n    static bool checkExist(const std::string &mmapID, const MMKVPath_t *relatePath = nullptr);\n\n    // just forbid it for possibly misuse\n    explicit MMKV(const MMKV &other) = delete;\n    MMKV &operator=(const MMKV &other) = delete;\n\n    friend class mmkv::NameSpace;\n};\n\n#if defined(MMKV_HAS_CPP20)\ntemplate<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\nbool MMKV::set(const T& value, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    mmkv::MMBuffer data;\n    if constexpr (std::is_same_v<T, std::vector<bool>>) {\n        data = mmkv::MiniPBCoder::encodeDataWithObject(value);\n    } else {\n        data = mmkv::MiniPBCoder::encodeDataWithObject(std::span(value));\n    }\n    if (mmkv_unlikely(m_enableKeyExpire) && data.length() > 0) {\n        auto tmp = mmkv::MMBuffer(data.length() + ConstFixed32Size);\n        auto ptr = (uint8_t *) tmp.getPtr();\n        memcpy(ptr, data.getPtr(), data.length());\n        auto time = (expireDuration != ExpireNever) ? getCurrentTimeInSecond() + expireDuration : ExpireNever;\n        memcpy(ptr + data.length(), &time, ConstFixed32Size);\n        data = std::move(tmp);\n    }\n    return setDataForKey(std::move(data), key);\n}\n\ntemplate<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\nbool MMKV::getVector(MMKVKey_t key, T &result) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    shared_lock();\n\n    bool ret = false;\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        ret = mmkv::MiniPBCoder::decodeVector(data, result);\n    }\n\n    shared_unlock();\n    return ret;\n}\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\ntemplate<MMKV_SUPPORTED_VECTOR_VALUE_TYPE T>\nbool getVector(std::string_view key, T &result) {\n    HybridString hybridKey(key);\n    return getVector(hybridKey.str, result);\n}\n#endif // __OBJC__\n#endif // MMKV_APPLE\n\n#endif // MMKV_HAS_CPP20\n\nMMKV_NAMESPACE_END\n\nnamespace mmkv {\n\n// a POD-like facade what wraps custom root directory\nclass MMKV_EXPORT NameSpace {\n    const MMKVPath_t &m_rootDir;\n    NameSpace(const MMKVPath_t &rootDir) : m_rootDir(rootDir) {}\npublic:\n    // return the absolute root dir of NameSpace\n    const MMKVPath_t &getRootDir() { return m_rootDir; }\n\n    MMKV *mmkvWithID(const std::string &mmapID, const MMKVConfig &config);\n\n    // mmapID: any unique ID (com.tencent.xin.pay, etc.)\n    // if you want a per-user mmkv, you could merge user-id within mmapID\n    // cryptKey: 16 bytes at most\n    MMKV *mmkvWithID(const std::string &mmapID,\n                     MMKVMode mode = MMKV_SINGLE_PROCESS,\n                     const std::string *cryptKey = nullptr,\n                     size_t expectedCapacity = 0,\n                     bool aes256 = false);\n\n    // backup one MMKV instance to dstDir\n    bool backupOneToDirectory(const std::string &mmapID, const MMKVPath_t &dstDir);\n\n    // restore one MMKV instance from srcDir\n    bool restoreOneFromDirectory(const std::string &mmapID, const MMKVPath_t &srcDir);\n\n    // backup all MMKV instance to dstDir\n    // return count of MMKV successfully backuped\n    size_t backupAllToDirectory(const MMKVPath_t &dstDir);\n\n    // restore all MMKV instance from srcDir\n    // return count of MMKV successfully restored\n    size_t restoreAllFromDirectory(const MMKVPath_t &srcDir);\n\n    // detect if the MMKV file is valid or not\n    // Note: Don't use this to check the existence of the instance, the return value is undefined if the file was never created.\n    bool isFileValid(const std::string &mmapID);\n\n    // remove the storage of the MMKV, including the data file & meta file (.crc)\n    // Note: the existing instance (if any) will be closed & destroyed\n    bool removeStorage(const std::string &mmapID);\n\n    // check the existence of the MMKV file\n    bool checkExist(const std::string &mmapID);\n\n    friend class MMKV_NAMESPACE_PREFIX::MMKV;\n};\n\n}\n\n#endif // __cplusplus\n#endif // MMKV_MMKV_H\n"
  },
  {
    "path": "Core/MMKVHandler.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKVCoreHandler_h\n#define MMKVCoreHandler_h\n\n#include \"MMKVPredef.h\"\n\n#ifdef __cplusplus\n\nnamespace mmkv {\n\n// unified callback handler for MMKV\nclass MMKVHandler {\npublic:\n    virtual ~MMKVHandler() = default;\n    \n    // by default MMKV will print log using system log\n    // implement this method to redirect MMKV's log\n    virtual void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message) {}\n    \n    // by default MMKV will discard all data on crc32-check failure\n    // return `OnErrorRecover` to recover any data on the file\n    virtual MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) { return OnErrorDiscard; }\n    \n    // by default MMKV will discard all data on file length mismatch\n    // return `OnErrorRecover` to recover any data on the file\n    virtual MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) { return OnErrorDiscard; }\n    \n    // called when content is changed by other process\n    // doesn't guarantee real-time notification\n    virtual void onContentChangedByOuterProcess(const std::string &mmapID) {}\n    \n    // called when an MMKV file is loaded successfully\n    virtual void onMMKVContentLoadSuccessfully(const std::string &mmapID) {}\n};\n\n} // namespace mmkv\n\n#endif\n\n#endif /* MMKVHandler_h */\n"
  },
  {
    "path": "Core/MMKVLog.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MMKVLog.h\"\n#ifdef MMKV_WIN32\n#include <windows.h>\n#endif // MMKV_WIN32\n\n\nMMKV_NAMESPACE_BEGIN\n\n#ifdef MMKV_DEBUG\nMMKVLogLevel g_currentLogLevel = MMKVLogDebug;\n#else\nMMKVLogLevel g_currentLogLevel = MMKVLogInfo;\n#endif\n\n#ifndef __FILE_NAME__\nconst char *_getFileName(const char *path) {\n    const char *ptr = strrchr(path, '/');\n    if (!ptr) {\n        ptr = strrchr(path, '\\\\');\n    }\n    if (ptr) {\n        return ptr + 1;\n    } else {\n        return path;\n    }\n}\n#endif\n\nmmkv::MMKVHandler *g_handler = nullptr;\n\nMMKV_NAMESPACE_END\n\n\n#ifdef ENABLE_MMKV_LOG\n#    include <cstdarg>\n#    include <string>\n\nusing namespace mmkv;\n\n#    ifndef MMKV_ANDROID\n\nstatic const char *MMKVLogLevelDesc(MMKVLogLevel level) {\n    switch (level) {\n        case MMKVLogDebug:\n            return \"D\";\n        case MMKVLogInfo:\n            return \"I\";\n        case MMKVLogWarning:\n            return \"W\";\n        case MMKVLogError:\n            return \"E\";\n        default:\n            return \"N\";\n    }\n}\n\n#        ifdef MMKV_APPLE\n\nvoid _MMKVLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...) {\n    if (level >= g_currentLogLevel) {\n        NSString *nsFormat = [NSString stringWithUTF8String:format];\n        va_list argList;\n        va_start(argList, format);\n        NSString *message = [[NSString alloc] initWithFormat:nsFormat arguments:argList];\n        va_end(argList);\n\n        if (g_handler) {\n            g_handler->mmkvLog(level, filename, line, func, message);\n        } else {\n            NSLog(@\"[%s] <%s:%d::%s> %@\", MMKVLogLevelDesc(level), filename, line, func, message);\n        }\n    }\n}\n\n#        else\n\n#if defined(MMKV_WIN32)\n// Helper to write raw bytes or convert to WideChar based on destination\nstatic void WriteUTF8ToStream(const char* utf8_str) {\n    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);\n    DWORD consoleMode;\n\n    if (GetConsoleMode(hOut, &consoleMode)) {\n        // --- CONSOLE: Convert to UTF-16 and write ---\n        int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);\n        if (wlen > 0) {\n            wchar_t* wbuf = (wchar_t*)malloc(wlen * sizeof(wchar_t));\n            if (wbuf) {\n                MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wbuf, wlen);\n                WriteConsoleW(hOut, wbuf, wlen - 1, NULL, NULL);\n                free(wbuf);\n            }\n        }\n    } else {\n        // --- FILE/PIPE: Write raw UTF-8 bytes ---\n        DWORD bytesWritten;\n        WriteFile(hOut, utf8_str, (DWORD)strlen(utf8_str), &bytesWritten, NULL);\n    }\n}\n\n// Main VarArg Function\nstatic void PrintUTF8(const char* format, ...) {\n    va_list args;\n\n    // 1. Calculate required length\n    // We pass NULL/0 to vsnprintf just to get the required size (excluding null terminator)\n    va_start(args, format);\n    int len = vsnprintf(NULL, 0, format, args);\n    va_end(args);\n\n    if (len < 0) return; // Encoding error or invalid format\n\n    // 2. Allocate buffer (len + 1 for null terminator)\n    // Using malloc ensures we don't overflow the stack with huge strings\n    char* buf = (char*)malloc(len + 1);\n    if (!buf) return; // Out of memory\n\n    // 3. Format the string into the buffer\n    va_start(args, format);\n    vsnprintf(buf, len + 1, format, args);\n    va_end(args);\n\n    // 4. Send to output helper\n    WriteUTF8ToStream(buf);\n\n    // 5. Cleanup\n    free(buf);\n}\n#endif\n\nvoid _MMKVLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...) {\n    if (level >= g_currentLogLevel) {\n        std::string message;\n        char buffer[16];\n\n        va_list args;\n        va_start(args, format);\n        auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n        va_end(args);\n\n        if (length < 0) { // something wrong\n            message = {};\n        } else if (length < sizeof(buffer)) {\n            message = std::string(buffer, static_cast<unsigned long>(length));\n        } else {\n            message.resize(static_cast<unsigned long>(length), '\\0');\n            va_start(args, format);\n            std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n            va_end(args);\n        }\n\n        if (g_handler) {\n            g_handler->mmkvLog(level, filename, line, func, message);\n        } else {\n#if defined(MMKV_WIN32)\n            PrintUTF8(\"[%s] <%s:%d::%s> %s\\n\", MMKVLogLevelDesc(level), filename, line, func, message.c_str());\n#else\n            printf(\"[%s] <%s:%d::%s> %s\\n\", MMKVLogLevelDesc(level), filename, line, func, message.c_str());\n#endif\n            //fflush(stdout);\n        }\n    }\n}\n\n#        endif // MMKV_APPLE\n\n#    endif // MMKV_ANDROID\n\n#endif // ENABLE_MMKV_LOG\n"
  },
  {
    "path": "Core/MMKVLog.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MMKVLOG_H\n#define MMKV_MMKVLOG_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n#include \"MMKVHandler.h\"\n\n#include <cerrno>\n#include <cstdint>\n#include <cstring>\n\n//#ifdef MMKV_WIN32\n//void _MMKVLogWithLevel(\n//    MMKV_NAMESPACE_PREFIX::MMKVLogLevel level, const char *filename, const char *func, int line, const wchar_t *format, ...);\n//#endif\n\nvoid _MMKVLogWithLevel(\n    MMKV_NAMESPACE_PREFIX::MMKVLogLevel level, const char* filename, const char* func, int line, const char* format, ...);\n\nMMKV_NAMESPACE_BEGIN\n\nextern MMKVLogLevel g_currentLogLevel;\nextern mmkv::MMKVHandler *g_handler;\n\n// enable logging\n#define ENABLE_MMKV_LOG\n\n#ifdef ENABLE_MMKV_LOG\n\n//#ifdef MMKV_WIN32\n//#define MMKV_LOG_FORMAT_PREFIX(format) L##format\n//#else\n//#define MMKV_LOG_FORMAT_PREFIX(format) format\n//#endif\n\n#    ifdef __FILE_NAME__\n#        define __MMKV_FILE_NAME__ __FILE_NAME__\n#    else\nconst char *_getFileName(const char *path);\n#        define __MMKV_FILE_NAME__ MMKV_NAMESPACE_PREFIX::_getFileName(__FILE__)\n#    endif\n\n#    define MMKVError(format, ...)                                                                                     \\\n        _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogError, __MMKV_FILE_NAME__, __func__, __LINE__, format,         \\\n                          ##__VA_ARGS__)\n#    define MMKVWarning(format, ...)                                                                                   \\\n        _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogWarning, __MMKV_FILE_NAME__, __func__, __LINE__, format,       \\\n                          ##__VA_ARGS__)\n#    define MMKVInfo(format, ...)                                                                                      \\\n        _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogInfo, __MMKV_FILE_NAME__, __func__, __LINE__, format,          \\\n                          ##__VA_ARGS__)\n\n#    ifdef MMKV_DEBUG\n#        define MMKVDebug(format, ...)                                                                                 \\\n            _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogDebug, __MMKV_FILE_NAME__, __func__, __LINE__, format,     \\\n                              ##__VA_ARGS__)\n#    else\n#        define MMKVDebug(format, ...)                                                                                 \\\n            {}\n#    endif\n\n#else\n\n#    define MMKVError(format, ...)                                                                                     \\\n        {}\n#    define MMKVWarning(format, ...)                                                                                   \\\n        {}\n#    define MMKVInfo(format, ...)                                                                                      \\\n        {}\n#    define MMKVDebug(format, ...)                                                                                     \\\n        {}\n\n#endif\n\nMMKV_NAMESPACE_END\n\n#endif\n#endif //MMKV_MMKVLOG_H\n"
  },
  {
    "path": "Core/MMKVLog_Android.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MMKVLog.h\"\n\n#ifdef ENABLE_MMKV_LOG\n#   ifdef MMKV_OHOS\n#   include <hilog/log.h>\n#   include <cstdarg>\n#   include <string>\n\nusing namespace std;\n\nconstexpr auto APP_NAME = \"MMKV\";\n\nstatic LogLevel MMKVLogLevelDesc(MMKVLogLevel level) {\n    switch (level) {\n    case MMKVLogDebug:\n        return LOG_DEBUG;\n    case MMKVLogInfo:\n        return LOG_INFO;\n    case MMKVLogWarning:\n        return LOG_WARN;\n    case MMKVLogError:\n        return LOG_ERROR;\n    default:\n        return LOG_INFO;\n    }\n}\n\nvoid _MMKVLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...) {\n    if (level >= g_currentLogLevel) {\n        string message;\n        char buffer[16];\n\n        va_list args;\n        va_start(args, format);\n        auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n        va_end(args);\n\n        if (length < 0) { // something wrong\n            message = {};\n        } else if (length < sizeof(buffer)) {\n            message = string(buffer, static_cast<unsigned long>(length));\n        } else {\n            message.resize(static_cast<unsigned long>(length), '\\0');\n            va_start(args, format);\n            std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n            va_end(args);\n        }\n\n        if (g_handler) {\n            g_handler->mmkvLog(level, filename, line, func, message);\n        } else {\n            auto desc = MMKVLogLevelDesc(level);\n            OH_LOG_Print(LOG_APP, desc, 0, APP_NAME, \"<%{public}s:%{public}d::%{public}s> %{public}s\",\n                filename, line, func, message.c_str());\n        }\n    }\n}\n\n#    elif defined(MMKV_ANDROID)\n#        include <android/log.h>\n#        include <cstdarg>\n#        include <string>\n\nusing namespace std;\n\nconstexpr auto APP_NAME = \"MMKV\";\n\nstatic android_LogPriority MMKVLogLevelDesc(MMKVLogLevel level) {\n    switch (level) {\n        case MMKVLogDebug:\n            return ANDROID_LOG_DEBUG;\n        case MMKVLogInfo:\n            return ANDROID_LOG_INFO;\n        case MMKVLogWarning:\n            return ANDROID_LOG_WARN;\n        case MMKVLogError:\n            return ANDROID_LOG_ERROR;\n        default:\n            return ANDROID_LOG_UNKNOWN;\n    }\n}\n\nvoid _MMKVLogWithLevel(MMKVLogLevel level, const char *filename, const char *func, int line, const char *format, ...) {\n    if (level >= g_currentLogLevel) {\n        string message;\n        char buffer[16];\n\n        va_list args;\n        va_start(args, format);\n        auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n        va_end(args);\n\n        if (length < 0) { // something wrong\n            message = {};\n        } else if (length < sizeof(buffer)) {\n            message = string(buffer, static_cast<unsigned long>(length));\n        } else {\n            message.resize(static_cast<unsigned long>(length), '\\0');\n            va_start(args, format);\n            std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n            va_end(args);\n        }\n\n        if (g_handler) {\n            g_handler->mmkvLog(level, filename, line, func, message);\n        } else {\n            auto desc = MMKVLogLevelDesc(level);\n            __android_log_print(desc, APP_NAME, \"<%s:%d::%s> %s\", filename, line, func, message.c_str());\n        }\n    }\n}\n#    endif // MMKV_ANDROID\n\n#endif // ENABLE_MMKV_LOG\n"
  },
  {
    "path": "Core/MMKVMetaInfo.hpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MMKVMETAINFO_H\n#define MMKV_MMKVMETAINFO_H\n#ifdef __cplusplus\n\n#include \"aes/AESCrypt.h\"\n#include <cstdint>\n#include <cstring>\n\nnamespace mmkv {\n\nenum MMKVVersion : uint32_t {\n    MMKVVersionDefault = 0,\n\n    // record full write back count\n    MMKVVersionSequence = 1,\n\n    // store random iv for encryption\n    MMKVVersionRandomIV = 2,\n\n    // store actual size together with crc checksum, try to reduce file corruption\n    MMKVVersionActualSize = 3,\n\n    // store extra flags\n    MMKVVersionFlag = 4,\n\n    // preserved for internal use\n    MMKVVersionPreserved = 5,\n\n    // preserved for next use\n    MMKVVersionNext = 6,\n\n    // always large than next, a placeholder for error check\n    MMKVVersionHolder = MMKVVersionNext + 1,\n};\n\nstruct MMKVMetaInfo {\n    uint32_t m_crcDigest = 0;\n    uint32_t m_version = MMKVVersionSequence;\n    uint32_t m_sequence = 0; // full write-back count\n    uint8_t m_vector[AES_IV_LEN] = {};\n    uint32_t m_actualSize = 0;\n\n    // confirmed info: it's been synced to file\n    struct {\n        uint32_t lastActualSize = 0;\n        uint32_t lastCRCDigest = 0;\n        uint32_t _reserved[16] = {};\n    } m_lastConfirmedMetaInfo;\n\n    uint64_t m_flags = 0;\n\n    enum MMKVMetaInfoFlag : uint64_t {\n        EnableKeyExipre = 1 << 0,\n    };\n    bool hasFlag(MMKVMetaInfoFlag flag) { return (m_flags & flag) != 0; }\n    void setFlag(MMKVMetaInfoFlag flag) { m_flags |= flag; }\n    void unsetFlag(MMKVMetaInfoFlag flag) { m_flags &= ~flag; }\n\n    void write(void *ptr) const {\n        MMKV_ASSERT(ptr);\n        memcpy(ptr, this, sizeof(MMKVMetaInfo));\n    }\n\n    void writeCRCAndActualSizeOnly(void *ptr) const {\n        MMKV_ASSERT(ptr);\n        auto other = (MMKVMetaInfo *) ptr;\n        other->m_crcDigest = m_crcDigest;\n        other->m_actualSize = m_actualSize;\n    }\n\n    void read(const void *ptr) {\n        MMKV_ASSERT(ptr);\n        memcpy(this, ptr, sizeof(MMKVMetaInfo));\n    }\n};\n\nstatic_assert(sizeof(MMKVMetaInfo) <= (4 * 1024), \"MMKVMetaInfo lager than one pagesize\");\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_MMKVMETAINFO_H\n"
  },
  {
    "path": "Core/MMKVPredef.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_SRC_MMKVPREDEF_H\n#define MMKV_SRC_MMKVPREDEF_H\n\n// disable encryption & decryption to reduce some code\n// #define MMKV_DISABLE_CRYPT\n//#define MMKV_DISABLE_FLUTTER\n\n// using POSIX implementation\n//#define FORCE_POSIX\n\n#ifdef __cplusplus\n\n#include <string>\n#include <vector>\n#include <unordered_map>\n\nconstexpr auto MMKV_VERSION = \"v2.4.0\";\n\n#ifdef DEBUG\n#    define MMKV_DEBUG\n#endif\n\n#ifdef NDEBUG\n#    undef MMKV_DEBUG\n#endif\n\n#if __cplusplus>=202002L\n#    define MMKV_HAS_CPP20\n#endif\n\n#ifdef __ANDROID__\n#    ifdef FORCE_POSIX\n#        define MMKV_POSIX\n#    else\n#        define MMKV_ANDROID\n#    endif\n#elif __OHOS__\n#   ifdef FORCE_POSIX\n#       define MMKV_POSIX\n#   else\n#       define MMKV_ANDROID\n#       define MMKV_OHOS\n#endif\n#elif __APPLE__\n#    ifdef FORCE_POSIX\n#        define MMKV_POSIX\n#    else\n#        define MMKV_APPLE\n#        ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__\n#            define MMKV_IOS\n#        elif __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__\n#            define MMKV_WATCH\n#        else\n#            define MMKV_MAC\n#        endif\n#    endif // FORCE_POSIX\n#elif __linux__ || __unix__\n#    define MMKV_POSIX\n#    if __linux__\n#        define MMKV_LINUX\n#    endif\n#elif _WIN32\n#    define MMKV_WIN32\n#endif\n\n#ifdef MMKV_WIN32\n#    if !defined(_WIN32_WINNT)\n#        define _WIN32_WINNT _WIN32_WINNT_WINXP\n#    endif\n\n#    include <SDKDDKVer.h>\n// Exclude rarely-used stuff from Windows headers\n#    define WIN32_LEAN_AND_MEAN\n// Windows Header Files\n#    include <windows.h>\n\nconstexpr auto MMKV_PATH_SLASH = L\"\\\\\";\nusing MMKVFileHandle_t = HANDLE;\n#define MMKVFileHandleInvalidValue INVALID_HANDLE_VALUE\nusing MMKVPath_t = std::wstring;\nextern MMKVPath_t string2MMKVPath_t(const std::string &str);\nextern std::string MMKVPath_t2String(const MMKVPath_t &str);\n\n#    ifndef MMKV_EMBED_ZLIB\n#        define MMKV_EMBED_ZLIB 1\n#    endif\n\n#else // MMKV_WIN32\n\nconstexpr auto MMKV_PATH_SLASH = \"/\";\nusing MMKVFileHandle_t = int;\nconstexpr MMKVFileHandle_t MMKVFileHandleInvalidValue = -1;\nusing MMKVPath_t = std::string;\n#    define string2MMKVPath_t(str) (str)\n#    define MMKVPath_t2String(str) (str)\n\n#    ifndef MMKV_EMBED_ZLIB\n#        define MMKV_EMBED_ZLIB 0\n#    endif\n\n#endif // MMKV_WIN32\n\n#ifdef MMKV_ANDROID\n#define MMKV_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define MMKV_EXPORT\n#endif\n\n#ifdef MMKV_APPLE\n#ifdef __OBJC__\n#    import <Foundation/Foundation.h>\nusing MMKVLog_t = NSString *;\n#else\nusing MMKVLog_t = void *;\n#endif\n#    define MMKV_NAMESPACE_BEGIN namespace mmkv {\n#    define MMKV_NAMESPACE_END }\n#    define MMKV_NAMESPACE_PREFIX mmkv\n#else\n#    define MMKV_NAMESPACE_BEGIN\n#    define MMKV_NAMESPACE_END\n#    define MMKV_NAMESPACE_PREFIX\nusing MMKVLog_t = const std::string &;\n#endif // MMKV_APPLE\n\nMMKV_NAMESPACE_BEGIN\n\nenum MMKVLogLevel : int {\n    MMKVLogDebug = 0, // not available for release/product build\n    MMKVLogInfo = 1,  // default level\n    MMKVLogWarning,\n    MMKVLogError,\n    MMKVLogNone, // special level used to disable all log messages\n};\n\nenum MMKVRecoverStrategic : int {\n    OnErrorDiscard = 0,\n    OnErrorRecover,\n};\n\nenum MMKVErrorType : int {\n    MMKVCRCCheckFail = 0,\n    MMKVFileLength,\n};\n\nenum SyncFlag : bool { MMKV_SYNC = true, MMKV_ASYNC = false };\n\nMMKV_NAMESPACE_END\n\nnamespace mmkv {\n\nextern MMKV_EXPORT size_t DEFAULT_MMAP_SIZE;\n#define DEFAULT_MMAP_ID \"mmkv.default\"\n\nclass MMBuffer;\nstruct KeyValueHolder;\n\n#ifdef MMKV_DISABLE_CRYPT\nusing KeyValueHolderCrypt = KeyValueHolder;\n#else\nstruct KeyValueHolderCrypt;\n#endif\n\n#ifdef MMKV_APPLE\n\n#ifdef __OBJC__\nstruct HybridStringCP {\n    NSString *str;\n    HybridStringCP(std::string_view cpp);\n    ~HybridStringCP();\n};\n\nstruct HybridString {\n    NSString *str;\n    HybridString(std::string_view cpp);\n    ~HybridString();\n};\n\nstruct KeyHasher {\n    // enables heterogeneous lookup\n    using is_transparent = void;\n    size_t operator()(NSString *key) const { return key.hash; }\n};\n\nstruct KeyEqualer {\n    // enables heterogeneous lookup\n    using is_transparent = void;\n    bool operator()(NSString *left, NSString *right) const {\n        if (left == right) {\n            return true;\n        }\n        return ([left isEqualToString:right] == YES);\n    }\n};\nusing MMKVVector = std::vector<std::pair<NSString *, mmkv::MMBuffer>>;\nusing MMKVMap = std::unordered_map<NSString *, mmkv::KeyValueHolder, KeyHasher, KeyEqualer>;\nusing MMKVMapCrypt = std::unordered_map<NSString *, mmkv::KeyValueHolderCrypt, KeyHasher, KeyEqualer>;\n#else // type erase for pure C++ users\nusing MMKVVector = std::vector<std::pair<void *, mmkv::MMBuffer>>;\nusing MMKVMap = std::unordered_map<void *, mmkv::KeyValueHolder>;\nusing MMKVMapCrypt = std::unordered_map<void *, mmkv::KeyValueHolderCrypt>;\n#endif // __OBJC__\n\n#else // !MMKV_APPLE\n\nstruct KeyHasher {\n    // enables heterogeneous lookup\n    using is_transparent = void;\n\n    std::size_t operator()(const std::string_view& str) const {\n        return std::hash<std::string_view>{}(str);\n    }\n\n    std::size_t operator()(const std::string& str) const {\n        return std::hash<std::string>{}(str);\n    }\n};\n\nstruct KeyEqualer {\n    // enables heterogeneous lookup\n    using is_transparent = void;\n\n    bool operator()(const std::string_view& lhs, const std::string_view& rhs) const {\n        return lhs == rhs;\n    }\n\n    bool operator()(const std::string& lhs, const std::string& rhs) const {\n        return lhs == rhs;\n    }\n};\nusing MMKVVector = std::vector<std::pair<std::string, mmkv::MMBuffer>>;\nusing MMKVMap = std::unordered_map<std::string, mmkv::KeyValueHolder, KeyHasher, KeyEqualer>;\nusing MMKVMapCrypt = std::unordered_map<std::string, mmkv::KeyValueHolderCrypt, KeyHasher, KeyEqualer>;\n#endif // MMKV_APPLE\n\ntemplate <typename T>\nvoid unused(const T &) {}\n\nconstexpr size_t AES_KEY_LEN = 16;\nconstexpr size_t AES_KEY_BITSET_LEN = 128;\nconstexpr size_t AES_IV_LEN = 16;\nconstexpr size_t AES256_KEY_LEN = 32;\nconstexpr size_t AES256_KEY_BITSET_LEN = 256;\n\n} // namespace mmkv\n\n#ifdef MMKV_DEBUG\n#    include <cassert>\n#    define MMKV_ASSERT(var) assert(var)\n#else\n#    define MMKV_ASSERT(var) mmkv::unused(var)\n#endif\n\n#endif //cplus-plus\n\n#ifndef MMKV_WIN32\n#    ifndef likely\n#        define mmkv_unlikely(x) (__builtin_expect(bool(x), 0))\n#        define mmkv_likely(x) (__builtin_expect(bool(x), 1))\n#    endif\n#else\n#    ifndef likely\n#        define mmkv_unlikely(x) (x)\n#        define mmkv_likely(x) (x)\n#    endif\n#endif\n\n#if defined(__x86_64__) || defined(_M_X64)\n  #define MMKV_ABI \"x86_64\"\n#elif defined(__aarch64__) || defined(_M_ARM64)\n  #define MMKV_ABI \"arm64-v8a\"\n#else\n  #define MMKV_ABI \"unknow\"\n//  #error \"Unsupported arch.\"\n#endif\n\n#endif //MMKV_SRC_MMKVPREDEF_H\n"
  },
  {
    "path": "Core/MMKV_Android.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MMKV.h\"\n// #include <bits/alltypes.h>\n\n#ifdef MMKV_ANDROID\n\n#    include \"InterProcessLock.h\"\n#    include \"KeyValueHolder.h\"\n#    include \"MMKVLog.h\"\n#    include \"MMKVMetaInfo.hpp\"\n#    include \"MemoryFile.h\"\n#    include \"ScopedLock.hpp\"\n#    include \"ThreadLock.h\"\n#    include <unistd.h>\n#    include \"MMKV_IO.h\"\n\nusing namespace std;\nusing namespace mmkv;\n\nextern unordered_map<string, MMKV *> *g_instanceDic;\nextern ThreadLock *g_instanceLock;\nextern MMKVPath_t g_realRootDir;\n\n#ifndef MMKV_OHOS\nstatic bool g_enableProcessModeCheck = false;\n#endif\n\nMMKV::MMKV(const string &mmapID, const MMKVConfig &config)\n    : m_mmapID(mmapID)\n    , m_mode(config.mode)\n    , m_path(mappedKVPathWithID(m_mmapID, config.rootPath, config.mode, true))\n    , m_crcPath(crcPathWithPath(m_path))\n    , m_dic(nullptr)\n    , m_dicCrypt(nullptr)\n    , m_expectedCapacity(std::max<size_t>(DEFAULT_MMAP_SIZE, roundUp<size_t>(config.expectedCapacity, DEFAULT_MMAP_SIZE)))\n    , m_file(new MemoryFile(m_path, isAshmem() ? MMFILE_TYPE_ASHMEM : MMFILE_TYPE_FILE, m_expectedCapacity, isReadOnly(), !isAshmem()))\n    , m_metaFile(new MemoryFile(m_crcPath, m_file->m_fileType, DEFAULT_MMAP_SIZE, isReadOnly()))\n    , m_metaInfo(new MMKVMetaInfo())\n    , m_crypter(nullptr)\n    , m_lock(new ThreadLock())\n    , m_fileLock(new FileLock(m_metaFile->getFd(), isAshmem(), isAshmem(), 0, 1))\n    , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))\n    , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType)) {\n    m_actualSize = 0;\n    m_output = nullptr;\n\n#ifndef MMKV_OHOS\n    if (g_enableProcessModeCheck) {\n        // force use fcntl(), otherwise will conflict with MemoryFile::reloadFromFile()\n        m_fileModeLock = new FileLock(m_metaFile->getFd(), true, isAshmem(), 1, 2);\n        m_sharedProcessModeLock = new InterProcessLock(m_fileModeLock, SharedLockType);\n    } else {\n        m_fileModeLock = nullptr;\n        m_sharedProcessModeLock = nullptr;\n    }\n    m_exclusiveProcessModeLock = nullptr;\n#endif\n\n    if (isAshmem()) {\n        m_fileMigrationLock = nullptr;\n        m_sharedMigrationLock = nullptr;\n    } else {\n        m_fileMigrationLock = new FileLock(m_metaFile->getFd(), true, false, 2, 3);\n        m_sharedMigrationLock = new InterProcessLock(m_fileMigrationLock, SharedLockType);\n        m_sharedMigrationLock->try_lock();\n    }\n\n#    ifndef MMKV_DISABLE_CRYPT\n    auto cryptKey = config.cryptKey;\n    if (cryptKey && cryptKey->length() > 0) {\n        m_dicCrypt = new MMKVMapCrypt();\n        m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length(), nullptr, 0, config.aes256);\n    } else\n#    endif\n    {\n        m_dic = new MMKVMap();\n    }\n\n    m_needLoadFromFile = true;\n    m_hasFullWriteback = false;\n\n    m_crcDigest = 0;\n\n    m_sharedProcessLock->m_enable = isMultiProcess();\n    m_exclusiveProcessLock->m_enable = isMultiProcess();\n\n    m_recoverStrategic = config.recover;\n    m_itemSizeLimit = config.itemSizeLimit;\n\n    if (config.enableKeyExpire.has_value()) {\n        configAutoExipreIfNeeded(config);\n    }\n\n    if (config.enableCompareBeforeSet) {\n        enableCompareBeforeSet();\n    }\n}\n\nMMKV::MMKV(const string &mmapID, int ashmemFD, int ashmemMetaFD, const MMKVConfig &config)\n    : m_mmapID(mmapID)\n    , m_mode(MMKV_ASHMEM)\n    , m_path(mappedKVPathWithID(m_mmapID, nullptr, MMKV_ASHMEM))\n    , m_crcPath(crcPathWithPath(m_path))\n    , m_dic(nullptr)\n    , m_dicCrypt(nullptr)\n    , m_expectedCapacity(DEFAULT_MMAP_SIZE)\n    , m_file(new MemoryFile(ashmemFD))\n    , m_metaFile(new MemoryFile(ashmemMetaFD))\n    , m_metaInfo(new MMKVMetaInfo())\n    , m_crypter(nullptr)\n    , m_lock(new ThreadLock())\n    , m_fileLock(new FileLock(m_metaFile->getFd(), true, true, 0, 1))\n    , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))\n    , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType)) {\n\n    m_actualSize = 0;\n    m_output = nullptr;\n\n#ifndef MMKV_OHOS\n    if (g_enableProcessModeCheck) {\n        // force use fcntl(), otherwise will conflict with MemoryFile::reloadFromFile()\n        m_fileModeLock = new FileLock(m_metaFile->getFd(), true, true, 1, 2);\n        m_sharedProcessModeLock = new InterProcessLock(m_fileModeLock, SharedLockType);\n    } else {\n        m_fileModeLock = nullptr;\n        m_sharedProcessModeLock = nullptr;\n    }\n    m_exclusiveProcessModeLock = nullptr;\n#endif\n\n    m_fileMigrationLock = nullptr;\n    m_sharedMigrationLock = nullptr;\n\n#    ifndef MMKV_DISABLE_CRYPT\n    auto cryptKey = config.cryptKey;\n    if (cryptKey && cryptKey->length() > 0) {\n        m_dicCrypt = new MMKVMapCrypt();\n        m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length(), nullptr, 0, config.aes256);\n    } else\n#    endif\n    {\n        m_dic = new MMKVMap();\n    }\n\n    m_needLoadFromFile = true;\n    m_hasFullWriteback = false;\n\n    m_crcDigest = 0;\n\n    m_sharedProcessLock->m_enable = true;\n    m_exclusiveProcessLock->m_enable = true;\n\n    m_recoverStrategic = config.recover;\n    m_itemSizeLimit = config.itemSizeLimit;\n\n    if (config.enableKeyExpire.has_value()) {\n        configAutoExipreIfNeeded(config);\n    }\n\n    if (config.enableCompareBeforeSet) {\n        enableCompareBeforeSet();\n    }\n}\n\n// historically Android mistakenly use mmapKey as mmapID, we try migrate back to normal when possible\nMigrateStatus tryMigrateLegacyMMKVFile(const string &mmapID, const string *rootPath, bool alreadyAbsolute) {\n    auto legacyID = legacyMmapedKVKey(mmapID, rootPath);\n    if (legacyID == mmapID) {\n        // it's not specially encoded\n        return MigrateStatus::NotSpecial;\n    }\n    auto path = mappedKVPathWithID(legacyID, rootPath, MMKV_MULTI_PROCESS, alreadyAbsolute);\n    auto targetPath = mappedKVPathWithID(mmapID, rootPath, MMKV_MULTI_PROCESS, alreadyAbsolute);\n    bool oldExit = isFileExist(path);\n    bool newExist = isFileExist(targetPath);\n    if (oldExit) {\n        if (newExist) {\n            MMKVWarning(\"both legacy file [%s] modify: %lld ms, and new file [%s] modify: %lld ms exist\",\n                        path.c_str(), getFileModifyTimeInMS(path.c_str()),\n                        targetPath.c_str(), getFileModifyTimeInMS(targetPath.c_str()));\n            return MigrateStatus::OldAndNewExist;\n        }\n        auto file = File(path, OpenFlag::ReadWrite);\n        if (file.isFileValid()) {\n            // check if it's opened by other process\n            auto fileMigrationLock = FileLock(file.getFd(), true, false, 2, 3);\n            auto exclusiveMigrationLock = InterProcessLock(&fileMigrationLock, ExclusiveLockType);\n            // works even if it's opened by us\n            if (exclusiveMigrationLock.try_lock()) {\n                if (tryAtomicRename(path, targetPath)) {\n                    if (tryAtomicRename(crcPathWithPath(path), crcPathWithPath(targetPath))) {\n                        MMKVInfo(\"Migrated legacy MMKV [%s] to [%s] in path %s\", legacyID.c_str(), mmapID.c_str(), rootPath->c_str());\n                        return MigrateStatus::OldToNewMigrated;\n                    }\n                }\n            } else {\n                MMKVInfo(\"Can't migrate legacy MMKV [%s] to [%s] in path %s, try next time.\", legacyID.c_str(), mmapID.c_str(), rootPath->c_str());\n            }\n        }\n        return MigrateStatus::OldToNewMigrateFail;\n    }\n    return newExist ? MigrateStatus::NewExist : MigrateStatus::NoneExist;\n}\n\nMMKV *MMKV::getMMKVWithID(const string &mmapID, const MMKVConfig &config) {\n    if (mmapID.empty() || !g_instanceLock) {\n        return nullptr;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    auto mmapKey = mmapedKVKey(mmapID, config.rootPath, true);\n    auto itr = g_instanceDic->find(mmapKey);\n    if (itr != g_instanceDic->end()) {\n        MMKV *kv = itr->second;\n        return kv;\n    }\n    auto rootPath = config.rootPath;\n    auto mode = config.mode;\n    if (rootPath && (rootPath != &g_realRootDir) && !(mode & MMKV_READ_ONLY)) {\n        MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;\n        if (!isFileExist(specialPath)) {\n            mkPath(specialPath);\n        }\n        MMKVInfo(\"prepare to load %s (id %s) from rootPath %s\", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());\n    }\n\n    MMKV *kv = nullptr;\n    auto migrateStatus = (mode & MMKV_ASHMEM) ? MigrateStatus::NoneExist : tryMigrateLegacyMMKVFile(mmapID, rootPath, true);\n    switch (migrateStatus) {\n        case MigrateStatus::NotSpecial:\n        case MigrateStatus::NoneExist:\n        case MigrateStatus::NewExist:\n        case MigrateStatus::OldToNewMigrated:\n        case MigrateStatus::OldAndNewExist: // TODO: shell we compare and use the latest one?\n            kv = new MMKV(mmapID, config);\n            break;\n        case MigrateStatus::OldToNewMigrateFail: {\n            auto legacyID = legacyMmapedKVKey(mmapID, rootPath);\n            kv = new MMKV(legacyID, config);\n            break;\n        }\n    }\n    kv->m_mmapKey = mmapKey;\n\n    (*g_instanceDic)[mmapKey] = kv;\n    return kv;\n}\n\nMMKV *MMKV::mmkvWithAshmemFD(const std::string &mmapID, int fd, int metaFD, const std::string *cryptKey, bool aes256) {\n    MMKVConfig config;\n    config.aes256 = aes256;\n    config.cryptKey = cryptKey;\n    return mmkvWithAshmemFD(mmapID, fd, metaFD, config);\n}\n\nMMKV *MMKV::mmkvWithAshmemFD(const string &mmapID, int fd, int metaFD, const MMKVConfig &config) {\n    if (fd < 0 || !g_instanceLock) {\n        return nullptr;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    auto itr = g_instanceDic->find(mmapID);\n    if (itr != g_instanceDic->end()) {\n        MMKV *kv = itr->second;\n#    ifndef MMKV_DISABLE_CRYPT\n        kv->checkReSetCryptKey(fd, metaFD, config.cryptKey, config.aes256);\n#    endif\n        return kv;\n    }\n    auto kv = new MMKV(mmapID, fd, metaFD, config);\n    kv->m_mmapKey = mmapID;\n    (*g_instanceDic)[mmapID] = kv;\n    return kv;\n}\n\nint MMKV::ashmemFD() {\n    return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_file->getFd() : -1;\n}\n\nint MMKV::ashmemMetaFD() {\n    return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_metaFile->getFd() : -1;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\nvoid MMKV::checkReSetCryptKey(int fd, int metaFD, const string *cryptKey, bool aes256) {\n    SCOPED_LOCK(m_lock);\n\n    checkReSetCryptKey(cryptKey, aes256);\n\n    if (m_file->m_fileType & MMFILE_TYPE_ASHMEM) {\n        if (m_file->getFd() != fd) {\n            ::close(fd);\n        }\n        if (m_metaFile->getFd() != metaFD) {\n            ::close(metaFD);\n        }\n    }\n}\n#    endif // MMKV_DISABLE_CRYPT\n\n#ifndef MMKV_OHOS\n\nbool MMKV::checkProcessMode() {\n    // avoid exception on open() error\n    if (!m_file->isFileValid()) {\n        return true;\n    }\n    // avoid invalid status\n    if (!g_enableProcessModeCheck || !m_fileModeLock || !m_sharedProcessModeLock) {\n        return true;\n    }\n\n    if (isMultiProcess()) {\n        if (!m_exclusiveProcessModeLock) {\n            m_exclusiveProcessModeLock = new InterProcessLock(m_fileModeLock, ExclusiveLockType);\n        }\n        // avoid multiple processes get shared lock at the same time, https://github.com/Tencent/MMKV/issues/523\n        auto tryAgain = false;\n        auto exclusiveLocked = m_exclusiveProcessModeLock->try_lock(&tryAgain);\n        if (exclusiveLocked) {\n            return true;\n        }\n        auto shareLocked = m_sharedProcessModeLock->try_lock();\n        if (!shareLocked) {\n            // this call will fail on most case, just do it to make sure\n            m_exclusiveProcessModeLock->try_lock();\n            return true;\n        } else {\n            if (!tryAgain) {\n                // something wrong with the OS/filesystem, let's try again\n                exclusiveLocked = m_exclusiveProcessModeLock->try_lock(&tryAgain);\n                if (!exclusiveLocked && !tryAgain) {\n                    // still something wrong, we have to give up and assume it passed the test\n                    MMKVWarning(\"Got a shared lock, but fail to exclusive lock [%s], assume it's ok\", m_mmapID.c_str());\n                    exclusiveLocked = true;\n                }\n            }\n            if (!exclusiveLocked) {\n                MMKVError(\"Got a shared lock, but fail to exclusive lock [%s]\", m_mmapID.c_str());\n            }\n            return exclusiveLocked;\n        }\n    } else {\n        auto tryAgain = false;\n        auto shareLocked = m_sharedProcessModeLock->try_lock(&tryAgain);\n        if (!shareLocked && !tryAgain) {\n            // something wrong with the OS/filesystem, we have to give up and assume it passed the test\n            MMKVWarning(\"Fail to shared lock [%s], assume it's ok\", m_mmapID.c_str());\n            shareLocked = true;\n        }\n        if (!shareLocked) {\n            MMKVError(\"Fail to share lock [%s]\", m_mmapID.c_str());\n        }\n        return shareLocked;\n    }\n}\n\nvoid MMKV::enableDisableProcessMode(bool enable) {\n    MMKVInfo(\"process mode check enable/disable: %d\", enable);\n    g_enableProcessModeCheck = enable;\n}\n\n#endif // !MMKV_OHOS\n\n#endif // MMKV_ANDROID\n"
  },
  {
    "path": "Core/MMKV_IO.cpp",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#include \"MMKV_IO.h\"\n#include \"CodedInputData.h\"\n#include \"CodedOutputData.h\"\n#include \"InterProcessLock.h\"\n#include \"MMBuffer.h\"\n#include \"MMKVLog.h\"\n#include \"MMKVMetaInfo.hpp\"\n#include \"MemoryFile.h\"\n#include \"MiniPBCoder.h\"\n#include \"PBUtility.h\"\n#include \"ScopedLock.hpp\"\n#include \"ThreadLock.h\"\n#include \"aes/AESCrypt.h\"\n#include \"aes/openssl/openssl_aes.h\"\n#include \"aes/openssl/openssl_md5.h\"\n#include \"crc32/Checksum.h\"\n#include <algorithm>\n#include <cassert>\n#include <cstring>\n#include <ctime>\n#include <filesystem>\n\n#ifdef MMKV_IOS\n#    include \"MMKV_OSX.h\"\n#endif\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif // MMKV_APPLE\n\n#ifndef MMKV_WIN32\n#    include <unistd.h>\n#endif\n\nusing namespace std;\nusing namespace mmkv;\nnamespace fs = std::filesystem;\nusing KVHolderRet_t = std::pair<bool, KeyValueHolder>;\nextern ThreadLock *g_instanceLock;\nextern unordered_map<string, MMKV *> *g_instanceDic;\nextern MMKVPath_t g_realRootDir;\n\nMMKV_NAMESPACE_BEGIN\n\nvoid MMKV::loadFromFile() {\n    loadMetaInfoAndCheck();\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        if (m_metaInfo->m_version >= MMKVVersionRandomIV) {\n            m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));\n        }\n    }\n#endif\n    if (!m_file->isFileValid()) {\n        m_file->reloadFromFile(m_expectedCapacity);\n    } else if (isMultiProcess()) {\n        // the file size may change by other process between instance creation and loadFromFile\n        // because we have lazy load\n        auto actualFileSize = m_file->getActualFileSize();\n        if (actualFileSize != m_file->getFileSize()) {\n            m_file->reloadFromFile(m_expectedCapacity);\n        }\n    }\n    if (!m_file->isFileValid()) {\n        MMKVError(\"file [%s] not valid\", m_path.c_str());\n    } else {\n        // error checking\n        bool loadFromFile = false, needFullWriteback = false;\n        checkDataValid(loadFromFile, needFullWriteback);\n        MMKVInfo(\"loading [%s] with %zu actual size, file size %zu, InterProcess %d, meta info \"\n                 \"version:%u\",\n                 m_mmapID.c_str(), m_actualSize, m_file->getFileSize(), isMultiProcess(), m_metaInfo->m_version);\n        auto ptr = (uint8_t *) m_file->getMemory();\n        // loading\n        if (loadFromFile && m_actualSize > 0) {\n            MMKVInfo(\"loading [%s] with crc %u sequence %u version %u\", m_mmapID.c_str(), m_metaInfo->m_crcDigest,\n                     m_metaInfo->m_sequence, m_metaInfo->m_version);\n            MMBuffer inputBuffer(ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);\n            if (m_crypter) {\n                clearDictionary(m_dicCrypt);\n            } else {\n                clearDictionary(m_dic);\n            }\n            if (needFullWriteback) {\n#ifndef MMKV_DISABLE_CRYPT\n                if (m_crypter) {\n                    MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter);\n                } else\n#endif\n                {\n                    MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer);\n                }\n            } else {\n#ifndef MMKV_DISABLE_CRYPT\n                if (m_crypter) {\n                    MiniPBCoder::decodeMap(*m_dicCrypt, inputBuffer, m_crypter);\n                } else\n#endif\n                {\n                    MiniPBCoder::decodeMap(*m_dic, inputBuffer);\n                }\n            }\n            m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);\n            m_output->seek(m_actualSize);\n            if (needFullWriteback && !isReadOnly()) {\n                fullWriteback();\n            }\n        } else {\n            // file not valid or empty, discard everything\n            SCOPED_LOCK(m_exclusiveProcessLock);\n\n            m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);\n            if (isReadOnly()) {\n                // do nothing\n            } else if (m_actualSize > 0) {\n                writeActualSize(0, 0, nullptr, IncreaseSequence);\n                sync(MMKV_SYNC);\n            } else {\n                writeActualSize(0, 0, nullptr, KeepSequence);\n            }\n        }\n        auto count = m_crypter ? m_dicCrypt->size() : m_dic->size();\n        MMKVInfo(\"loaded [%s] with %zu key-values\", m_mmapID.c_str(), count);\n        notifyContentLoaded();\n//        auto keys = allKeys();\n//        for (size_t index = 0; index < count; index++) {\n//            MMKVInfo(\"key[%llu]: %s\", index, keys[index].c_str());\n//        }\n    }\n\n    m_needLoadFromFile = false;\n}\n\n// read from last m_position\nvoid MMKV::partialLoadFromFile() {\n    if (!m_file->isFileValid()) {\n        return;\n    }\n    m_metaInfo->read(m_metaFile->getMemory());\n\n    size_t oldActualSize = m_actualSize;\n    m_actualSize = readActualSize();\n    auto fileSize = m_file->getFileSize();\n    MMKVDebug(\"loading [%s] with file size %zu, oldActualSize %zu, newActualSize %zu\", m_mmapID.c_str(), fileSize,\n              oldActualSize, m_actualSize);\n\n    if (m_actualSize > 0) {\n        if (m_actualSize < fileSize && m_actualSize + Fixed32Size <= fileSize) {\n            if (m_actualSize > oldActualSize) {\n                auto position = oldActualSize;\n                size_t addedSize = m_actualSize - position;\n                auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n                // incremental update crc digest\n                m_crcDigest = (uint32_t) CRC32(m_crcDigest, basePtr + position, (z_size_t) addedSize);\n                if (m_crcDigest == m_metaInfo->m_crcDigest) {\n                    MMBuffer inputBuffer(basePtr, m_actualSize, MMBufferNoCopy);\n#ifndef MMKV_DISABLE_CRYPT\n                    if (m_crypter) {\n                        MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter, position);\n                    } else\n#endif\n                    {\n                        MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer, position);\n                    }\n                    m_output->seek(addedSize);\n                    m_hasFullWriteback = false;\n\n                    [[maybe_unused]] auto count = m_crypter ? m_dicCrypt->size() : m_dic->size();\n                    MMKVDebug(\"partial loaded [%s] with %zu values\", m_mmapID.c_str(), count);\n                    return;\n                } else {\n                    MMKVError(\"m_crcDigest[%u] != m_metaInfo->m_crcDigest[%u]\", m_crcDigest, m_metaInfo->m_crcDigest);\n                }\n            }\n        }\n    }\n    // something is wrong, do a full load\n    clearMemoryCache();\n    loadFromFile();\n}\n\nstatic bool deleteOrRenameFile(const MMKVPath_t &src) {\n    if (!deleteFile(src)) {\n        fs::path path = src;\n        auto folder = path.parent_path().native();\n        auto filename = path.filename().native();\n        if (auto tmpPath = getUniqueFileName(folder, filename)) {\n            return tryAtomicRename(src, tmpPath.value());\n        }\n        return false;\n    }\n    return true;\n}\n\nbool MMKV::checkFileHasDiskError() {\n    if (m_isSecondLoad) {\n        return false;\n    }\n    m_isSecondLoad = true;\n\n    bool needReportReadFail = false;\n    if (isDiskOfMMAPFileCorrupted(m_metaFile, needReportReadFail)) {\n        m_metaFile->clearMemoryCache();\n        deleteOrRenameFile(m_metaFile->getPath());\n        m_metaFile->reloadFromFile();\n    }\n\n    if (!m_file->isFileValid()) {\n        m_file->reloadFromFile(m_expectedCapacity);\n    }\n    if (!m_file->isFileValid()) {\n        MMKVError(\"file [%s] not valid\", m_file->getPath().c_str());\n        return false;\n    }\n    if (isDiskOfMMAPFileCorrupted(m_file, needReportReadFail)) {\n        m_file->clearMemoryCache();\n        deleteOrRenameFile(m_file->getPath());\n        m_file->reloadFromFile(m_expectedCapacity);\n    }\n    return needReportReadFail;\n}\n\nvoid MMKV::loadMetaInfoAndCheck() {\n    if (!m_metaFile->isFileValid()) {\n        m_metaFile->reloadFromFile();\n    }\n    if (!m_metaFile->isFileValid()) {\n        MMKVError(\"file [%s] not valid\", m_metaFile->getPath().c_str());\n        return;\n    }\n\n    if (checkFileHasDiskError()) {\n        // let user know?\n    }\n\n    // check again, the meta file might get reloaded\n    if (!m_metaFile->isFileValid()) {\n        MMKVError(\"file [%s] not valid\", m_metaFile->getPath().c_str());\n        return;\n    }\n\n    m_metaInfo->read(m_metaFile->getMemory());\n\n    if (isReadOnly()) {\n        return;\n    }\n\n    // the meta file is in specious status\n    if (m_metaInfo->m_version >= MMKVVersionHolder) {\n        MMKVWarning(\"meta file [%s] in specious state, version %u, flags 0x%llx\", m_mmapID.c_str(),\n                    m_metaInfo->m_version, m_metaInfo->m_flags);\n\n        // MMKVVersionActualSize is the last version we don't check meta file\n        m_metaInfo->m_version = MMKVVersionActualSize;\n        m_metaInfo->m_flags = 0;\n        m_metaInfo->write(m_metaFile->getMemory());\n    }\n\n    if (m_metaInfo->m_version >= MMKVVersionFlag) {\n        m_enableKeyExpire = m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre);\n        if (m_enableKeyExpire && m_enableCompareBeforeSet) {\n            MMKVError(\"enableCompareBeforeSet will be invalid when Expiration is on\");\n            m_enableCompareBeforeSet = false;\n        }\n        MMKVInfo(\"meta file [%s] has flag [%llu]\", m_mmapID.c_str(), m_metaInfo->m_flags);\n    } else {\n        if (m_metaInfo->m_flags != 0) {\n            m_metaInfo->m_flags = 0;\n            m_metaInfo->write(m_metaFile->getMemory());\n        }\n    }\n}\n\nvoid MMKV::checkDataValid(bool &loadFromFile, bool &needFullWriteback) {\n    // try auto recover from last confirmed location\n    auto fileSize = m_file->getFileSize();\n    auto checkLastConfirmedInfo = [&] {\n        if (m_metaInfo->m_version >= MMKVVersionActualSize) {\n            // downgrade & upgrade support\n            uint32_t oldStyleActualSize = 0;\n            memcpy(&oldStyleActualSize, m_file->getMemory(), Fixed32Size);\n            if (oldStyleActualSize != m_actualSize) {\n                MMKVWarning(\"oldStyleActualSize %u not equal to meta actual size %lu\", oldStyleActualSize,\n                            m_actualSize);\n                if (oldStyleActualSize < fileSize && (oldStyleActualSize + Fixed32Size) <= fileSize) {\n                    if (checkFileCRCValid(oldStyleActualSize, m_metaInfo->m_crcDigest)) {\n                        MMKVInfo(\"looks like [%s] been downgrade & upgrade again\", m_mmapID.c_str());\n                        loadFromFile = true;\n                        writeActualSize(oldStyleActualSize, m_metaInfo->m_crcDigest, nullptr, KeepSequence);\n                        return;\n                    }\n                } else {\n                    MMKVWarning(\"oldStyleActualSize %u greater than file size %lu\", oldStyleActualSize, fileSize);\n                }\n            }\n\n            auto lastActualSize = m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize;\n            if (lastActualSize < fileSize && (lastActualSize + Fixed32Size) <= fileSize) {\n                auto lastCRCDigest = m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest;\n                if (checkFileCRCValid(lastActualSize, lastCRCDigest)) {\n                    loadFromFile = true;\n                    writeActualSize(lastActualSize, lastCRCDigest, nullptr, KeepSequence);\n                } else {\n                    MMKVError(\"check [%s] error: lastActualSize %u, lastActualCRC %u\", m_mmapID.c_str(), lastActualSize,\n                              lastCRCDigest);\n                }\n            } else {\n                MMKVError(\"check [%s] error: lastActualSize %u, file size is %u\", m_mmapID.c_str(), lastActualSize,\n                          fileSize);\n            }\n        }\n    };\n\n    m_actualSize = readActualSize();\n\n    if (m_actualSize < fileSize && (m_actualSize + Fixed32Size) <= fileSize) {\n        if (checkFileCRCValid(m_actualSize, m_metaInfo->m_crcDigest)) {\n            loadFromFile = true;\n        } else {\n            checkLastConfirmedInfo();\n            if (!loadFromFile) {\n\n                auto strategic = onMMKVCRCCheckFail(m_mmapID);\n                strategic = m_recoverStrategic.has_value() ? m_recoverStrategic.value() : strategic;\n                if (strategic == OnErrorRecover) {\n                    loadFromFile = true;\n                    needFullWriteback = true;\n                }\n                MMKVInfo(\"recover strategic for [%s] is %d\", m_mmapID.c_str(), strategic);\n            }\n        }\n    } else {\n        MMKVError(\"check [%s] error: %zu size in total, file size is %zu\", m_mmapID.c_str(), m_actualSize, fileSize);\n\n        checkLastConfirmedInfo();\n\n        if (!loadFromFile) {\n            auto strategic = onMMKVFileLengthError(m_mmapID);\n            strategic = m_recoverStrategic.has_value() ? m_recoverStrategic.value() : strategic;\n            if (strategic == OnErrorRecover) {\n                // make sure we don't over read the file\n                m_actualSize = fileSize - Fixed32Size;\n                loadFromFile = true;\n                needFullWriteback = true;\n            }\n            MMKVInfo(\"recover strategic for [%s] is %d\", m_mmapID.c_str(), strategic);\n        }\n    }\n}\n\nvoid MMKV::checkLoadData() {\n    if (m_needLoadFromFile) {\n        SCOPED_LOCK(m_sharedProcessLock);\n\n        m_needLoadFromFile = false;\n        loadFromFile();\n        return;\n    }\n    if (!isMultiProcess()) {\n        return;\n    }\n\n    if (!m_metaFile->isFileValid()) {\n        return;\n    }\n    SCOPED_LOCK(m_sharedProcessLock);\n\n    MMKVMetaInfo metaInfo;\n    metaInfo.read(m_metaFile->getMemory());\n    if (m_metaInfo->m_sequence != metaInfo.m_sequence) {\n        MMKVInfo(\"[%s] oldSeq %u, newSeq %u\", m_mmapID.c_str(), m_metaInfo->m_sequence, metaInfo.m_sequence);\n        SCOPED_LOCK(m_sharedProcessLock);\n\n        clearMemoryCache();\n        loadFromFile();\n        notifyContentChanged();\n    } else if ((m_metaInfo->m_crcDigest != metaInfo.m_crcDigest) || (m_metaInfo->m_actualSize != metaInfo.m_actualSize)) {\n        MMKVDebug(\"[%s] crcDigest %u -> %u, actualSize %u -> %u\", m_mmapID.c_str(), m_metaInfo->m_crcDigest,\n                  metaInfo.m_crcDigest, m_metaInfo->m_actualSize, metaInfo.m_actualSize);\n        SCOPED_LOCK(m_sharedProcessLock);\n\n        // looks like this is no longer needed\n        // for we inc sequence on truncate()/trim()/expandAndWriteBack()/fullWriteBack() etc\n        /*size_t fileSize = m_file->getActualFileSize();\n        if (m_file->getFileSize() != fileSize) {\n            MMKVInfo(\"file size has changed [%s] from %zu to %zu\", m_mmapID.c_str(), m_file->getFileSize(), fileSize);\n            clearMemoryCache();\n            loadFromFile();\n        } else*/ {\n            partialLoadFromFile();\n        }\n        notifyContentChanged();\n    }\n}\n\nconstexpr uint32_t ItemSizeHolderSize = 4;\n\nstatic pair<MMBuffer, size_t> prepareEncode(const MMKVMap &dic) {\n    // make some room for placeholder\n    size_t totalSize = ItemSizeHolderSize;\n    for (auto &itr : dic) {\n        auto &kvHolder = itr.second;\n        totalSize += kvHolder.computedKVSize + kvHolder.valueSize;\n    }\n    return make_pair(MMBuffer(), totalSize);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\nstatic pair<MMBuffer, size_t> prepareEncode(const MMKVMapCrypt &dic) {\n    MMKVVector vec;\n    size_t totalSize = 0;\n    // make some room for placeholder\n    uint32_t smallestOffet = 5 + 1; // 5 is the largest size needed to encode varint32\n    for (auto &itr : dic) {\n        auto &kvHolder = itr.second;\n        if (kvHolder.type == KeyValueHolderType_Offset) {\n            totalSize += kvHolder.pbKeyValueSize + kvHolder.keySize + kvHolder.valueSize;\n            smallestOffet = min(smallestOffet, kvHolder.offset);\n        } else {\n            vec.emplace_back(itr.first, kvHolder.toMMBuffer(nullptr, nullptr));\n        }\n    }\n    if (smallestOffet > 5) {\n        smallestOffet = ItemSizeHolderSize;\n    }\n    totalSize += smallestOffet;\n    if (vec.empty()) {\n        return make_pair(MMBuffer(), totalSize);\n    }\n    auto buffer = MiniPBCoder::encodeDataWithObject(vec);\n    // skip the pb size of buffer\n    auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();\n    totalSize += sizeOfMap;\n    return make_pair(std::move(buffer), totalSize);\n}\n#endif\n\nstatic pair<MMBuffer, size_t> prepareEncode(MMKVVector &&vec) {\n    // make some room for placeholder\n    size_t totalSize = ItemSizeHolderSize;\n    auto buffer = MiniPBCoder::encodeDataWithObject(vec);\n    // skip the pb size of buffer\n    auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();\n    totalSize += sizeOfMap;\n    return make_pair(std::move(buffer), totalSize);\n}\n\n// since we use append mode, when -[setData: forKey:] many times, space may not be enough\n// try a full rewrite to make space\nbool MMKV::ensureMemorySize(size_t newSize) {\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n\n    if (newSize >= m_output->spaceLeft() || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {\n        // remove expired keys\n        if (m_enableKeyExpire) {\n            filterExpiredKeys();\n        }\n        // try a full rewrite to make space\n        auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);\n        // dic.empty() means inserting key-value for the first time, no need to call msync()\n        return expandAndWriteBack(newSize, std::move(preparedData), m_crypter ? !m_dicCrypt->empty() : !m_dic->empty());\n    }\n    return true;\n}\n\nbool MMKV::checkSizeLimit(size_t size, const MMBuffer &keyData, uint32_t originKeyLength) {\n    if (m_itemSizeLimit != 0 && size > m_itemSizeLimit) {\n        auto isKeyEncoded = (originKeyLength < keyData.length());\n        uint8_t *keyPtr = nullptr;\n        if (isKeyEncoded) {\n            auto keyLen = pbRawVarint32Size(originKeyLength);\n            keyPtr = (uint8_t *) keyData.getPtr() + keyLen;\n        } else {\n            keyPtr = (uint8_t *) keyData.getPtr();\n        }\n        MMKVError(\"[%s] itemSizeLimit %u: ignore value size %zu of key [%.*s] too large\",\n                  m_mmapID.c_str(), m_itemSizeLimit, size, originKeyLength, keyPtr);\n        return false;\n    }\n    return true;\n}\n\n// try a full rewrite to make space\nbool MMKV::expandAndWriteBack(size_t newSize, std::pair<mmkv::MMBuffer, size_t> preparedData, bool needSync) {\n    auto fileSize = m_file->getFileSize();\n    auto sizeOfDic = preparedData.second;\n    size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;\n    size_t nowDicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();\n    size_t laterDicCount = std::max<size_t>(1, nowDicCount + 1);\n    // or use <cmath> ceil()\n    size_t avgItemSize = (lenNeeded + laterDicCount - 1) / laterDicCount;\n    size_t futureUsage = avgItemSize * std::max<size_t>(8, laterDicCount / 2);\n    // 1. no space for a full rewrite, double it\n    // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite\n    if (lenNeeded >= fileSize || (needSync && (lenNeeded + futureUsage) >= fileSize)) {\n        size_t oldSize = fileSize;\n        do {\n            fileSize *= 2;\n        } while (lenNeeded + futureUsage >= fileSize);\n        MMKVInfo(\"extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu\", m_mmapID.c_str(),\n                 oldSize, fileSize, newSize, futureUsage);\n\n        // if we can't extend size, rollback to old state\n        // this is a good place to mock enlarging file failure\n        if (!m_file->truncate(fileSize)) {\n            return false;\n        }\n\n        // check if we fail to make more space\n        if (!isFileValid()) {\n            MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n            return false;\n        }\n    }\n    return doFullWriteBack(std::move(preparedData), nullptr, needSync);\n}\n\nsize_t MMKV::readActualSize() {\n    if (m_metaInfo->m_version >= MMKVVersionActualSize) {\n        MMKV_ASSERT(m_metaFile->isFileValid());\n\n        return m_metaInfo->m_actualSize;\n    } else {\n        MMKV_ASSERT(m_file->isFileValid());\n\n        uint32_t actualSize = 0;\n        memcpy(&actualSize, m_file->getMemory(), Fixed32Size);\n        return actualSize;\n    }\n}\n\nvoid MMKV::oldStyleWriteActualSize(size_t actualSize) {\n    MMKV_ASSERT(m_file->getMemory());\n\n    m_actualSize = actualSize;\n    memcpy(m_file->getMemory(), &actualSize, Fixed32Size);\n}\n\nbool MMKV::writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence) {\n    if (isReadOnly()) {\n        return false;\n    }\n\n    // backward compatibility\n    if (!increaseSequence && m_metaInfo->m_version < MMKVVersionActualSize) {\n        oldStyleWriteActualSize(size);\n    }\n\n    if (!m_metaFile->isFileValid()) {\n        return false;\n    }\n\n    bool needsFullWrite = false;\n    m_actualSize = size;\n    m_metaInfo->m_actualSize = static_cast<uint32_t>(size);\n    m_crcDigest = crcDigest;\n    m_metaInfo->m_crcDigest = crcDigest;\n    if (m_metaInfo->m_version < MMKVVersionSequence) {\n        m_metaInfo->m_version = MMKVVersionSequence;\n        needsFullWrite = true;\n    }\n#ifndef MMKV_DISABLE_CRYPT\n    if (mmkv_unlikely(iv)) {\n        memcpy(m_metaInfo->m_vector, iv, sizeof(m_metaInfo->m_vector));\n        if (m_metaInfo->m_version < MMKVVersionRandomIV) {\n            m_metaInfo->m_version = MMKVVersionRandomIV;\n        }\n        needsFullWrite = true;\n    }\n#endif\n    if (mmkv_unlikely(increaseSequence)) {\n        m_metaInfo->m_sequence++;\n        m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize = static_cast<uint32_t>(size);\n        m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest = crcDigest;\n        if (m_metaInfo->m_version < MMKVVersionActualSize) {\n            m_metaInfo->m_version = MMKVVersionActualSize;\n        }\n        needsFullWrite = true;\n        MMKVInfo(\"[%s] increase sequence to %u, crc %u, actualSize %u\", m_mmapID.c_str(), m_metaInfo->m_sequence,\n                 m_metaInfo->m_crcDigest, m_metaInfo->m_actualSize);\n    }\n    if (m_metaInfo->m_version < MMKVVersionFlag) {\n        m_metaInfo->m_flags = 0;\n        m_metaInfo->m_version = MMKVVersionFlag;\n        needsFullWrite = true;\n    }\n    if (mmkv_unlikely(needsFullWrite)) {\n        m_metaInfo->write(m_metaFile->getMemory());\n    } else {\n        m_metaInfo->writeCRCAndActualSizeOnly(m_metaFile->getMemory());\n    }\n    return true;\n}\n\nMMBuffer MMKV::getRawDataForKey(MMKVKey_t key) {\n    checkLoadData();\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        auto itr = m_dicCrypt->find(key);\n        if (itr != m_dicCrypt->end()) {\n            auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n            return itr->second.toMMBuffer(basePtr, m_crypter);\n        }\n    } else\n#endif\n    {\n        auto itr = m_dic->find(key);\n        if (itr != m_dic->end()) {\n            auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n            return itr->second.toMMBuffer(basePtr);\n        }\n    }\n    MMBuffer nan;\n    return nan;\n}\n\nmmkv::MMBuffer MMKV::getDataForKey(MMKVKey_t key) {\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        return getDataWithoutMTimeForKey(key);\n    }\n    return getRawDataForKey(key);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n// for Apple watch simulator\n#    if defined(TARGET_OS_SIMULATOR) && defined(TARGET_CPU_X86)\nstatic AESCryptStatus t_status;\n#    else\nthread_local AESCryptStatus t_status;\n#    endif\n#endif // MMKV_DISABLE_CRYPT\n\nbool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {\n    if ((!isDataHolder && data.length() == 0) || isKeyEmpty(key)) {\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        if (isDataHolder) {\n            auto sizeNeededForData = pbRawVarint32Size((uint32_t) data.length()) + data.length();\n            if (!KeyValueHolderCrypt::isValueStoredAsOffset(sizeNeededForData)) {\n                data = MiniPBCoder::encodeDataWithObject(data);\n                isDataHolder = false;\n            }\n        }\n        auto itr = m_dicCrypt->find(key);\n        if (itr != m_dicCrypt->end()) {\n            bool onlyOneKey = !isMultiProcess() && m_dicCrypt->size() == 1;\n#    ifdef MMKV_APPLE\n            KVHolderRet_t ret;\n            if (onlyOneKey) {\n                ret = overrideDataWithKey(data, key, itr->second, isDataHolder);\n            } else {\n                ret = appendDataWithKey(data, key, itr->second, isDataHolder);\n            }\n#    else\n            KVHolderRet_t ret;\n            if (onlyOneKey) {\n                ret = overrideDataWithKey(data, key, isDataHolder);\n            } else {\n                ret = appendDataWithKey(data, key, isDataHolder);\n            }\n#    endif\n            if (!ret.first) {\n                return false;\n            }\n            KeyValueHolderCrypt kvHolder;\n            if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {\n                kvHolder = KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset);\n                memcpy(&kvHolder.cryptStatus, &t_status, sizeof(t_status));\n            } else {\n                kvHolder = KeyValueHolderCrypt(std::move(data));\n            }\n            if (mmkv_likely(!m_enableKeyExpire)) {\n                itr->second = std::move(kvHolder);\n            } else {\n                itr = m_dicCrypt->find(key);\n                if (itr != m_dicCrypt->end()) {\n                    itr->second = std::move(kvHolder);\n                } else {\n                    // in case filterExpiredKeys() is triggered\n                    m_dicCrypt->emplace(key, std::move(kvHolder));\n                    mmkv_retain_key(key);\n                }\n            }\n        } else {\n            bool needOverride = !isMultiProcess() && m_dicCrypt->empty() && m_actualSize > 0;\n            KVHolderRet_t ret;\n            if (needOverride) {\n                ret = overrideDataWithKey(data, key, isDataHolder);\n            } else {\n                ret = appendDataWithKey(data, key, isDataHolder);\n            }\n            if (!ret.first) {\n                return false;\n            }\n            if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {\n                auto r = m_dicCrypt->emplace(\n                    key, KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset));\n                if (r.second) {\n                    memcpy(&(r.first->second.cryptStatus), &t_status, sizeof(t_status));\n                }\n            } else {\n                m_dicCrypt->emplace(key, KeyValueHolderCrypt(std::move(data)));\n            }\n            mmkv_retain_key(key);\n        }\n    } else\n#endif // MMKV_DISABLE_CRYPT\n    {\n        auto itr = m_dic->find(key);\n        if (itr != m_dic->end()) {\n            // compare data before appending to file\n            if (isCompareBeforeSetEnabled()) {\n                auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n                MMBuffer oldValueData = itr->second.toMMBuffer(basePtr);\n                if (isDataHolder) {\n                    CodedInputData inputData(oldValueData.getPtr(), oldValueData.length());\n                    try {\n                        // read extra holder header bytes and to real MMBuffer\n                        oldValueData = CodedInputData::readRealData(oldValueData);\n                        if (oldValueData == data) {\n                            // MMKVInfo(\"[key] %s, set the same data\", key.c_str());\n                            return true;\n                        }\n                    } catch (std::exception &exception) {\n                        MMKVWarning(\"compareBeforeSet exception: %s\", exception.what());\n                    } catch (...) {\n                        MMKVWarning(\"compareBeforeSet fail\");\n                    }\n                } else {\n                    if (oldValueData == data) {\n                        //  MMKVInfo(\"[key] %s, set the same data\", key.c_str());\n                        return true;\n                    }\n                }\n            }\n\n            bool onlyOneKey = !isMultiProcess() && m_dic->size() == 1;\n            if (mmkv_likely(!m_enableKeyExpire)) {\n                KVHolderRet_t ret;\n                if (onlyOneKey) {\n                    ret = overrideDataWithKey(data, itr->second, isDataHolder);\n                } else {\n                    ret = appendDataWithKey(data, itr->second, isDataHolder);\n                }\n                if (!ret.first) {\n                    return false;\n                }\n                itr->second = std::move(ret.second);\n            } else {\n                KVHolderRet_t ret;\n                if (onlyOneKey) {\n                    ret = overrideDataWithKey(data, key, isDataHolder);\n                } else {\n                    ret = appendDataWithKey(data, key, isDataHolder);\n                }\n                if (!ret.first) {\n                    return false;\n                }\n                itr = m_dic->find(key);\n                if (itr != m_dic->end()) {\n                    itr->second = std::move(ret.second);\n                } else {\n                    // in case filterExpiredKeys() is triggered\n                    m_dic->emplace(key, std::move(ret.second));\n                    mmkv_retain_key(key);\n                }\n            }\n        } else {\n            bool needOverride = !isMultiProcess() && m_dic->empty() && m_actualSize > 0;\n            KVHolderRet_t ret;\n            if (needOverride) {\n                ret = overrideDataWithKey(data, key, isDataHolder);\n            } else {\n                ret = appendDataWithKey(data, key, isDataHolder);\n            }\n            if (!ret.first) {\n                return false;\n            }\n            m_dic->emplace(key, std::move(ret.second));\n            mmkv_retain_key(key);\n        }\n    }\n    m_hasFullWriteback = false;\n    return true;\n}\n\ntemplate <typename T>\nstatic void eraseHelper(T& container, std::string_view key) {\n    auto itr = container.find(key);\n    if (itr != container.end()) {\n        container.erase(itr);\n    }\n}\n\nbool MMKV::removeDataForKey(MMKVKey_t key) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        auto itr = m_dicCrypt->find(key);\n        if (itr != m_dicCrypt->end()) {\n            m_hasFullWriteback = false;\n            static MMBuffer nan;\n#    ifdef MMKV_APPLE\n            auto ret = appendDataWithKey(nan, key, itr->second);\n            if (ret.first) {\n                if (mmkv_unlikely(m_enableKeyExpire)) {\n                    // filterExpiredKeys() may invalid itr\n                    itr = m_dicCrypt->find(key);\n                    if (itr == m_dicCrypt->end()) {\n                        return true;\n                    }\n                }\n                auto oldKey = itr->first;\n                m_dicCrypt->erase(itr);\n                [oldKey release];\n            }\n#    else\n            auto ret = appendDataWithKey(nan, key);\n            if (ret.first) {\n                if (mmkv_unlikely(m_enableKeyExpire)) {\n                    eraseHelper(*m_dicCrypt, key);\n                } else {\n                    m_dicCrypt->erase(itr);\n                }\n            }\n#    endif\n            return ret.first;\n        }\n    } else\n#endif // MMKV_DISABLE_CRYPT\n    {\n        auto itr = m_dic->find(key);\n        if (itr != m_dic->end()) {\n            m_hasFullWriteback = false;\n            static MMBuffer nan;\n            auto ret = mmkv_likely(!m_enableKeyExpire) ? appendDataWithKey(nan, itr->second) : appendDataWithKey(nan, key);\n            if (ret.first) {\n#ifdef MMKV_APPLE\n                if (mmkv_unlikely(m_enableKeyExpire)) {\n                    // filterExpiredKeys() may invalid itr\n                    itr = m_dic->find(key);\n                    if (itr == m_dic->end()) {\n                        return true;\n                    }\n                }\n                auto oldKey = itr->first;\n                m_dic->erase(itr);\n                [oldKey release];\n#else\n                if (mmkv_unlikely(m_enableKeyExpire)) {\n                    // filterExpiredKeys() may invalid itr\n                    eraseHelper(*m_dic, key);\n                } else {\n                    m_dic->erase(itr);\n                }\n#endif\n            }\n            return ret.first;\n        }\n    }\n\n    return false;\n}\n\nKVHolderRet_t\nMMKV::doAppendDataWithKey(const MMBuffer &data, const MMBuffer &keyData, bool isDataHolder, uint32_t originKeyLength) {\n    auto isKeyEncoded = (originKeyLength < keyData.length());\n    auto keyLength = static_cast<uint32_t>(keyData.length());\n    auto valueLength = static_cast<uint32_t>(data.length());\n    if (isDataHolder) {\n        valueLength += pbRawVarint32Size(valueLength);\n    }\n    // size needed to encode the key\n    size_t size = isKeyEncoded ? keyLength : (keyLength + pbRawVarint32Size(keyLength));\n    // size needed to encode the value\n    size += valueLength + pbRawVarint32Size(valueLength);\n\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    if (!checkSizeLimit(size, keyData, originKeyLength)) {\n        return make_pair(false, KeyValueHolder());\n    }\n    bool hasEnoughSize = ensureMemorySize(size);\n    if (!hasEnoughSize || !isFileValid()) {\n        return make_pair(false, KeyValueHolder());\n    }\n\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        if (KeyValueHolderCrypt::isValueStoredAsOffset(valueLength)) {\n            m_crypter->getCurStatus(t_status);\n        }\n    }\n#endif\n    try {\n        if (isKeyEncoded) {\n            m_output->writeRawData(keyData);\n        } else {\n            m_output->writeData(keyData);\n        }\n        if (isDataHolder) {\n            m_output->writeRawVarint32((int32_t) valueLength);\n        }\n        m_output->writeData(data); // note: write size of data\n    } catch (std::exception &e) {\n        MMKVError(\"%s\", e.what());\n        return make_pair(false, KeyValueHolder());\n    } catch (...) {\n        MMKVError(\"append fail\");\n        return make_pair(false, KeyValueHolder());\n    }\n\n    auto offset = static_cast<uint32_t>(m_actualSize);\n    auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size + m_actualSize;\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        m_crypter->encrypt(ptr, ptr, size);\n    }\n#endif\n    m_actualSize += size;\n    updateCRCDigest(ptr, size);\n\n    return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));\n}\n\nKVHolderRet_t MMKV::doOverrideDataWithKey(const MMBuffer &data,\n                                          const MMBuffer &keyData,\n                                          bool isDataHolder,\n                                          uint32_t originKeyLength) {\n    auto isKeyEncoded = (originKeyLength < keyData.length());\n    auto keyLength = static_cast<uint32_t>(keyData.length());\n    auto valueLength = static_cast<uint32_t>(data.length());\n    if (isDataHolder) {\n        valueLength += pbRawVarint32Size(valueLength);\n    }\n    // size needed to encode the key\n    size_t size = isKeyEncoded ? keyLength : (keyLength + pbRawVarint32Size(keyLength));\n    // size needed to encode the value\n    size += valueLength + pbRawVarint32Size(valueLength);\n\n    if (!checkSizeLimit(size, keyData, originKeyLength)) {\n        return make_pair(false, KeyValueHolder());\n    }\n\n    if (!checkSizeForOverride(size)) {\n        return doAppendDataWithKey(data, keyData, isDataHolder, originKeyLength);\n    }\n\n    // we don't not support override in multi-process mode\n    // SCOPED_LOCK(m_exclusiveProcessLock);\n\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        if (m_metaInfo->m_version >= MMKVVersionRandomIV) {\n            m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));\n        } else {\n            m_crypter->resetIV();\n        }\n    }\n#endif\n    try {\n        // write ItemSizeHolder\n        m_output->setPosition(0);\n        m_output->writeUInt32(AESCrypt::randomItemSizeHolder(ItemSizeHolderSize));\n        m_actualSize = ItemSizeHolderSize;\n#ifndef MMKV_DISABLE_CRYPT\n        if (m_crypter) {\n            auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n            m_crypter->encrypt(ptr, ptr, m_actualSize);\n            if (KeyValueHolderCrypt::isValueStoredAsOffset(valueLength)) {\n                m_crypter->getCurStatus(t_status);\n            }\n        }\n#endif\n        if (isKeyEncoded) {\n            m_output->writeRawData(keyData);\n        } else {\n            m_output->writeData(keyData);\n        }\n        if (isDataHolder) {\n            m_output->writeRawVarint32((int32_t) valueLength);\n        }\n        m_output->writeData(data); // note: write size of data\n    } catch (std::exception &e) {\n        MMKVError(\"%s\", e.what());\n        return make_pair(false, KeyValueHolder());\n    } catch (...) {\n        MMKVError(\"append fail\");\n        return make_pair(false, KeyValueHolder());\n    }\n\n    auto offset = static_cast<uint32_t>(m_actualSize);\n    m_actualSize += size;\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size + offset;\n        m_crypter->encrypt(ptr, ptr, size);\n    }\n#endif\n    recalculateCRCDigestOnly();\n\n    return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));\n}\n\nbool MMKV::checkSizeForOverride(size_t size) {\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n\n    // only override if the file can hole it without ftruncate()\n    auto fileSize = m_file->getFileSize();\n    auto spaceNeededForOverride = size + Fixed32Size + ItemSizeHolderSize;\n    if (size > fileSize || spaceNeededForOverride > fileSize) {\n        return false;\n    }\n    return true;\n}\n\nKVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {\n#ifdef MMKV_APPLE\n    auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];\n    auto keyData = MMBuffer(oData, MMBufferNoCopy);\n#else\n    auto keyData = MMBuffer((void *) key.data(), key.size(), MMBufferNoCopy);\n#endif\n    return doAppendDataWithKey(data, keyData, isDataHolder, static_cast<uint32_t>(keyData.length()));\n}\n\nKVHolderRet_t MMKV::overrideDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {\n#ifdef MMKV_APPLE\n    auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];\n    auto keyData = MMBuffer(oData, MMBufferNoCopy);\n#else\n    auto keyData = MMBuffer((void *) key.data(), key.size(), MMBufferNoCopy);\n#endif\n    return doOverrideDataWithKey(data, keyData, isDataHolder, static_cast<uint32_t>(keyData.length()));\n}\n\nKVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    uint32_t keyLength = kvHolder.keySize;\n    // size needed to encode the key\n    size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);\n\n    // ensureMemorySize() might change kvHolder.offset, so have to do it early\n    {\n        auto valueLength = static_cast<uint32_t>(data.length());\n        if (isDataHolder) {\n            valueLength += pbRawVarint32Size(valueLength);\n        }\n        auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);\n        bool hasEnoughSize = ensureMemorySize(size);\n        if (!hasEnoughSize) {\n            return make_pair(false, KeyValueHolder());\n        }\n    }\n    auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n    MMBuffer keyData(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);\n\n    return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);\n}\n\n// only one key in dict, do not append, just rewrite from beginning\nKVHolderRet_t MMKV::overrideDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {\n    // we don't not support override in multi-process mode\n    // SCOPED_LOCK(m_exclusiveProcessLock);\n\n    uint32_t keyLength = kvHolder.keySize;\n    // size needed to encode the key\n    size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);\n\n    // ensureMemorySize() (inside doAppendDataWithKey() which be called from doOverrideDataWithKey())\n    // might change kvHolder.offset, so have to do it early\n    {\n        auto valueLength = static_cast<uint32_t>(data.length());\n        if (isDataHolder) {\n            valueLength += pbRawVarint32Size(valueLength);\n        }\n        auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);\n        bool hasEnoughSize = checkSizeForOverride(size);\n        if (!hasEnoughSize) {\n            return appendDataWithKey(data, kvHolder, isDataHolder);\n        }\n    }\n    auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n    MMBuffer keyData;\n    if (kvHolder.offset < ItemSizeHolderSize) {\n        keyData = MMBuffer(basePtr + kvHolder.offset, rawKeySize, MMBufferCopy);\n    } else {\n        keyData = MMBuffer(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);\n    }\n\n    return doOverrideDataWithKey(data, keyData, isDataHolder, keyLength);\n}\n\nbool MMKV::fullWriteback(AESCrypt *newCrypter, bool onlyWhileExpire) {\n    if (m_hasFullWriteback) {\n        return true;\n    }\n    if (m_needLoadFromFile) {\n        return true;\n    }\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n\n    if (mmkv_unlikely(m_enableKeyExpire)) {\n        auto expiredCount = filterExpiredKeys();\n        if (onlyWhileExpire && expiredCount == 0) {\n            return true;\n        }\n    }\n\n    auto isEmpty = m_crypter ? m_dicCrypt->empty() : m_dic->empty();\n    if (isEmpty) {\n        clearAll();\n        return true;\n    }\n\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);\n    auto sizeOfDic = preparedData.second;\n    if (sizeOfDic > 0) {\n        auto fileSize = m_file->getFileSize();\n        if (sizeOfDic + Fixed32Size <= fileSize) {\n            return doFullWriteBack(std::move(preparedData), newCrypter);\n        } else {\n            assert(0);\n            assert(newCrypter == nullptr);\n            // expandAndWriteBack() will extend file & full rewrite, no need to write back again\n            auto newSize = sizeOfDic + Fixed32Size - fileSize;\n            return expandAndWriteBack(newSize, std::move(preparedData));\n        }\n    }\n    return false;\n}\n\n// we don't need to really serialize the dictionary, just reuse what's already in the file\nstatic void\nmemmoveDictionary(MMKVMap &dic, CodedOutputData *output, uint8_t *ptr, AESCrypt *encrypter, size_t totalSize) {\n    auto originOutputPtr = output->curWritePointer();\n    // make space to hold the fake size of dictionary's serialization result\n    auto writePtr = originOutputPtr + ItemSizeHolderSize;\n    // reuse what's already in the file\n    if (!dic.empty()) {\n        // sort by offset\n        vector<KeyValueHolder *> vec;\n        vec.reserve(dic.size());\n        for (auto &itr : dic) {\n            vec.push_back(&itr.second);\n        }\n        sort(vec.begin(), vec.end(), [](const auto &left, const auto &right) { return left->offset < right->offset; });\n\n        // merge nearby items to make memmove quicker\n        vector<pair<uint32_t, uint32_t>> dataSections; // pair(offset, size)\n        dataSections.emplace_back(vec.front()->offset, vec.front()->computedKVSize + vec.front()->valueSize);\n        for (size_t index = 1, total = vec.size(); index < total; index++) {\n            auto kvHolder = vec[index];\n            auto &lastSection = dataSections.back();\n            if (kvHolder->offset == lastSection.first + lastSection.second) {\n                lastSection.second += kvHolder->computedKVSize + kvHolder->valueSize;\n            } else {\n                dataSections.emplace_back(kvHolder->offset, kvHolder->computedKVSize + kvHolder->valueSize);\n            }\n        }\n        // do the move\n        auto basePtr = ptr + Fixed32Size;\n        for (auto &section : dataSections) {\n            // memmove() should handle this well: src == dst\n            memmove(writePtr, basePtr + section.first, section.second);\n            writePtr += section.second;\n        }\n        // update offset\n        if (!encrypter) {\n            auto offset = ItemSizeHolderSize;\n            for (auto kvHolder : vec) {\n                kvHolder->offset = offset;\n                offset += kvHolder->computedKVSize + kvHolder->valueSize;\n            }\n        }\n    }\n    // hold the fake size of dictionary's serialization result\n    output->writeUInt32(AESCrypt::randomItemSizeHolder(ItemSizeHolderSize));\n    auto writtenSize = static_cast<size_t>(writePtr - originOutputPtr);\n#ifndef MMKV_DISABLE_CRYPT\n    if (encrypter) {\n        encrypter->encrypt(originOutputPtr, originOutputPtr, writtenSize);\n    }\n#endif\n    assert(writtenSize == totalSize);\n    output->seek(writtenSize - ItemSizeHolderSize);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nstatic void memmoveDictionary(MMKVMapCrypt &dic,\n                              CodedOutputData *output,\n                              uint8_t *ptr,\n                              AESCrypt *decrypter,\n                              AESCrypt *encrypter,\n                              pair<MMBuffer, size_t> &preparedData) {\n    // reuse what's already in the file\n    vector<KeyValueHolderCrypt *> vec;\n    if (!dic.empty()) {\n        // sort by offset\n        vec.reserve(dic.size());\n        for (auto &itr : dic) {\n            if (itr.second.type == KeyValueHolderType_Offset) {\n                vec.push_back(&itr.second);\n            }\n        }\n        sort(vec.begin(), vec.end(), [](auto left, auto right) { return left->offset < right->offset; });\n    }\n    auto sizeHolderSize = ItemSizeHolderSize;\n    auto sizeHolder = AESCrypt::randomItemSizeHolder(sizeHolderSize);\n    if (!vec.empty()) {\n        auto smallestOffset = vec.front()->offset;\n        if (smallestOffset != ItemSizeHolderSize && smallestOffset <= 5) {\n            sizeHolderSize = smallestOffset;\n            assert(sizeHolderSize != 0);\n            static const uint32_t ItemSizeHolders[] = {0, 0x0f, 0xff, 0xffff, 0xffffff, 0xffffffff};\n            sizeHolder = AESCrypt::randomItemSizeHolder(sizeHolderSize);\n            assert(sizeHolder >= ItemSizeHolders[sizeHolderSize] && sizeHolder <= ItemSizeHolders[sizeHolderSize]);\n        }\n    }\n    output->writeRawVarint32(static_cast<int32_t>(sizeHolder));\n    auto writePtr = output->curWritePointer();\n    if (encrypter) {\n        encrypter->encrypt(writePtr - sizeHolderSize, writePtr - sizeHolderSize, sizeHolderSize);\n    }\n    if (!vec.empty()) {\n        // merge nearby items to make memmove quicker\n        vector<tuple<uint32_t, uint32_t, AESCryptStatus *>> dataSections; // pair(offset, size)\n        dataSections.push_back(vec.front()->toTuple());\n        for (size_t index = 1, total = vec.size(); index < total; index++) {\n            auto kvHolder = vec[index];\n            auto &lastSection = dataSections.back();\n            if (kvHolder->offset == get<0>(lastSection) + get<1>(lastSection)) {\n                get<1>(lastSection) += kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;\n            } else {\n                dataSections.push_back(kvHolder->toTuple());\n            }\n        }\n        // do the move\n        auto basePtr = ptr + Fixed32Size;\n        for (auto &section : dataSections) {\n            auto crypter = decrypter->cloneWithStatus(*get<2>(section));\n            crypter.decrypt(basePtr + get<0>(section), writePtr, get<1>(section));\n            writePtr += get<1>(section);\n        }\n        // update offset & AESCryptStatus\n        if (encrypter) {\n            auto offset = sizeHolderSize;\n            for (auto kvHolder : vec) {\n                kvHolder->offset = offset;\n                auto size = kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;\n                encrypter->getCurStatus(kvHolder->cryptStatus);\n                encrypter->encrypt(basePtr + offset, basePtr + offset, size);\n                offset += size;\n            }\n        }\n    }\n    auto &data = preparedData.first;\n    if (data.length() > 0) {\n        auto dataSize = CodedInputData(data.getPtr(), data.length()).readUInt32();\n        if (dataSize > 0) {\n            auto dataPtr = (uint8_t *) data.getPtr() + pbRawVarint32Size(dataSize);\n            if (encrypter) {\n                encrypter->encrypt(dataPtr, writePtr, dataSize);\n            } else {\n                memcpy(writePtr, dataPtr, dataSize);\n            }\n            writePtr += dataSize;\n        }\n    }\n    auto writtenSize = static_cast<size_t>(writePtr - output->curWritePointer());\n    assert(writtenSize + sizeHolderSize == preparedData.second);\n    output->seek(writtenSize);\n}\n\n#    define InvalidCryptPtr ((AESCrypt *) (void *) (1))\n\n#endif // MMKV_DISABLE_CRYPT\n\nstatic void fullWriteBackWholeData(MMBuffer allData, size_t totalSize, CodedOutputData *output) {\n    auto originOutputPtr = output->curWritePointer();\n    output->writeUInt32(AESCrypt::randomItemSizeHolder(ItemSizeHolderSize));\n    if (allData.length() > 0) {\n        auto dataSize = CodedInputData(allData.getPtr(), allData.length()).readUInt32();\n        if (dataSize > 0) {\n            auto dataPtr = (uint8_t *) allData.getPtr() + pbRawVarint32Size(dataSize);\n            memcpy(output->curWritePointer(), dataPtr, dataSize);\n            output->seek(dataSize);\n        }\n    }\n    [[maybe_unused]] auto writtenSize = (size_t)(output->curWritePointer() - originOutputPtr);\n    assert(writtenSize == totalSize);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\nbool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *newCrypter, bool needSync) {\n    auto ptr = (uint8_t *) m_file->getMemory();\n    auto totalSize = prepared.second;\n\n    uint8_t newIV[AES_IV_LEN];\n    auto encrypter = (newCrypter == InvalidCryptPtr) ? nullptr : (newCrypter ? newCrypter : m_crypter);\n    if (encrypter) {\n        AESCrypt::fillRandomIV(newIV);\n        encrypter->resetIV(newIV, sizeof(newIV));\n    }\n\n    delete m_output;\n    m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);\n    if (m_crypter) {\n        auto decrypter = m_crypter;\n        memmoveDictionary(*m_dicCrypt, m_output, ptr, decrypter, encrypter, prepared);\n    } else if (prepared.first.length() != 0) {\n        auto &preparedData = prepared.first;\n        fullWriteBackWholeData(std::move(preparedData), totalSize, m_output);\n        if (encrypter) {\n            encrypter->encrypt(ptr + Fixed32Size, ptr + Fixed32Size, totalSize);\n        }\n    } else {\n        memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);\n    }\n\n    m_actualSize = totalSize;\n    if (encrypter) {\n        recalculateCRCDigestWithIV(newIV);\n    } else {\n        recalculateCRCDigestWithIV(nullptr);\n    }\n    m_hasFullWriteback = true;\n    // make sure lastConfirmedMetaInfo is saved if needed\n    if (needSync) {\n        sync(MMKV_SYNC);\n    }\n    return true;\n}\n\n#else // MMKV_DISABLE_CRYPT\n\nbool MMKV::doFullWriteBack(pair<MMBuffer, size_t> prepared, AESCrypt *, bool needSync) {\n    auto ptr = (uint8_t *) m_file->getMemory();\n    auto totalSize = prepared.second;\n\n    delete m_output;\n    m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);\n    if (prepared.first.length() != 0) {\n        auto &preparedData = prepared.first;\n        fullWriteBackWholeData(std::move(preparedData), totalSize, m_output);\n    } else {\n        constexpr AESCrypt *encrypter = nullptr;\n        memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);\n    }\n\n    m_actualSize = totalSize;\n    recalculateCRCDigestWithIV(nullptr);\n    m_hasFullWriteback = true;\n    // make sure lastConfirmedMetaInfo is saved if needed\n    if (needSync) {\n        sync(MMKV_SYNC);\n    }\n    return true;\n}\n#endif // MMKV_DISABLE_CRYPT\n\n#ifndef MMKV_DISABLE_CRYPT\nbool MMKV::reKey(const string &cryptKey, bool aes256) {\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n\n    bool ret = false;\n    if (m_crypter) {\n        if (cryptKey.length() > 0) {\n            string oldKey = this->cryptKey();\n            if (cryptKey == oldKey) {\n                return true;\n            } else {\n                // change encryption key\n                MMKVInfo(\"reKey with new aes key\");\n                auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length(), nullptr, 0, aes256);\n                m_hasFullWriteback = false;\n                ret = fullWriteback(newCrypt);\n                if (ret) {\n                    delete m_crypter;\n                    m_crypter = newCrypt;\n                } else {\n                    delete newCrypt;\n                }\n            }\n        } else {\n            // decryption to plain text\n            MMKVInfo(\"reKey to no aes key\");\n            m_hasFullWriteback = false;\n            ret = fullWriteback(InvalidCryptPtr);\n            if (ret) {\n                delete m_crypter;\n                m_crypter = nullptr;\n                if (!m_dic) {\n                    m_dic = new MMKVMap();\n                }\n            }\n        }\n    } else {\n        if (cryptKey.length() > 0) {\n            // transform plain text to encrypted text\n            MMKVInfo(\"reKey to a aes key\");\n            m_hasFullWriteback = false;\n            auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length(), nullptr, 0, aes256);\n            ret = fullWriteback(newCrypt);\n            if (ret) {\n                m_crypter = newCrypt;\n                if (!m_dicCrypt) {\n                    m_dicCrypt = new MMKVMapCrypt();\n                }\n            } else {\n                delete newCrypt;\n            }\n        } else {\n            return true;\n        }\n    }\n    // m_dic or m_dicCrypt is not valid after reKey\n    if (ret) {\n        clearMemoryCache();\n    }\n    return ret;\n}\n#endif\n\nvoid MMKV::trim() {\n    MMKVInfo(\"prepare to trim %s\", m_mmapID.c_str());\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return;\n    }\n\n    if (m_actualSize == 0) {\n        clearAll();\n        return;\n    } else if (m_file->getFileSize() <= m_expectedCapacity) {\n        return;\n    }\n\n    fullWriteback();\n    auto oldSize = m_file->getFileSize();\n    auto fileSize = oldSize;\n    while (fileSize > (m_actualSize + Fixed32Size) * 2) {\n        fileSize /= 2;\n    }\n    fileSize = std::max<size_t>(fileSize, m_expectedCapacity);\n    if (oldSize == fileSize) {\n        MMKVInfo(\"there's no need to trim %s with size %zu, actualSize %zu\", m_mmapID.c_str(), fileSize, m_actualSize);\n        return;\n    }\n\n    MMKVInfo(\"trimming %s from %zu to %zu, actualSize %zu\", m_mmapID.c_str(), oldSize, fileSize, m_actualSize);\n\n    if (!m_file->truncate(fileSize)) {\n        return;\n    }\n    fileSize = m_file->getFileSize();\n    auto ptr = (uint8_t *) m_file->getMemory();\n    delete m_output;\n    m_output = new CodedOutputData(ptr + pbFixed32Size(), fileSize - Fixed32Size);\n    m_output->seek(m_actualSize);\n\n    MMKVInfo(\"finish trim %s from %zu to %zu\", m_mmapID.c_str(), oldSize, fileSize);\n}\n\nvoid MMKV::clearAll(bool keepSpace) {\n    MMKVInfo(\"cleaning all key-values from [%s]\", m_mmapID.c_str());\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n    if (!isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return;\n    }\n\n    if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {\n        MMKVInfo(\"nothing to clear for [%s]\", m_mmapID.c_str());\n        return;\n    }\n\n    if (!keepSpace) {\n        m_file->truncate(m_expectedCapacity);\n    }\n\n#ifndef MMKV_DISABLE_CRYPT\n    uint8_t newIV[AES_IV_LEN];\n    AESCrypt::fillRandomIV(newIV);\n    if (m_crypter) {\n        m_crypter->resetIV(newIV, sizeof(newIV));\n    }\n    writeActualSize(0, 0, newIV, IncreaseSequence);\n#else\n    writeActualSize(0, 0, nullptr, IncreaseSequence);\n#endif\n\n    m_metaFile->msync(MMKV_SYNC);\n\n    clearMemoryCache(keepSpace);\n    loadFromFile();\n}\n\nsize_t MMKV::importFrom(MMKV *src) {\n    if (!src) {\n        return 0;\n    }\n    MMKVInfo(\"importing from [%s] to [%s]\", src->m_mmapID.c_str(), m_mmapID.c_str());\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return 0;\n    }\n\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    SCOPED_LOCK(src->m_lock);\n    SCOPED_LOCK(src->m_exclusiveProcessLock);\n\n    checkLoadData();\n    src->checkLoadData();\n    if (!isFileValid() || !src->isFileValid()) {\n        MMKVWarning(\"[%s] or [%s] file not valid\", m_mmapID.c_str(), src->m_mmapID.c_str());\n        return 0;\n    }\n\n    size_t count = 0;\n    bool notAutoExpire = !m_enableKeyExpire;\n    auto time = UInt32ToInt32((m_expiredInSeconds != ExpireNever) ? getCurrentTimeInSecond() + m_expiredInSeconds : ExpireNever);\n    for (auto &key : src->allKeys(false)) {\n        auto value = src->getDataForKey(key);\n        if (value.length() > 0) {\n            if (mmkv_likely(notAutoExpire)) {\n                setDataForKey(std::move(value), key, false);\n            } else {\n                auto tmp = MMBuffer(value.length() + Fixed32Size);\n                CodedOutputData output(tmp.getPtr(), tmp.length());\n                // no need write size, it's already written in value\n                output.writeRawData(value);\n                output.writeRawLittleEndian32(time);\n                setDataForKey(std::move(tmp), key, false);\n            }\n            count++;\n        }\n    }\n\n    MMKVInfo(\"imported %llu from [%s] to [%s]\", count, src->m_mmapID.c_str(), m_mmapID.c_str());\n    return count;\n}\n\nstatic std::pair<MMKVPath_t, MMKVPath_t> getStorage(const std::string &mmapID, const MMKVPath_t *relatePath, std::string& realID, std::string& mmapKey) {\n    relatePath = relatePath ? relatePath : &g_realRootDir;\n    auto ns = MMKV::nameSpace(*relatePath);\n    relatePath = &ns.getRootDir();\n#ifdef MMKV_ANDROID\n    auto migrateStatus = tryMigrateLegacyMMKVFile(mmapID, relatePath, true);\n    if (migrateStatus == MigrateStatus::NoneExist) {\n        MMKVWarning(\"file id [%s] not exist in path %s\", mmapID.c_str(), relatePath->c_str());\n        return {};\n    } else if (migrateStatus == MigrateStatus::OldToNewMigrateFail) {\n        realID = legacyMmapedKVKey(mmapID, relatePath);\n    } else {\n        realID = mmapID;\n    }\n    MMKVPath_t kvPath = mappedKVPathWithID(realID, relatePath, MMKV_MULTI_PROCESS, true);\n#else\n    realID = mmapID;\n    MMKVPath_t kvPath = mappedKVPathWithID(realID, relatePath, true);\n#endif\n    mmapKey = mmapedKVKey(realID, relatePath, true);\n    MMKVDebug(\"mmapKey %s, real ID %s\", mmapKey.c_str(), realID.c_str());\n\n    MMKVPath_t crcPath = crcPathWithPath(kvPath);\n    if (!isFileExist(kvPath)) {\n        const auto &kvPathUTF8 = MMKVPath_t2String(kvPath);\n        MMKVInfo(\"file not exist %s\", kvPathUTF8.c_str());\n        kvPath.resize(0);\n    }\n    if (!isFileExist(crcPath)) {\n        const auto &crcPathUTF8 = MMKVPath_t2String(crcPath);\n        MMKVInfo(\"crc file not exist %s\", crcPathUTF8.c_str());\n        crcPath.resize(0);\n    }\n    return {kvPath, crcPath};\n}\n\nbool MMKV::isFileValid(const string &mmapID, const MMKVPath_t *relatePath) {\n    if (!g_instanceLock) {\n        return false;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    std::string realID, mmapKey;\n    auto [kvPath, crcPath] = getStorage(mmapID, relatePath, realID, mmapKey);\n    if (kvPath.empty()) {\n        return true;\n    }\n    if (crcPath.empty()) {\n        return false;\n    }\n\n    uint32_t crcFile = 0;\n    MMBuffer *data = readWholeFile(crcPath);\n    if (data) {\n        if (data->getPtr()) {\n            MMKVMetaInfo metaInfo;\n            metaInfo.read(data->getPtr());\n            crcFile = metaInfo.m_crcDigest;\n        }\n        delete data;\n    } else {\n        return false;\n    }\n\n    uint32_t crcDigest = 0;\n    MMBuffer *fileData = readWholeFile(kvPath);\n    if (fileData) {\n        if (fileData->getPtr() && (fileData->length() >= Fixed32Size)) {\n            uint32_t actualSize = 0;\n            memcpy(&actualSize, fileData->getPtr(), Fixed32Size);\n            if (actualSize > (fileData->length() - Fixed32Size)) {\n                delete fileData;\n                return false;\n            }\n\n            crcDigest = (uint32_t) CRC32(0, (const uint8_t *) fileData->getPtr() + Fixed32Size, (uint32_t) actualSize);\n        }\n        delete fileData;\n        return crcFile == crcDigest;\n    } else {\n        return false;\n    }\n}\n\nbool MMKV::removeStorage(const std::string &mmapID, const MMKVPath_t *relatePath) {\n    if (!g_instanceLock) {\n        return false;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    std::string realID, mmapKey;\n    auto [kvPath, crcPath] = getStorage(mmapID, relatePath, realID, mmapKey);\n    if (kvPath.empty() && crcPath.empty()) {\n        return false;\n    }\n    MMKVInfo(\"remove storage [%s]\", realID.c_str());\n\n    if (crcPath.empty()) {\n        deleteFile(kvPath);\n        return true;\n    }\n\n    File crcFile(crcPath, OpenFlag::ReadOnly);\n    if (!crcFile.isFileValid()) {\n        deleteFile(kvPath);\n        return true;\n    }\n    FileLock fileLock(crcFile.getFd());\n    InterProcessLock lock(&fileLock, ExclusiveLockType);\n    SCOPED_LOCK(&lock);\n\n    auto itr = g_instanceDic->find(mmapKey);\n    if (itr != g_instanceDic->end()) {\n        itr->second->close();\n        // itr is not valid after this\n    }\n\n    deleteFile(kvPath);\n    deleteFile(crcPath);\n\n    return true;\n}\n\nbool MMKV::checkExist(const std::string &mmapID, const MMKVPath_t *relatePath) {\n    if (!g_instanceLock) {\n        return false;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    std::string realID, mmapKey;\n    auto [kvPath, crcPath] = getStorage(mmapID, relatePath, realID, mmapKey);\n    return (!kvPath.empty() && !crcPath.empty());\n}\n\n// ---- auto expire ----\n\nuint32_t MMKV::getCurrentTimeInSecond() {\n    auto time = ::time(nullptr);\n    return static_cast<uint32_t>(time);\n}\n\nbool MMKV::doFullWriteBack(MMKVVector &&vec) {\n    auto preparedData = prepareEncode(std::move(vec));\n\n    // must clean before write-back and after prepareEncode()\n    if (m_crypter) {\n        clearDictionary(m_dicCrypt);\n    } else {\n        clearDictionary(m_dic);\n    }\n\n    bool ret = false;\n    auto sizeOfDic = preparedData.second;\n    auto fileSize = m_file->getFileSize();\n    if (sizeOfDic + Fixed32Size <= fileSize) {\n        ret = doFullWriteBack(std::move(preparedData), nullptr);\n    } else {\n        // expandAndWriteBack() will extend file & full rewrite, no need to write back again\n        auto newSize = sizeOfDic + Fixed32Size - fileSize;\n        ret = expandAndWriteBack(newSize, std::move(preparedData));\n    }\n\n    clearMemoryCache();\n    return ret;\n}\n\nvoid MMKV::configAutoExipreIfNeeded(const MMKVConfig &config) {\n    if (!config.enableKeyExpire.has_value()) {\n        return;\n    }\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    // it will set m_enableKeyExpire from meta file\n    loadMetaInfoAndCheck();\n\n    if (!m_metaFile->isFileValid()) {\n        return;\n    }\n\n    if (m_enableKeyExpire) {\n        if (config.enableKeyExpire.value()) {\n            m_expiredInSeconds = config.expiredInSeconds;\n        } else {\n            disableAutoKeyExpire();\n        }\n    } else {\n        if (config.enableKeyExpire.value()) {\n            enableAutoKeyExpire(config.expiredInSeconds);\n        } else {\n            // no action\n        }\n    }\n}\n\nbool MMKV::enableAutoKeyExpire(uint32_t expiredInSeconds) {\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n    if (!isFileValid() || !m_metaFile->isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n\n    if (m_enableCompareBeforeSet) {\n        MMKVError(\"enableCompareBeforeSet will be invalid when Expiration is on\");\n        m_enableCompareBeforeSet = false;\n    }\n\n    if (m_expiredInSeconds != expiredInSeconds) {\n        MMKVInfo(\"expiredInSeconds: %u\", expiredInSeconds);\n        m_expiredInSeconds = expiredInSeconds;\n    }\n    m_enableKeyExpire = true;\n    if (m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre)) {\n        return true;\n    }\n\n    auto autoRecordExpireTime = (m_expiredInSeconds != 0);\n    auto time = autoRecordExpireTime ? getCurrentTimeInSecond() + m_expiredInSeconds : 0;\n    MMKVInfo(\"turn on recording expire date for all keys inside [%s] from now %u\", m_mmapID.c_str(), time);\n    m_metaInfo->setFlag(MMKVMetaInfo::EnableKeyExipre);\n    m_metaInfo->m_version = MMKVVersionFlag;\n\n    if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {\n        MMKVInfo(\"file is new, don't need a full writeback [%s], just update meta file\", m_mmapID.c_str());\n        writeActualSize(0, 0, nullptr, IncreaseSequence);\n        m_metaFile->msync(MMKV_SYNC);\n        return true;\n    }\n\n    MMKVVector vec;\n    auto packKeyValue = [&](const auto &key, const MMBuffer &value) {\n        MMBuffer data(value.length() + Fixed32Size);\n        auto ptr = (uint8_t *) data.getPtr();\n        memcpy(ptr, value.getPtr(), value.length());\n        memcpy(ptr + value.length(), &time, Fixed32Size);\n        vec.emplace_back(key, std::move(data));\n    };\n\n    auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        for (auto &pair : *m_dicCrypt) {\n            auto &key = pair.first;\n            auto &value = pair.second;\n            auto buffer = value.toMMBuffer(basePtr, m_crypter);\n            packKeyValue(key, buffer);\n        }\n    } else\n#endif\n    {\n        for (auto &pair : *m_dic) {\n            auto &key = pair.first;\n            auto &value = pair.second;\n            auto buffer = value.toMMBuffer(basePtr);\n            packKeyValue(key, buffer);\n        }\n    }\n\n    return doFullWriteBack(std::move(vec));\n}\n\nbool MMKV::disableAutoKeyExpire() {\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n    if (!isFileValid() || !m_metaFile->isFileValid()) {\n        MMKVWarning(\"[%s] file not valid\", m_mmapID.c_str());\n        return false;\n    }\n\n    m_expiredInSeconds = 0;\n    m_enableKeyExpire = false;\n    if (!m_metaInfo->hasFlag(MMKVMetaInfo::EnableKeyExipre)) {\n        return true;\n    }\n\n    MMKVInfo(\"erase previous recorded expire date for all keys inside [%s]\", m_mmapID.c_str());\n    m_metaInfo->unsetFlag(MMKVMetaInfo::EnableKeyExipre);\n    m_metaInfo->m_version = MMKVVersionFlag;\n\n    if (m_file->getFileSize() == m_expectedCapacity && m_actualSize == 0) {\n        MMKVInfo(\"file is new, don't need a full write-back [%s], just update meta file\", m_mmapID.c_str());\n        writeActualSize(0, 0, nullptr, IncreaseSequence);\n        m_metaFile->msync(MMKV_SYNC);\n        return true;\n    }\n\n    MMKVVector vec;\n    auto packKeyValue = [&](auto &key, const MMBuffer &value) {\n        assert(value.length() >= Fixed32Size);\n        if (value.length() < Fixed32Size) {\n#ifdef MMKV_APPLE\n            MMKVWarning(\"key [%@] has invalid value size %u\", key, value.length());\n#else\n            MMKVWarning(\"key [%s] has invalid value size %u\", key.data(), value.length());\n#endif\n            return;\n        }\n        MMBuffer data(value.length() - Fixed32Size);\n        auto ptr = (uint8_t *) data.getPtr();\n        memcpy(ptr, value.getPtr(), value.length() - Fixed32Size);\n        vec.emplace_back(key, std::move(data));\n    };\n\n    auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        for (auto &pair : *m_dicCrypt) {\n            auto &key = pair.first;\n            auto &value = pair.second;\n            auto buffer = value.toMMBuffer(basePtr, m_crypter);\n            packKeyValue(key, buffer);\n        }\n    } else\n#endif\n    {\n        for (auto &pair : *m_dic) {\n            auto &key = pair.first;\n            auto &value = pair.second;\n            auto buffer = value.toMMBuffer(basePtr);\n            packKeyValue(key, buffer);\n        }\n    }\n\n    return doFullWriteBack(std::move(vec));\n}\n\nuint32_t MMKV::getExpireTimeForKey(MMKVKey_t key) {\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    checkLoadData();\n\n    if (!m_enableKeyExpire || mmkv_key_length(key) == 0) {\n        return 0;\n    }\n    auto raw = getRawDataForKey(key);\n    assert(raw.length() == 0 || raw.length() >= Fixed32Size);\n    if (raw.length() < Fixed32Size) {\n        if (raw.length() != 0) {\n#ifdef MMKV_APPLE\n            MMKVWarning(\"key [%@] has invalid value size %u\", key, raw.length());\n#else\n            MMKVWarning(\"key [%s] has invalid value size %u\", key.data(), raw.length());\n#endif\n        }\n        return 0;\n    }\n    auto ptr = (const uint8_t *) raw.getPtr() + raw.length() - Fixed32Size;\n    auto time = *(const uint32_t *) ptr;\n    return time;\n}\n\nmmkv::MMBuffer MMKV::getDataWithoutMTimeForKey(MMKVKey_t key) {\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    checkLoadData();\n\n    auto raw = getRawDataForKey(key);\n    assert(raw.length() == 0 || raw.length() >= Fixed32Size);\n    if (raw.length() < Fixed32Size) {\n        if (raw.length() != 0) {\n#ifdef MMKV_APPLE\n            MMKVWarning(\"key [%@] has invalid value size %u\", key, raw.length());\n#else\n            MMKVWarning(\"key [%s] has invalid value size %u\", key.data(), raw.length());\n#endif\n        }\n        return raw;\n    }\n    auto newLength = raw.length() - Fixed32Size;\n    if (m_enableKeyExpire) {\n        auto ptr = (const uint8_t *) raw.getPtr() + newLength;\n        auto time = *(const uint32_t *) ptr;\n        if (time != ExpireNever && time <= getCurrentTimeInSecond()) {\n#ifdef MMKV_APPLE\n            MMKVInfo(\"deleting expired key [%@] in mmkv [%s], due date %u\", key, m_mmapID.c_str(), time);\n#else\n            MMKVInfo(\"deleting expired key [%s] in mmkv [%s], due date %u\", key.data(), m_mmapID.c_str(), time);\n#endif\n            removeValueForKey(key);\n            return MMBuffer();\n        }\n    }\n    return MMBuffer(std::move(raw), newLength);\n}\n\n#define NOOP ((void) 0)\n\nsize_t MMKV::filterExpiredKeys() {\n    if (!m_enableKeyExpire || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {\n        return 0;\n    }\n    SCOPED_LOCK(m_sharedProcessLock);\n\n    auto now = getCurrentTimeInSecond();\n    MMKVInfo(\"filtering expired keys inside [%s] now: %u, m_expiredInSeconds: %u\", m_mmapID.c_str(), now,\n             m_expiredInSeconds);\n\n    size_t count = 0;\n    auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;\n#ifndef MMKV_DISABLE_CRYPT\n    if (m_crypter) {\n        for (auto itr = m_dicCrypt->begin(); itr != m_dicCrypt->end(); NOOP) {\n            auto &kvHolder = itr->second;\n            assert(kvHolder.realValueSize() >= Fixed32Size);\n            if (kvHolder.realValueSize() < Fixed32Size) {\n#ifdef MMKV_APPLE\n                MMKVWarning(\"key [%@] has invalid value size %u\", itr->first, kvHolder.realValueSize());\n#else\n                MMKVWarning(\"key [%s] has invalid value size %u\", itr->first.c_str(), kvHolder.realValueSize());\n#endif\n                itr++;\n                continue;\n            }\n            auto buffer = kvHolder.toMMBuffer(basePtr, m_crypter);\n            auto ptr = (uint8_t *) buffer.getPtr();\n            ptr += buffer.length() - Fixed32Size;\n            auto time = *(const uint32_t *) ptr;\n            if (time != ExpireNever && time <= now) {\n                auto oldKey = itr->first;\n                itr = m_dicCrypt->erase(itr);\n#    ifdef MMKV_APPLE\n                MMKVInfo(\"deleting expired key [%@], due date %u\", oldKey, time);\n                [oldKey release];\n#    else\n                MMKVInfo(\"deleting expired key [%s], due date %u\", oldKey.c_str(), time);\n#    endif\n                count++;\n            } else {\n                itr++;\n            }\n        }\n    } else\n#endif // !MMKV_DISABLE_CRYPT\n    {\n        for (auto itr = m_dic->begin(); itr != m_dic->end(); NOOP) {\n            auto &kvHolder = itr->second;\n            assert(kvHolder.valueSize >= Fixed32Size);\n            if (kvHolder.valueSize < Fixed32Size) {\n#ifdef MMKV_APPLE\n                MMKVWarning(\"key [%@] has invalid value size %u\", itr->first, kvHolder.valueSize);\n#else\n                MMKVWarning(\"key [%s] has invalid value size %u\", itr->first.c_str(), kvHolder.valueSize);\n#endif\n                itr++;\n                continue;\n            }\n            auto ptr = basePtr + kvHolder.offset + kvHolder.computedKVSize;\n            ptr += kvHolder.valueSize - Fixed32Size;\n            auto time = *(const uint32_t *) ptr;\n            if (time != ExpireNever && time <= now) {\n                auto oldKey = itr->first;\n                itr = m_dic->erase(itr);\n#ifdef MMKV_APPLE\n                MMKVInfo(\"deleting expired key [%@], due date %u\", oldKey, time);\n                [oldKey release];\n#else\n                MMKVInfo(\"deleting expired key [%s], due date %u\", oldKey.c_str(), time);\n#endif\n                count++;\n            } else {\n                itr++;\n            }\n        }\n    }\n    if (count != 0) {\n        MMKVInfo(\"deleted %zu expired keys inside [%s]\", count, m_mmapID.c_str());\n    }\n    return count;\n}\n\nbool MMKV::enableCompareBeforeSet() {\n    MMKVInfo(\"enableCompareBeforeSet for [%s]\", m_mmapID.c_str());\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    assert(!m_enableKeyExpire && \"enableCompareBeforeSet is invalid when Expiration is on\");\n    assert(!m_crypter && \"enableCompareBeforeSet is invalid when key encryption is on\");\n    if (m_enableKeyExpire || m_crypter) {\n        return false;\n    }\n\n    m_enableCompareBeforeSet = true;\n    return true;\n}\n\nbool MMKV::disableCompareBeforeSet() {\n    MMKVInfo(\"disableCompareBeforeSet for [%s]\", m_mmapID.c_str());\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    assert(!m_enableKeyExpire && \"disableCompareBeforeSet is invalid when Expiration is on\");\n    assert(!m_crypter && \"disableCompareBeforeSet is invalid when key encryption is on\");\n    if (m_enableKeyExpire || m_crypter) {\n        return false;\n    }\n\n    m_enableCompareBeforeSet = false;\n    return true;\n}\n\nMMKV_NAMESPACE_END\n"
  },
  {
    "path": "Core/MMKV_IO.h",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#ifndef MMKV_IO_h\n#define MMKV_IO_h\n#ifdef __cplusplus\n\n#include \"MMKV.h\"\n\nMMKV_NAMESPACE_BEGIN\n\nstd::string mmapedKVKey(const std::string &mmapID, const MMKVPath_t *rootPath = nullptr, bool alreadyAbsolute = false);\nstd::string legacyMmapedKVKey(const std::string &mmapID, const MMKVPath_t *rootPath = nullptr);\n#ifndef MMKV_ANDROID\nMMKVPath_t mappedKVPathWithID(const std::string &mmapID, const MMKVPath_t *rootPath, bool alreadyAbsolute = false);\n#else\nMMKVPath_t mappedKVPathWithID(const std::string &mmapID, const MMKVPath_t *rootPath, MMKVMode mode = MMKV_MULTI_PROCESS, bool alreadyAbsolute = false);\n#endif\nMMKVPath_t crcPathWithPath(const MMKVPath_t &kvPath);\n\nMMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID);\nMMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID);\n\n#ifndef MMKV_WIN32\nconstexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = \"specialCharacter\";\nconstexpr auto CRC_SUFFIX = \".crc\";\n#else\nconstexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = L\"specialCharacter\";\nconstexpr auto CRC_SUFFIX = L\".crc\";\n#endif\n\ntemplate <typename T>\nvoid clearDictionary(T *dic) {\n    if (!dic) {\n        return;\n    }\n#ifdef MMKV_APPLE\n    for (auto &pair : *dic) {\n        [pair.first release];\n    }\n#endif\n    dic->clear();\n}\n\nenum : bool {\n    KeepSequence = false,\n    IncreaseSequence = true,\n};\n\n#ifdef MMKV_ANDROID\n// status of migrating old file to new file\nenum class MigrateStatus: uint32_t {\n    NotSpecial, // it's not specially (mistakenly) encoded\n    NoneExist, // none of these files exist\n    NewExist, // only new file exist\n    OldToNewMigrated, // migrated, it's one time only operation\n    OldToNewMigrateFail, // old file exist but fail to migrate (maybe other process opened)\n    OldAndNewExist, // both old and new exist (fail to delete old file? old file migrated from old device?)\n};\n\n// historically Android mistakenly use mmapKey as mmapID, we try migrate back to normal when possible\nMigrateStatus tryMigrateLegacyMMKVFile(const std::string &mmapID, const MMKVPath_t *rootPath, bool alreadyAbsolute = false);\n#endif // MMKV_ANDROID\n\nMMKV_NAMESPACE_END\n\n#endif\n#endif /* MMKV_IO_h */\n"
  },
  {
    "path": "Core/MMKV_OSX.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MMKVPredef.h\"\n\n#ifdef MMKV_APPLE\n\n#    include \"CodedInputData.h\"\n#    include \"CodedOutputData.h\"\n#    include \"InterProcessLock.h\"\n#    include \"MMKV.h\"\n#    include \"MemoryFile.h\"\n#    include \"MiniPBCoder.h\"\n#    include \"PBUtility.h\"\n#    include \"ScopedLock.hpp\"\n#    include \"ThreadLock.h\"\n#    include \"aes/AESCrypt.h\"\n#    include <sys/utsname.h>\n#    include <sys/sysctl.h>\n#    include \"MMKV_OSX.h\"\n#    include \"MMKVLog.h\"\n\n#    ifdef MMKV_IOS\n#        include <sys/mman.h>\n#    endif\n\n#    ifdef __aarch64__\n#        include \"crc32/Checksum.h\"\n#    endif\n\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n\nusing namespace std;\nusing namespace mmkv;\n\nextern ThreadLock *g_instanceLock;\nextern MMKVPath_t g_rootDir;\n\nMMKV_NAMESPACE_BEGIN\n\nHybridString::HybridString(string_view cpp) {\n    if (cpp.empty()) {\n        str = nil;\n    } else {\n        str = [[NSString alloc] initWithBytesNoCopy:(void*)cpp.data() length:cpp.length() encoding:NSUTF8StringEncoding freeWhenDone:NO];\n    }\n}\nHybridString::~HybridString() {\n    [str release];\n}\n\nHybridStringCP::HybridStringCP(string_view cpp) {\n    if (cpp.empty()) {\n        str = nil;\n    } else {\n        str = [[NSString alloc] initWithBytes:(void*)cpp.data() length:cpp.length() encoding:NSUTF8StringEncoding];\n    }\n}\nHybridStringCP::~HybridStringCP() {\n    [str release];\n}\n\nbool MMKV::set(bool value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(bool value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(int32_t value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(int32_t value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(uint32_t value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(uint32_t value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(int64_t value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(int64_t value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(uint64_t value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(uint64_t value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(float value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(float value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(double value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(double value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(const char *value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(const char *value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(const std::string &value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(const std::string &value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(std::string_view value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(std::string_view value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(const mmkv::MMBuffer &value, std::string_view key) {\n    return set(value, key, m_expiredInSeconds);\n}\nbool MMKV::set(const mmkv::MMBuffer &value, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(value, hybridKey.str, expireDuration);\n}\n\nbool MMKV::set(const std::vector<std::string> &vector, std::string_view key) {\n    return set(vector, key, m_expiredInSeconds);\n}\nbool MMKV::set(const std::vector<std::string> &vector, std::string_view key, uint32_t expireDuration) {\n    HybridStringCP hybridKey = key;\n    return set(vector, hybridKey.str, expireDuration);\n}\n\nbool MMKV::containsKey(std::string_view key) {\n    HybridString hybridKey = key;\n    return containsKey(hybridKey.str);\n}\n\nbool MMKV::removeValueForKey(std::string_view key) {\n    HybridString hybridKey = key;\n    return removeValueForKey(hybridKey.str);\n}\n\nbool MMKV::getBool(std::string_view key, bool defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getBool(hybridKey.str, defaultValue, hasValue);\n}\n\nint32_t MMKV::getInt32(std::string_view key, int32_t defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getInt32(hybridKey.str, defaultValue, hasValue);\n}\n\nuint32_t MMKV::getUInt32(std::string_view key, uint32_t defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getUInt32(hybridKey.str, defaultValue, hasValue);\n}\n\nint64_t MMKV::getInt64(std::string_view key, int64_t defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getInt64(hybridKey.str, defaultValue, hasValue);\n}\n\nuint64_t MMKV::getUInt64(std::string_view key, uint64_t defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getUInt64(hybridKey.str, defaultValue, hasValue);\n}\n\nfloat MMKV::getFloat(std::string_view key, float defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getFloat(hybridKey.str, defaultValue, hasValue);\n}\n\ndouble MMKV::getDouble(std::string_view key, double defaultValue, bool *hasValue) {\n    HybridString hybridKey = key;\n    return getDouble(hybridKey.str, defaultValue, hasValue);\n}\n\nbool MMKV::getString(std::string_view key, std::string &result, bool inplaceModification) {\n    HybridString hybridKey = key;\n    return getString(hybridKey.str, result, inplaceModification);\n}\n\nmmkv::MMBuffer MMKV::getBytes(std::string_view key) {\n    HybridString hybridKey = key;\n    return getBytes(hybridKey.str);\n}\n\nbool MMKV::getBytes(std::string_view key, mmkv::MMBuffer &result) {\n    HybridString hybridKey = key;\n    return getBytes(hybridKey.str, result);\n}\n\nmmkv::MMBuffer MMKV::getDataForKey(std::string_view key) {\n    HybridString hybridKey = key;\n    return getDataForKey(hybridKey.str);\n}\n\nbool MMKV::setDataForKey(mmkv::MMBuffer &&data, std::string_view key, bool isDataHolder) {\n    HybridStringCP hybridKey = key;\n    return setDataForKey(std::move(data), hybridKey.str, isDataHolder);\n}\n\nbool MMKV::getVector(std::string_view key, std::vector<std::string> &result) {\n    HybridString hybridKey = key;\n    return getVector(hybridKey.str, result);\n}\n\n#    ifdef MMKV_IOS\n\nstatic bool g_isInBackground = false;\n\nvoid MMKV::setIsInBackground(bool isInBackground) {\n    if (!g_instanceLock) {\n        return;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    g_isInBackground = isInBackground;\n    MMKVInfo(\"g_isInBackground:%d\", g_isInBackground);\n}\n\nbool MMKV::isInBackground() {\n    if (!g_instanceLock) {\n        return true;\n    }\n    SCOPED_LOCK(g_instanceLock);\n\n    return g_isInBackground;\n}\n\n#    endif // MMKV_IOS\n\nbool MMKV::set(NSObject<NSCoding> *__unsafe_unretained obj, MMKVKey_t key) {\n    return set(obj, key, m_expiredInSeconds);\n}\n\nbool MMKV::set(NSObject<NSCoding> *__unsafe_unretained obj, MMKVKey_t key, uint32_t expireDuration) {\n    if (isKeyEmpty(key)) {\n        return false;\n    }\n    if (!obj) {\n        removeValueForKey(key);\n        return true;\n    }\n\n    NSData *tmpData = nil;\n    if ([obj isKindOfClass:NSString.class]) {\n        auto str = (NSString *) obj;\n        tmpData = [str dataUsingEncoding:NSUTF8StringEncoding];\n    } else if ([obj isKindOfClass:NSData.class]) {\n        tmpData = (NSData *) obj;\n    }\n    if (tmpData) {\n        // delay write the size needed for encoding tmpData\n        // avoid memory copying\n        if (mmkv_likely(!m_enableKeyExpire)) {\n            return setDataForKey(MMBuffer(tmpData, MMBufferNoCopy), key, true);\n        } else {\n            MMBuffer data(tmpData, MMBufferNoCopy);\n            if (data.length() > 0) {\n                auto tmp = MMBuffer(pbMMBufferSize(data) + Fixed32Size);\n                CodedOutputData output(tmp.getPtr(), tmp.length());\n                output.writeData(data);\n                auto time = (expireDuration != 0) ? getCurrentTimeInSecond() + expireDuration : 0;\n                output.writeRawLittleEndian32(UInt32ToInt32(time));\n                data = std::move(tmp);\n            }\n            return setDataForKey(std::move(data), key);\n        }\n    } else if ([obj isKindOfClass:NSDate.class]) {\n        NSDate *oDate = (NSDate *) obj;\n        double time = oDate.timeIntervalSince1970;\n        return set(time, key, expireDuration);\n    } else {\n        /*if ([object conformsToProtocol:@protocol(NSCoding)])*/ {\n            @try {\n                NSError *error = nil;\n                auto archived = [NSKeyedArchiver archivedDataWithRootObject:obj requiringSecureCoding:NO error:&error];\n                if (error) {\n                    MMKVError(\"fail to archive: %@\", error);\n                    return false;\n                }\n                if (archived.length > 0) {\n                    if (mmkv_likely(!m_enableKeyExpire)) {\n                        return setDataForKey(MMBuffer(archived, MMBufferNoCopy), key);\n                    } else {\n                        MMBuffer data(archived, MMBufferNoCopy);\n                        if (data.length() > 0) {\n                            auto tmp = MMBuffer(data.length() + Fixed32Size);\n                            CodedOutputData output(tmp.getPtr(), tmp.length());\n                            output.writeRawData(data); // NSKeyedArchiver has its own size management\n                            auto time = (expireDuration != 0) ? getCurrentTimeInSecond() + expireDuration : 0;\n                            output.writeRawLittleEndian32(UInt32ToInt32(time));\n                            data = std::move(tmp);\n                        }\n                        return setDataForKey(std::move(data), key);\n                    }\n                }\n            } @catch (NSException *exception) {\n                MMKVError(\"exception: %@\", exception.reason);\n            }\n        }\n    }\n    return false;\n}\n\nstatic id unSecureUnArchiveObjectWithData(NSData *data) {\n    @try {\n        NSError *error = nil;\n        auto unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];\n        if (error) {\n            MMKVError(\"fail to init unarchiver %@\", error);\n            return nil;\n        }\n\n        unarchiver.requiresSecureCoding = NO;\n        id result = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];\n        [unarchiver release];\n        return result;\n    } @catch (NSException *exception) {\n        MMKVError(\"exception: %@\", exception.reason);\n    }\n    return nil;\n}\n\nNSObject *MMKV::getObject(MMKVKey_t key, Class cls) {\n    if (isKeyEmpty(key) || !cls) {\n        return nil;\n    }\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_sharedProcessLock);\n    auto data = getDataForKey(key);\n    if (data.length() > 0) {\n        if (MiniPBCoder::isCompatibleClass(cls)) {\n            try {\n                auto result = MiniPBCoder::decodeObject(data, cls);\n                return result;\n            } catch (std::exception &exception) {\n                MMKVError(\"%s\", exception.what());\n            } catch (...) {\n                MMKVError(\"decode fail\");\n            }\n        } else {\n            if ([cls conformsToProtocol:@protocol(NSCoding)]) {\n                auto tmp = [NSData dataWithBytesNoCopy:data.getPtr() length:data.length() freeWhenDone:NO];\n                return unSecureUnArchiveObjectWithData(tmp);\n            }\n        }\n    }\n    return nil;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\npair<bool, KeyValueHolder>\nMMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, const KeyValueHolderCrypt &kvHolder, bool isDataHolder) {\n    if (kvHolder.type != KeyValueHolderType_Offset) {\n        return appendDataWithKey(data, key, isDataHolder);\n    }\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    uint32_t keyLength = kvHolder.keySize;\n    // size needed to encode the key\n    size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);\n\n    auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n    MMBuffer keyData(rawKeySize);\n    AESCrypt decrypter = m_crypter->cloneWithStatus(kvHolder.cryptStatus);\n    decrypter.decrypt(basePtr + kvHolder.offset, keyData.getPtr(), rawKeySize);\n\n    return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);\n}\n\npair<bool, KeyValueHolder>\nMMKV::overrideDataWithKey(const MMBuffer &data, MMKVKey_t key, const KeyValueHolderCrypt &kvHolder, bool isDataHolder) {\n    if (kvHolder.type != KeyValueHolderType_Offset) {\n        return overrideDataWithKey(data, key, isDataHolder);\n    }\n    SCOPED_LOCK(m_exclusiveProcessLock);\n\n    uint32_t keyLength = kvHolder.keySize;\n    // size needed to encode the key\n    size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);\n\n    auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;\n    MMBuffer keyData(rawKeySize);\n    AESCrypt decrypter = m_crypter->cloneWithStatus(kvHolder.cryptStatus);\n    decrypter.decrypt(basePtr + kvHolder.offset, keyData.getPtr(), rawKeySize);\n\n    return doOverrideDataWithKey(data, keyData, isDataHolder, keyLength);\n}\n#    endif\n\nNSArray *MMKV::allKeysObjC(bool filterExpire) {\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n\n    if (mmkv_unlikely(filterExpire && m_enableKeyExpire)) {\n        SCOPED_LOCK(m_exclusiveProcessLock);\n        fullWriteback(nullptr, true);\n    }\n\n    NSMutableArray *keys = [NSMutableArray array];\n    if (m_crypter) {\n        for (const auto &pair : *m_dicCrypt) {\n            [keys addObject:pair.first];\n        }\n    } else {\n        for (const auto &pair : *m_dic) {\n            [keys addObject:pair.first];\n        }\n    }\n    return keys;\n}\n\nstd::vector<std::string> MMKV::allKeys(bool filterExpire) {\n    @autoreleasepool {\n        auto arrKeys = allKeysObjC(filterExpire);\n        std::vector<std::string> vec;\n        for (NSString* str in arrKeys) {\n            vec.push_back(str.UTF8String);\n        }\n        return vec;\n    }\n}\n\nbool MMKV::removeValuesForKeys(NSArray *arrKeys) {\n    if (isReadOnly()) {\n        MMKVWarning(\"[%s] file readonly\", m_mmapID.c_str());\n        return false;\n    }\n    if (arrKeys.count == 0) {\n        return true;\n    }\n    if (arrKeys.count == 1) {\n        return removeValueForKey(arrKeys[0]);\n    }\n\n    SCOPED_LOCK(m_lock);\n    SCOPED_LOCK(m_exclusiveProcessLock);\n    checkLoadData();\n\n    size_t deleteCount = 0;\n    if (m_crypter) {\n        for (NSString *key in arrKeys) {\n            auto itr = m_dicCrypt->find(key);\n            if (itr != m_dicCrypt->end()) {\n                auto oldKey = itr->first;\n                m_dicCrypt->erase(itr);\n                [oldKey release];\n                deleteCount++;\n            }\n        }\n    } else {\n        for (NSString *key in arrKeys) {\n            auto itr = m_dic->find(key);\n            if (itr != m_dic->end()) {\n                auto oldKey = itr->first;\n                m_dic->erase(itr);\n                [oldKey release];\n                deleteCount++;\n            }\n        }\n    }\n    if (deleteCount > 0) {\n        m_hasFullWriteback = false;\n\n        return fullWriteback();\n    }\n    return true;\n}\n\nbool MMKV::removeValuesForKeys(const std::vector<std::string> &arrKeys) {\n    if (arrKeys.empty()) {\n        return true;\n    }\n    NSMutableArray* arr = [[NSMutableArray alloc] initWithCapacity:arrKeys.size()];\n    for (auto& key : arrKeys) {\n        [arr addObject:HybridString(key).str];\n    }\n    auto ret = removeValuesForKeys(arr);\n    [arr release];\n    return ret;\n}\n\nvoid MMKV::enumerateKeys(EnumerateBlock block) {\n    if (block == nil) {\n        return;\n    }\n    SCOPED_LOCK(m_lock);\n    checkLoadData();\n\n    MMKVInfo(\"enumerate [%s] begin\", m_mmapID.c_str());\n    if (m_crypter) {\n        for (const auto &pair : *m_dicCrypt) {\n            BOOL stop = NO;\n            block(pair.first, &stop);\n            if (stop) {\n                break;\n            }\n        }\n    } else {\n        for (const auto &pair : *m_dic) {\n            BOOL stop = NO;\n            block(pair.first, &stop);\n            if (stop) {\n                break;\n            }\n        }\n    }\n    MMKVInfo(\"enumerate [%s] finish\", m_mmapID.c_str());\n}\n\nvoid GetAppleMachineInfo(int &device, int &version) {\n    device = UnKnown;\n    version = 0;\n\n#    if 0\n    struct utsname systemInfo = {};\n    uname(&systemInfo);\n    std::string machine(systemInfo.machine);\n#    else\n    size_t size;\n    sysctlbyname(\"hw.machine\", nullptr, &size, nullptr, 0);\n    char *answer = (char *) malloc(size);\n    sysctlbyname(\"hw.machine\", answer, &size, nullptr, 0);\n    std::string machine(answer);\n    free(answer);\n#    endif\n\n    if (machine.find(\"PowerMac\") != std::string::npos || machine.find(\"Power Macintosh\") != std::string::npos) {\n        device = PowerMac;\n    } else if (machine.find(\"Mac\") != std::string::npos || machine.find(\"Macintosh\") != std::string::npos) {\n        device = Mac;\n    } else if (machine.find(\"iPhone\") != std::string::npos) {\n        device = iPhone;\n    } else if (machine.find(\"iPod\") != std::string::npos) {\n        device = iPod;\n    } else if (machine.find(\"iPad\") != std::string::npos) {\n        device = iPad;\n    } else if (machine.find(\"AppleTV\") != std::string::npos) {\n        device = AppleTV;\n    } else if (machine.find(\"AppleWatch\") != std::string::npos) {\n        device = AppleWatch;\n    }\n    auto pos = machine.find_first_of(\"0123456789\");\n    if (pos != std::string::npos) {\n        version = std::atoi(machine.substr(pos).c_str());\n    }\n}\n\nMMKV_NAMESPACE_END\n\n#endif // MMKV_APPLE\n"
  },
  {
    "path": "Core/MMKV_OSX.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include \"MMKVPredef.h\"\n\nMMKV_NAMESPACE_BEGIN\n\nenum { UnKnown = 0, PowerMac = 1, Mac, iPhone, iPod, iPad, AppleTV, AppleWatch };\n\nvoid GetAppleMachineInfo(int &device, int &version);\n\nMMKV_NAMESPACE_END\n"
  },
  {
    "path": "Core/MemoryFile.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MemoryFile.h\"\n\n#ifndef MMKV_WIN32\n\n#    include \"InterProcessLock.h\"\n#    include \"MMBuffer.h\"\n#    include \"MMKVLog.h\"\n#    include \"ScopedLock.hpp\"\n#    include <cerrno>\n#    include <utility>\n#    include <fcntl.h>\n#    include <sys/mman.h>\n#    include <sys/stat.h>\n#    include <unistd.h>\n#    include <sys/file.h>\n#    include <dirent.h>\n#    include <cstring>\n#    include <filesystem>\n#    include <random>\n\nusing namespace std;\nnamespace fs = std::filesystem;\n\nnamespace mmkv {\n\nstatic bool getFileSize(const char *path, size_t &size);\n\n#    ifdef MMKV_ANDROID\nextern size_t ASharedMemory_getSize(int fd);\n#    else\nFile::File(MMKVPath_t path, OpenFlag flag) : m_path(std::move(path)), m_fd(-1), m_flag(flag) {\n    open();\n}\n\nMemoryFile::MemoryFile(MMKVPath_t path, size_t expectedCapacity, bool readOnly, bool mayflyFD)\n    : m_diskFile(std::move(path), readOnly ? OpenFlag::ReadOnly : (OpenFlag::ReadWrite | OpenFlag::Create))\n    , m_ptr(nullptr), m_size(0), m_readOnly(readOnly), m_isMayflyFD(mayflyFD)\n{\n    reloadFromFile(expectedCapacity);\n}\n#    endif // !defined(MMKV_ANDROID)\n\n#    ifdef MMKV_IOS\nvoid tryResetFileProtection(const string &path);\n#    endif\n\nstatic int OpenFlag2NativeFlag(OpenFlag flag) {\n    int native = O_CLOEXEC;\n    if ((flag & OpenFlagRWMask) == OpenFlag::ReadWrite) {\n        native |= O_RDWR;\n    } else if (flag & OpenFlag::ReadOnly) {\n        native |= O_RDONLY;\n    } else if (flag & OpenFlag::WriteOnly) {\n        native |= O_WRONLY;\n    }\n\n    if (flag & OpenFlag::Create) {\n        native |= O_CREAT;\n    }\n    if (flag & OpenFlag::Excel) {\n        native |= O_EXCL;\n    }\n    if (flag & OpenFlag::Truncate) {\n        native |= O_TRUNC;\n    }\n    return native;\n}\n\nbool File::open() {\n#    ifdef MMKV_ANDROID\n    if (m_fileType == MMFILE_TYPE_ASHMEM) {\n        return isFileValid();\n    }\n#    endif\n    if (isFileValid()) {\n        return true;\n    }\n    m_fd = ::open(m_path.c_str(), OpenFlag2NativeFlag(m_flag), S_IRWXU);\n    if (!isFileValid()) {\n        MMKVError(\"fail to open [%s], flag 0x%x, %d(%s)\", m_path.c_str(), m_flag, errno, strerror(errno));\n        return false;\n    }\n    MMKVInfo(\"open fd[%d], flag 0x%x, %s\", m_fd, m_flag, m_path.c_str());\n    return true;\n}\n\nvoid File::close() {\n    if (isFileValid()) {\n        MMKVInfo(\"closing fd[%d], %s\", m_fd, m_path.c_str());\n        if (::close(m_fd) == 0) {\n            m_fd = -1;\n        } else {\n            MMKVError(\"fail to close [%s], %d(%s)\", m_path.c_str(), errno, strerror(errno));\n        }\n    }\n}\n\nsize_t File::getActualFileSize() const {\n#    ifdef MMKV_ANDROID\n    if (m_fileType == MMFILE_TYPE_ASHMEM) {\n        return ASharedMemory_getSize(m_fd);\n    }\n#    endif\n    size_t size = 0;\n    if (isFileValid()) {\n        mmkv::getFileSize(m_fd, size);\n    } else {\n        mmkv::getFileSize(m_path.c_str(), size);\n    }\n    return size;\n}\n\nbool MemoryFile::openIfNeeded() {\n    if (!m_diskFile.isFileValid()) {\n        return m_diskFile.open();\n    }\n    return true;\n}\n\nvoid MemoryFile::cleanMayflyFD() {\n    if (m_isMayflyFD && m_diskFile.isFileValid()) {\n        m_diskFile.close();\n    }\n}\n\nsize_t MemoryFile::getActualFileSize() {\n    if (!m_isMayflyFD && !m_diskFile.isFileValid()) {\n        return 0;\n    }\n\n    return m_diskFile.getActualFileSize();\n}\n\nMMKVFileHandle_t MemoryFile::getFd() {\n    if (m_isMayflyFD) {\n        openIfNeeded();\n    }\n    return m_diskFile.getFd();\n}\n\nbool MemoryFile::truncate(size_t size, FileLock *fileLock) {\n    if (m_isMayflyFD) {\n        openIfNeeded();\n    }\n    if (!m_diskFile.isFileValid()) {\n        return false;\n    }\n    if (size == m_size) {\n        return true;\n    }\n    if (m_readOnly) {\n        // truncate readonly file not allow\n        return false;\n    }\n#    ifdef MMKV_ANDROID\n    if (m_diskFile.m_fileType == MMFILE_TYPE_ASHMEM) {\n        if (size > m_size) {\n            MMKVError(\"ashmem %s reach size limit:%zu, consider configure with larger size\", m_diskFile.m_path.c_str(), m_size);\n        } else {\n            MMKVInfo(\"no way to trim ashmem %s from %zu to smaller size %zu\", m_diskFile.m_path.c_str(), m_size, size);\n        }\n        return false;\n    }\n#    endif // MMKV_ANDROID\n\n    auto oldSize = m_size;\n    m_size = size;\n    // round up to (n * pagesize)\n    if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {\n        m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;\n    }\n\n    if (::ftruncate(m_diskFile.m_fd, static_cast<off_t>(m_size)) != 0) {\n        MMKVError(\"fail to truncate [%s] to size %zu, %s\", m_diskFile.m_path.c_str(), m_size, strerror(errno));\n        m_size = oldSize;\n        return false;\n    }\n    if (m_size > oldSize) {\n        if (!zeroFillFile(m_diskFile.m_fd, oldSize, m_size - oldSize)) {\n            MMKVError(\"fail to zeroFile [%s] to size %zu, %s\", m_diskFile.m_path.c_str(), m_size, strerror(errno));\n            m_size = oldSize;\n\n            // redo ftruncate to its previous size\n            int status = ::ftruncate(m_diskFile.m_fd, static_cast<off_t>(m_size));\n            if (status != 0) {\n                MMKVError(\"failed to truncate back [%s] to size %zu, %s\", m_diskFile.m_path.c_str(), m_size, strerror(errno));\n            } else {\n                MMKVError(\"success to truncate [%s] back to size %zu\", m_diskFile.m_path.c_str(), m_size);\n                MMKVError(\"after truncate, file size = %zu\", getActualFileSize());\n            }\n\n            return false;\n        }\n    }\n\n    if (m_ptr) {\n        if (munmap(m_ptr, oldSize) != 0) {\n            MMKVError(\"fail to munmap [%s], %s\", m_diskFile.m_path.c_str(), strerror(errno));\n        }\n    }\n    return mmapOrCleanup(fileLock);\n}\n\nbool MemoryFile::msync(SyncFlag syncFlag) {\n    if (m_readOnly) {\n        // there's no point in msync() readonly memory\n        return true;\n    }\n    if (m_ptr) {\n        auto ret = ::msync(m_ptr, m_size, syncFlag ? MS_SYNC : MS_ASYNC);\n        if (ret == 0) {\n            return true;\n        }\n        MMKVError(\"fail to msync [%s], %s\", m_diskFile.m_path.c_str(), strerror(errno));\n    }\n    return false;\n}\n\nbool MemoryFile::mmapOrCleanup(FileLock *fileLock) {\n    auto oldPtr = m_ptr;\n    auto mode = m_readOnly ? PROT_READ : (PROT_READ | PROT_WRITE);\n    m_ptr = (char *) ::mmap(m_ptr, m_size, mode, MAP_SHARED, m_diskFile.m_fd, 0);\n    if (m_ptr == MAP_FAILED) {\n        MMKVError(\"fail to mmap [%s], mode 0x%x, %s\", m_diskFile.m_path.c_str(), mode, strerror(errno));\n        m_ptr = nullptr;\n\n        doCleanMemoryCache(true);\n        return false;\n    }\n    MMKVInfo(\"mmap to address [%p], oldPtr [%p], [%s]\", m_ptr, oldPtr, m_diskFile.m_path.c_str());\n\n    if (m_isMayflyFD && fileLock) {\n        fileLock->destroyAndUnLock();\n    }\n\n    cleanMayflyFD();\n    return true;\n}\n\nvoid MemoryFile::reloadFromFile(size_t expectedCapacity) {\n#    ifdef MMKV_ANDROID\n    if (m_fileType == MMFILE_TYPE_ASHMEM) {\n        return;\n    }\n#    endif\n    if (isFileValid()) {\n        MMKVWarning(\"calling reloadFromFile while the cache [%s] is still valid\", m_diskFile.m_path.c_str());\n        MMKV_ASSERT(0);\n        doCleanMemoryCache(false);\n    }\n\n    if (openIfNeeded()) {\n        FileLock fileLock(m_diskFile.m_fd);\n        InterProcessLock lock(&fileLock, SharedLockType);\n        SCOPED_LOCK(&lock);\n\n        mmkv::getFileSize(m_diskFile.m_fd, m_size);\n        size_t expectedSize = std::max<size_t>(DEFAULT_MMAP_SIZE, roundUp<size_t>(expectedCapacity, DEFAULT_MMAP_SIZE));\n        // round up to (n * pagesize)\n        if (!m_readOnly && (m_size < expectedSize || (m_size % DEFAULT_MMAP_SIZE != 0))) {\n            InterProcessLock exclusiveLock(&fileLock, ExclusiveLockType);\n            SCOPED_LOCK(&exclusiveLock);\n\n            size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;;\n            roundSize = std::max<size_t>(expectedSize, roundSize);\n            truncate(roundSize, &fileLock);\n        } else {\n            mmapOrCleanup(&fileLock);\n        }\n#    ifdef MMKV_IOS\n        if (!m_readOnly) {\n            tryResetFileProtection(m_diskFile.m_path);\n        }\n#    endif\n    }\n}\n\nvoid MemoryFile::doCleanMemoryCache(bool forceClean) {\n#    ifdef MMKV_ANDROID\n    if (m_diskFile.m_fileType == MMFILE_TYPE_ASHMEM && !forceClean) {\n        return;\n    }\n#    endif\n    if (m_ptr && m_ptr != MAP_FAILED) {\n        if (munmap(m_ptr, m_size) != 0) {\n            MMKVError(\"fail to munmap [%s], %s\", m_diskFile.m_path.c_str(), strerror(errno));\n        }\n    }\n    m_ptr = nullptr;\n\n    m_diskFile.close();\n    m_size = 0;\n}\n\nbool isFileExist(const string &nsFilePath) {\n    if (nsFilePath.empty()) {\n        return false;\n    }\n\n    return access(nsFilePath.c_str(), F_OK) == 0;\n}\n\n#ifndef MMKV_APPLE\nextern bool mkPath(const MMKVPath_t &str) {\n    char *path = strdup(str.c_str());\n\n    struct stat sb = {};\n    bool done = false;\n    char *slash = path;\n\n    while (!done) {\n        slash += strspn(slash, \"/\");\n        slash += strcspn(slash, \"/\");\n\n        done = (*slash == '\\0');\n        *slash = '\\0';\n\n        if (stat(path, &sb) != 0) {\n            if (errno != ENOENT || mkdir(path, 0777) != 0) {\n                MMKVWarning(\"%s : %s\", path, strerror(errno));\n                // there's report that some Android devices might not have access permission on parent dir\n                if (done) {\n                    free(path);\n                    return false;\n                }\n                goto LContinue;\n            }\n        } else if (!S_ISDIR(sb.st_mode)) {\n            MMKVWarning(\"%s: %s\", path, strerror(ENOTDIR));\n            free(path);\n            return false;\n        }\nLContinue:\n        *slash = '/';\n    }\n    free(path);\n\n    return true;\n}\n#else\n// avoid using so-called privacy API\nextern bool mkPath(const MMKVPath_t &str) {\n    auto path = [NSString stringWithUTF8String:str.c_str()];\n    NSError *error = nil;\n    auto ret = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];\n    if (!ret) {\n        MMKVWarning(\"%s\", error.localizedDescription.UTF8String);\n        return false;\n    }\n    return true;\n}\n#endif\n\nMMBuffer *readWholeFile(const MMKVPath_t &path) {\n    MMBuffer *buffer = nullptr;\n    int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);\n    if (fd >= 0) {\n        auto fileLength = lseek(fd, 0, SEEK_END);\n        if (fileLength > 0) {\n            buffer = new MMBuffer(static_cast<size_t>(fileLength));\n            lseek(fd, 0, SEEK_SET);\n            auto readSize = read(fd, buffer->getPtr(), static_cast<size_t>(fileLength));\n            if (readSize != -1) {\n                //fileSize = readSize;\n            } else {\n                MMKVWarning(\"fail to read %s: %s\", path.c_str(), strerror(errno));\n\n                delete buffer;\n                buffer = nullptr;\n            }\n        }\n        close(fd);\n    } else {\n        MMKVWarning(\"fail to open %s: %s\", path.c_str(), strerror(errno));\n    }\n    return buffer;\n}\n\nbool zeroFillFile(int fd, size_t startPos, size_t size) {\n    if (fd < 0) {\n        return false;\n    }\n\n    if (lseek(fd, static_cast<off_t>(startPos), SEEK_SET) < 0) {\n        MMKVError(\"fail to lseek fd[%d], error:%s\", fd, strerror(errno));\n        return false;\n    }\n\n    static const char zeros[4096] = {};\n    while (size >= sizeof(zeros)) {\n        if (write(fd, zeros, sizeof(zeros)) < 0) {\n            MMKVError(\"fail to write fd[%d], error:%s\", fd, strerror(errno));\n            return false;\n        }\n        size -= sizeof(zeros);\n    }\n    if (size > 0) {\n        if (write(fd, zeros, size) < 0) {\n            MMKVError(\"fail to write fd[%d], error:%s\", fd, strerror(errno));\n            return false;\n        }\n    }\n    return true;\n}\n\n#ifndef MMKV_APPLE\n\nbool getFileSize(int fd, size_t &size) {\n    struct stat st = {};\n    if (fstat(fd, &st) != -1) {\n        size = (size_t) st.st_size;\n        return true;\n    }\n    return false;\n}\n\nbool getFileSize(const char *path, size_t &size) {\n    struct stat st = {};\n    if (stat(path, &st) != -1) {\n        size = (size_t) st.st_size;\n        return true;\n    }\n    return false;\n}\n\n#else // !MMKV_APPLE\n\n// avoid using so-called privacy API\nbool getFileSize(int fd, size_t &size) {\n    auto cur = lseek(fd, 0, SEEK_CUR);\n    if (cur == -1) {\n        return false;\n    }\n    auto end = lseek(fd, 0, SEEK_END);\n    if (end == -1) {\n        return false;\n    }\n    size = (size_t) end;\n\n    lseek(fd, cur, SEEK_SET);\n    return true;\n}\n\nbool getFileSize(const char *path, size_t &size) {\n    auto fd = open(path, O_RDONLY);\n    if (fd >= 0) {\n        auto ret = getFileSize(fd, size);\n        close(fd);\n        return ret;\n    }\n    return false;\n}\n\n#endif // !MMKV_APPLE\n\nsize_t getPageSize() {\n    return static_cast<size_t>(getpagesize());\n}\n\nextern MMKVPath_t absolutePath(const MMKVPath_t &path) {\n    fs::path relative_path(path);\n    fs::path absolute_path = fs::absolute(relative_path);\n    try {\n        fs::path normalized = fs::weakly_canonical(absolute_path);\n        return normalized.string();\n    } catch (std::exception &e) {\n        MMKVError(\"fail to weakly_canonical() path %s, error: %s\", absolute_path.c_str(), e.what());\n    }\n    return absolute_path.string();\n}\n\n#ifndef MMKV_APPLE\n\nstatic pair<MMKVPath_t, int> createUniqueTempFile(const char *prefix) {\n    char path[PATH_MAX];\n#ifdef MMKV_ANDROID\n    snprintf(path, PATH_MAX, \"%s/%s.XXXXXX\", g_android_tmpDir.c_str(), prefix);\n#else\n    snprintf(path, PATH_MAX, \"%s/%s.XXXXXX\", P_tmpdir, prefix);\n#endif\n\n    auto fd = mkstemp(path);\n    if (fd < 0) {\n        MMKVError(\"fail to create unique temp file [%s], %d(%s)\", path, errno, strerror(errno));\n        return {\"\", fd};\n    }\n    MMKVDebug(\"create unique temp file [%s] with fd[%d]\", path, fd);\n    return {MMKVPath_t(path), fd};\n}\n\n#if !defined(MMKV_ANDROID) && !defined(MMKV_LINUX)\n\nbool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    if (::rename(srcPath.c_str(), dstPath.c_str()) != 0) {\n        MMKVError(\"fail to rename [%s] to [%s], %d(%s)\", srcPath.c_str(), dstPath.c_str(), errno, strerror(errno));\n        return false;\n    }\n    return true;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {\n    if (dstFD < 0) {\n        return false;\n    }\n    bool ret = false;\n    File srcFile(srcPath, OpenFlag::ReadOnly);\n    if (!srcFile.isFileValid()) {\n        return false;\n    }\n    auto bufferSize = getPageSize();\n    auto buffer = (char *) malloc(bufferSize);\n    if (!buffer) {\n        MMKVError(\"fail to malloc size %zu, %d(%s)\", bufferSize, errno, strerror(errno));\n        goto errorOut;\n    }\n    lseek(dstFD, 0, SEEK_SET);\n\n    // the POSIX standard don't have sendfile()/fcopyfile() equivalent, do it the hard way\n    while (true) {\n        auto sizeRead = read(srcFile.getFd(), buffer, bufferSize);\n        if (sizeRead < 0) {\n            MMKVError(\"fail to read file [%s], %d(%s)\", srcPath.c_str(), errno, strerror(errno));\n            goto errorOut;\n        }\n\n        size_t totalWrite = 0;\n        do {\n            auto sizeWrite = write(dstFD, buffer + totalWrite, sizeRead - totalWrite);\n            if (sizeWrite < 0) {\n                MMKVError(\"fail to write fd [%d], %d(%s)\", dstFD, errno, strerror(errno));\n                goto errorOut;\n            }\n            totalWrite += sizeWrite;\n        } while (totalWrite < sizeRead);\n\n        if (sizeRead < bufferSize) {\n            break;\n        }\n    }\n    if (needTruncate) {\n        size_t dstFileSize = 0;\n        getFileSize(dstFD, dstFileSize);\n        auto srcFileSize = srcFile.getActualFileSize();\n        if ((dstFileSize != srcFileSize) && (::ftruncate(dstFD, static_cast<off_t>(srcFileSize)) != 0)) {\n            MMKVError(\"fail to truncate [%d] to size [%zu], %d(%s)\", dstFD, srcFileSize, errno, strerror(errno));\n            goto errorOut;\n        }\n    }\n\n    ret = true;\n    MMKVInfo(\"copy content from %s to fd[%d] finish\", srcPath.c_str(), dstFD);\n\nerrorOut:\n    free(buffer);\n    return ret;\n}\n\n#endif // !defined(MMKV_ANDROID) && !defined(MMKV_LINUX)\n\n// copy to a temp file then rename it\n// this is the best we can do under the POSIX standard\nbool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    auto pair = createUniqueTempFile(\"MMKV\");\n    auto tmpFD = pair.second;\n    auto &tmpPath = pair.first;\n    if (tmpFD < 0) {\n        return false;\n    }\n\n    bool renamed = false;\n    if (copyFileContent(srcPath, tmpFD, false)) {\n        MMKVInfo(\"copyfile [%s] to [%s]\", srcPath.c_str(), tmpPath.c_str());\n        renamed = tryAtomicRename(tmpPath, dstPath);\n        if (!renamed) {\n            MMKVInfo(\"rename fail, try copy file content instead.\");\n            if (copyFileContent(tmpPath, dstPath)) {\n                renamed = true;\n                ::unlink(tmpPath.c_str());\n            }\n        }\n        if (renamed) {\n            MMKVInfo(\"copyfile [%s] to [%s] finish.\", srcPath.c_str(), dstPath.c_str());\n        }\n    }\n\n    ::close(tmpFD);\n    if (!renamed) {\n        ::unlink(tmpPath.c_str());\n    }\n    return renamed;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);\n    if (!dstFile.isFileValid()) {\n        return false;\n    }\n    auto ret = copyFileContent(srcPath, dstFile.getFd(), false);\n    if (!ret) {\n        MMKVError(\"fail to copyfile(): target file %s\", dstPath.c_str());\n    } else {\n        MMKVInfo(\"copy content from %s to [%s] finish\", srcPath.c_str(), dstPath.c_str());\n    }\n    return ret;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {\n    return copyFileContent(srcPath, dstFD, true);\n}\n\n#endif // !defined(MMKV_APPLE)\n\nvoid walkInDir(const MMKVPath_t &dirPath, WalkType type, const function<void(const MMKVPath_t&, WalkType)> &walker) {\n    auto folderPathStr = dirPath.data();\n    DIR *dir = opendir(folderPathStr);\n    if (!dir) {\n        MMKVError(\"opendir failed: %d(%s), %s\", errno, strerror(errno), dirPath.c_str());\n        return;\n    }\n\n    char childPath[PATH_MAX];\n    size_t folderPathLength = dirPath.size();\n    strncpy(childPath, folderPathStr, folderPathLength + 1);\n    if (folderPathStr[folderPathLength - 1] != '/') {\n        childPath[folderPathLength] = '/';\n        folderPathLength++;\n    }\n\n    while (auto child = readdir(dir)) {\n        if ((child->d_type & DT_REG) && (type & WalkFile)) {\n#if defined(_DIRENT_HAVE_D_NAMLEN) || defined(__APPLE__)\n            stpcpy(childPath + folderPathLength, child->d_name);\n            childPath[folderPathLength + child->d_namlen] = 0;\n#else\n            strcpy(childPath + folderPathLength, child->d_name);\n#endif\n            walker(childPath, WalkFile);\n        } else if ((child->d_type & DT_DIR) && (type & WalkFolder)) {\n#if defined(_DIRENT_HAVE_D_NAMLEN) || defined(__APPLE__)\n            if ((child->d_namlen == 1 && child->d_name[0] == '.') ||\n                (child->d_namlen == 2 && child->d_name[0] == '.' && child->d_name[1] == '.')) {\n                continue;\n            }\n            stpcpy(childPath + folderPathLength, child->d_name);\n            childPath[folderPathLength + child->d_namlen] = 0;\n#else\n            if (strcmp(child->d_name, \".\") == 0 || strcmp(child->d_name, \"..\") == 0) {\n                continue;\n            }\n            strcpy(childPath + folderPathLength, child->d_name);\n#endif\n            walker(childPath, WalkFolder);\n        }\n    }\n\n    closedir(dir);\n}\n\nbool deleteFile(const MMKVPath_t &path) {\n    auto filename = path.c_str();\n    if (::unlink(filename) != 0) {\n        auto err = errno;\n        MMKVError(\"fail to delete file [%s], %d (%s)\", filename, err, strerror(err));\n        return false;\n    }\n    return true;\n}\n\n#ifndef MMKV_APPLE\nbool isDiskOfMMAPFileCorrupted(MemoryFile *file, bool &needReportReadFail) {\n    // TODO: maybe we need reading a larger chunk than 4 byte in Android/Linux\n    uint32_t info;\n    auto fd = file->getFd();\n    auto path = file->getPath().c_str();\n\n    auto oldPos = lseek(fd, 0, SEEK_CUR);\n    lseek(fd, 0, SEEK_SET);\n    auto size = read(fd, &info, sizeof(info));\n    auto err = errno;\n    lseek(fd, oldPos, SEEK_SET);\n\n    if (size <= 0) {\n        needReportReadFail = true;\n        MMKVError(\"fail to read [%s] from fd [%d], errno: %d (%s)\", path, fd, err, strerror(err));\n        if (err == EIO || err == EILSEQ || err == EINVAL || err == ENXIO) {\n            MMKVWarning(\"file fail to read, consider it illegal, delete now: [%s]\", path);\n            return true;\n        }\n    }\n    file->cleanMayflyFD();\n    return false;\n}\n#endif\n\nstd::optional<MMKVPath_t> getUniqueFileName(const MMKVPath_t &folder, const MMKVPath_t &prefix) {\n    fs::path folderPath(folder);\n    fs::path prefixPath(prefix);\n\n    // Ensure the directory exists\n    std::error_code ec;\n    if (!fs::exists(folderPath, ec)) {\n        // Attempt to create it or fail if preferred.\n        // GetTempFileName fails if dir doesn't exist, so we adhere to that.\n        return std::nullopt;\n    }\n\n    // Behavior: Generate random unique filename, CREATE the file to reserve it.\n    std::random_device rd;\n    std::mt19937_64 gen(rd());\n    std::uniform_int_distribution<uint64_t> dis;\n\n    constexpr int maxAttempts = 64;\n    for (int i = 0; i < maxAttempts; ++i) {\n        uint64_t randomVal = dis(gen);\n        MMKVPath_t suffix = to_string(randomVal);\n        MMKVPath_t fileName = prefix + \".\" + suffix + \".tmp\";\n        fs::path candidatePath = folderPath / fileName;\n\n        // Atomic check and create logic \"mimic\"\n        // std::filesystem::exists is not atomic, but standard C++17 <fstream> doesn't\n        // support O_EXCL (exclusive create) easily without platform headers.\n        // We check existence first to avoid clobbering existing files.\n        if (fs::exists(candidatePath, ec)) {\n            continue; // Collision found, try next\n        }\n\n        // Try to create the file to \"reserve\" it\n        File file(candidatePath.native(), OpenFlag::ReadWrite | OpenFlag::Create);\n        if (file.isFileValid()) {\n            return candidatePath.native();\n        }\n    }\n\n    // Failed to find unique name after max attempts\n    return std::nullopt;\n}\n} // namespace mmkv\n\n#endif // !defined(MMKV_WIN32)\n"
  },
  {
    "path": "Core/MemoryFile.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MAMERYFILE_H\n#define MMKV_MAMERYFILE_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n#include <cstdint>\n#include <functional>\n#include <optional>\n\n#ifdef MMKV_ANDROID\nMMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID);\n\nlong long getFileModifyTimeInMS(const char *path);\n\nnamespace mmkv {\nextern int g_android_api;\nextern std::string g_android_tmpDir;\n\nenum FileType : bool { MMFILE_TYPE_FILE = false, MMFILE_TYPE_ASHMEM = true };\n} // namespace mmkv\n#endif // MMKV_ANDROID\n\nnamespace mmkv {\n\nenum class OpenFlag : uint32_t {\n    ReadOnly = 1 << 0,\n    WriteOnly = 1 << 1,\n    ReadWrite = ReadOnly | WriteOnly,\n    Create = 1 << 2,\n    Excel = 1 << 3, // fail if Create is set but the file already exist\n    Truncate = 1 << 4,\n};\nconstexpr uint32_t OpenFlagRWMask = 0x3; // mask for Read Write mode\n\nstatic inline OpenFlag operator | (OpenFlag left, OpenFlag right) {\n    return static_cast<OpenFlag>(static_cast<uint32_t>(left) | static_cast<uint32_t>(right));\n}\n\nstatic inline bool operator & (OpenFlag left, OpenFlag right) {\n    return ((static_cast<uint32_t>(left) & static_cast<uint32_t>(right)) != 0);\n}\n\nstatic inline OpenFlag operator & (OpenFlag left, uint32_t right) {\n    return static_cast<OpenFlag>(static_cast<uint32_t>(left) & right);\n}\n\ntemplate <typename T>\nT roundUp(T numToRound, T multiple) {\n    return ((numToRound + multiple - 1) / multiple) * multiple;\n}\n\nclass FileLock;\n\nclass File {\n    MMKVPath_t m_path;\n#ifdef MMKV_WIN32\n    std::string m_utf8Path;\n#endif\n    MMKVFileHandle_t m_fd;\n\npublic:\n    const OpenFlag m_flag;\n#ifndef MMKV_ANDROID\n    explicit File(MMKVPath_t path, OpenFlag flag);\n#else\n    File(MMKVPath_t path, OpenFlag flag, size_t size = 0, FileType fileType = MMFILE_TYPE_FILE);\n    explicit File(MMKVFileHandle_t ashmemFD);\n\n    size_t m_size;\n    const FileType m_fileType;\n#endif // MMKV_ANDROID\n\n    ~File() { close(); }\n\n    bool open();\n\n    void close();\n\n    MMKVFileHandle_t getFd() const { return m_fd; }\n\n    const MMKVPath_t &getPath() const { return m_path; }\n\n#ifndef MMKV_WIN32\n    bool isFileValid() const { return m_fd >= 0; }\n\n    const std::string &getUTF8Path() const { return m_path; }\n#else\n    bool isFileValid() const { return m_fd != MMKVFileHandleInvalidValue; }\n\n    const std::string &getUTF8Path() const { return m_utf8Path; }\n#endif\n\n    // get the actual file size on disk\n    size_t getActualFileSize() const;\n\n    // just forbid it for possibly misuse\n    explicit File(const File &other) = delete;\n    File &operator=(const File &other) = delete;\n\n    friend class MemoryFile;\n};\n\nclass MemoryFile {\n    File m_diskFile;\n#ifdef MMKV_WIN32\n    HANDLE m_fileMapping;\n#endif\n    void *m_ptr;\n    size_t m_size;\n    const bool m_readOnly;\n    const bool m_isMayflyFD;\n\n    bool mmapOrCleanup(FileLock *fileLock);\n\n    void doCleanMemoryCache(bool forceClean);\n\n    bool openIfNeeded();\n\npublic:\n#ifndef MMKV_ANDROID\n    explicit MemoryFile(MMKVPath_t path, size_t expectedCapacity = 0, bool readOnly = false, bool mayflyFD = false);\n#else\n    MemoryFile(MMKVPath_t path, FileType fileType, size_t expectedCapacity = 0, bool readOnly = false, bool mayflyFD = false);\n    explicit MemoryFile(MMKVFileHandle_t ashmemFD);\n\n    const FileType m_fileType;\n#endif // MMKV_ANDROID\n\n    ~MemoryFile() { doCleanMemoryCache(true); }\n\n    size_t getFileSize() const { return m_size; }\n\n    // get the actual file size on disk\n    size_t getActualFileSize();\n\n    void *getMemory() { return m_ptr; }\n\n    const MMKVPath_t &getPath() { return m_diskFile.getPath(); }\n\n    const std::string &getUTF8Path() const { return m_diskFile.getUTF8Path(); }\n\n    MMKVFileHandle_t getFd();\n\n    void cleanMayflyFD();\n\n    // the newly expanded file content will be zeroed\n    bool truncate(size_t size, FileLock *fileLock = nullptr);\n\n    bool msync(SyncFlag syncFlag);\n\n    // call this if clearMemoryCache() has been called\n    void reloadFromFile(size_t expectedCapacity = 0);\n\n    void clearMemoryCache() { doCleanMemoryCache(false); }\n\n#ifndef MMKV_WIN32\n    bool isFileValid() { return (m_isMayflyFD || m_diskFile.isFileValid()) && m_size > 0 && m_ptr; }\n#else\n    bool isFileValid() { return (m_isMayflyFD || (m_diskFile.isFileValid() && m_fileMapping)) && m_size > 0 && m_ptr; }\n#endif\n\n    // just forbid it for possibly misuse\n    explicit MemoryFile(const MemoryFile &other) = delete;\n    MemoryFile &operator=(const MemoryFile &other) = delete;\n};\n\nclass MMBuffer;\n\nextern bool mkPath(const MMKVPath_t &path);\nextern bool isFileExist(const MMKVPath_t &nsFilePath);\nextern MMBuffer *readWholeFile(const MMKVPath_t &path);\nextern bool zeroFillFile(MMKVFileHandle_t fd, size_t startPos, size_t size);\nextern size_t getPageSize();\nextern MMKVPath_t absolutePath(const MMKVPath_t &path);\n#ifndef MMKV_WIN32\nextern bool getFileSize(int fd, size_t &size);\n#endif\nextern bool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);\n\n// copy file by potentially renaming target file, might change file inode\nextern bool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);\n\n// copy file by source file content, keep file inode the same\nextern bool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath);\nextern bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD);\nextern bool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate);\n\n//#if defined(MMKV_APPLE) || defined(MMKV_WIN32)\nbool isDiskOfMMAPFileCorrupted(MemoryFile *file, bool &needReportReadFail);\n//#endif\n\nbool deleteFile(const MMKVPath_t &path);\n\nstd::optional<MMKVPath_t> getUniqueFileName(const MMKVPath_t &folder, const MMKVPath_t &prefix);\n\nenum WalkType : uint32_t {\n    WalkFile = 1 << 0,\n    WalkFolder = 1 << 1,\n};\nextern void walkInDir(const MMKVPath_t &dirPath, WalkType type, const std::function<void(const MMKVPath_t&, WalkType)> &walker);\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_MAMERYFILE_H\n"
  },
  {
    "path": "Core/MemoryFile_Android.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MemoryFile.h\"\n\n#ifdef MMKV_ANDROID\n\n#    include \"MMBuffer.h\"\n#    include \"MMKVLog.h\"\n#    include <cerrno>\n#    include <fcntl.h>\n#    include <sys/mman.h>\n#    include <sys/stat.h>\n#    include <unistd.h>\n\nusing namespace std;\n\nconstexpr char ASHMEM_NAME_DEF[] = \"/dev/ashmem\";\n\nnamespace mmkv {\n\n// for Android Q limiting ashmem access\nextern int ASharedMemory_create(const char *name, size_t size);\nextern size_t ASharedMemory_getSize(int fd);\nextern string ASharedMemory_getName(int fd);\n\nFile::File(MMKVPath_t path, OpenFlag flag, size_t size, FileType fileType)\n    : m_path(std::move(path)), m_fd(-1), m_flag(flag), m_size(0), m_fileType(fileType) {\n    if (m_fileType == MMFILE_TYPE_FILE) {\n        open();\n    } else {\n        // round up to (n * pagesize)\n        if (size < DEFAULT_MMAP_SIZE || (size % DEFAULT_MMAP_SIZE != 0)) {\n            size = ((size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;\n        }\n        auto filename = m_path.c_str();\n        auto ptr = strstr(filename, ASHMEM_NAME_DEF);\n        if (ptr && ptr[sizeof(ASHMEM_NAME_DEF) - 1] == '/') {\n            filename = ptr + sizeof(ASHMEM_NAME_DEF);\n        }\n        m_fd = ASharedMemory_create(filename, size);\n        if (isFileValid()) {\n            m_size = size;\n        }\n    }\n}\n\nFile::File(MMKVFileHandle_t ashmemFD)\n    : m_path(), m_fd(ashmemFD), m_flag(OpenFlag::ReadWrite), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM) {\n    if (isFileValid()) {\n        m_path = ASharedMemory_getName(m_fd);\n        m_size = ASharedMemory_getSize(m_fd);\n    }\n}\n\nMemoryFile::MemoryFile(string path, FileType fileType, size_t expectedCapacity, bool isReadOnly, bool mayflyFD)\n    : m_diskFile(std::move(path), isReadOnly ? OpenFlag::ReadOnly : (OpenFlag::ReadWrite | OpenFlag::Create),\n                 expectedCapacity, fileType),\n    m_ptr(nullptr), m_size(0), m_fileType(fileType), m_readOnly(isReadOnly), m_isMayflyFD(mayflyFD) {\n    if (m_fileType == MMFILE_TYPE_FILE) {\n        reloadFromFile(expectedCapacity);\n    } else {\n        if (m_diskFile.isFileValid()) {\n            m_size = m_diskFile.m_size;\n            mmapOrCleanup(nullptr);\n        }\n    }\n}\n\nMemoryFile::MemoryFile(int ashmemFD)\n    : m_diskFile(ashmemFD), m_ptr(nullptr), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM), m_readOnly(false),\n    m_isMayflyFD(false) {\n    if (!m_diskFile.isFileValid()) {\n        MMKVError(\"fd %d invalid\", ashmemFD);\n    } else {\n        m_size = m_diskFile.m_size;\n        MMKVInfo(\"ashmem name:%s, size:%zu\", m_diskFile.m_path.c_str(), m_size);\n        mmapOrCleanup(nullptr);\n    }\n}\n\n} // namespace mmkv\n\n#    pragma mark - ashmem\n#    include <dlfcn.h>\n#    include <sys/ioctl.h>\n\nnamespace mmkv {\n\nconstexpr auto ASHMEM_NAME_LEN = 256;\nconstexpr auto ASHMEM_IOC = 0x77;\n#    define ASHMEM_SET_NAME _IOW(ASHMEM_IOC, 1, char[ASHMEM_NAME_LEN])\n#    define ASHMEM_GET_NAME _IOR(ASHMEM_IOC, 2, char[ASHMEM_NAME_LEN])\n#    define ASHMEM_SET_SIZE _IOW(ASHMEM_IOC, 3, size_t)\n#    define ASHMEM_GET_SIZE _IO(ASHMEM_IOC, 4)\n\n#ifndef MMKV_OHOS\nint g_android_api = __ANDROID_API_L__;\n#endif\nstd::string g_android_tmpDir = \"/data/local/tmp/\";\n\n#ifndef MMKV_OHOS\nvoid *loadLibrary() {\n    auto name = \"libandroid.so\";\n    static auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);\n    if (handle == RTLD_DEFAULT) {\n        MMKVError(\"unable to load library %s\", name);\n    }\n    return handle;\n}\n\ntypedef int (*AShmem_create_t)(const char *name, size_t size);\ntypedef size_t (*AShmem_getSize_t)(int fd);\n\n#endif\n\nint ASharedMemory_create(const char *name, size_t size) {\n#ifndef MMKV_OHOS\n    if (g_android_api >= __ANDROID_API_O__ || g_android_api >= __ANDROID_API_M__) {\n        static auto handle = loadLibrary();\n        static AShmem_create_t funcPtr =\n            (handle != nullptr) ? reinterpret_cast<AShmem_create_t>(dlsym(handle, \"ASharedMemory_create\")) : nullptr;\n        if (funcPtr) {\n            int fd = funcPtr(name, size);\n            if (fd < 0) {\n                MMKVError(\"fail to ASharedMemory_create %s with size %zu, errno:%s\", name, size, strerror(errno));\n            } else {\n                MMKVInfo(\"ASharedMemory_create %s with size %zu, fd:%d\", name, size, fd);\n                return fd;\n            }\n        } else if (g_android_api >= __ANDROID_API_O__) {\n            MMKVWarning(\"fail to locate ASharedMemory_create() from loading libandroid.so\");\n        }\n\n        static AShmem_create_t regionFuncPtr =\n            (handle != nullptr) ? reinterpret_cast<AShmem_create_t>(dlsym(handle, \"ashmem_create_region\")) : nullptr;\n        if (regionFuncPtr) {\n            int fd = regionFuncPtr(name, size);\n            if (fd < 0) {\n                MMKVError(\"fail to ashmem_create_region %s with size %zu, errno:%s\", name, size, strerror(errno));\n            } else {\n                MMKVInfo(\"ashmem_create_region %s with size %zu, fd:%d\", name, size, fd);\n                return fd;\n            }\n        } else {\n            MMKVWarning(\"fail to locate ashmem_create_region() from loading libandroid.so\");\n        }\n    }\n#endif\n    int fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC);\n    if (fd < 0) {\n        MMKVError(\"fail to open ashmem:%s, %s\", name, strerror(errno));\n    } else {\n        if (ioctl(fd, ASHMEM_SET_NAME, name) != 0) {\n            MMKVError(\"fail to set ashmem name:%s, %s\", name, strerror(errno));\n        } else if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {\n            MMKVError(\"fail to set ashmem:%s, size %zu, %s\", name, size, strerror(errno));\n        }\n    }\n    return fd;\n}\n\nsize_t ASharedMemory_getSize(int fd) {\n    size_t size = 0;\n#ifndef MMKV_OHOS\n    if (g_android_api >= __ANDROID_API_O__) {\n        static auto handle = loadLibrary();\n        static AShmem_getSize_t funcPtr =\n            (handle != nullptr) ? reinterpret_cast<AShmem_getSize_t>(dlsym(handle, \"ASharedMemory_getSize\")) : nullptr;\n        if (funcPtr) {\n            size = funcPtr(fd);\n            if (size == 0) {\n                MMKVError(\"fail to ASharedMemory_getSize:%d, %s\", fd, strerror(errno));\n            }\n        } else {\n            MMKVWarning(\"fail to locate ASharedMemory_create() from loading libandroid.so\");\n        }\n    }\n#endif\n    if (size == 0) {\n        int tmp = ioctl(fd, ASHMEM_GET_SIZE, nullptr);\n        if (tmp < 0) {\n            MMKVError(\"fail to get ashmem size:%d, %s\", fd, strerror(errno));\n        } else {\n            size = static_cast<size_t>(tmp);\n        }\n    }\n    return size;\n}\n\nstring ASharedMemory_getName(int fd) {\n    // Android Q doesn't have ASharedMemory_getName()\n    // I've make a request to Google, https://issuetracker.google.com/issues/130741665\n    // There's nothing we can do before it's supported officially by Google\n#ifndef MMKV_OHOS\n    if (g_android_api >= __ANDROID_API_O__) {\n        return \"\";\n    }\n#endif\n\n    char name[ASHMEM_NAME_LEN] = {0};\n    if (ioctl(fd, ASHMEM_GET_NAME, name) != 0) {\n        MMKVError(\"fail to get ashmem name:%d, %s\", fd, strerror(errno));\n        return \"\";\n    }\n    return {name};\n}\n\n} // namespace mmkv\n\nMMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID) {\n    return MMKVPath_t(ASHMEM_NAME_DEF) + MMKV_PATH_SLASH + mmapID;\n}\n\nstatic long long timespec_to_ms(struct timespec ts) {\n    return (long long)ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;\n}\n\nlong long getFileModifyTimeInMS(const char *path) {\n    if (!path) {\n        return -1;\n    }\n    struct stat soStat = {};\n    if (::stat(path, &soStat) < 0) {\n        MMKVError(\"fail to stat %s: %d(%s)\", path, errno, strerror(errno));\n        return -1;\n    }\n    return timespec_to_ms(soStat.st_mtim);\n}\n\n#endif // MMKV_ANDROID\n"
  },
  {
    "path": "Core/MemoryFile_Linux.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2021 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MemoryFile.h\"\n\n#if defined(MMKV_ANDROID) || defined(MMKV_LINUX)\n#    include \"InterProcessLock.h\"\n#    include \"MMBuffer.h\"\n#    include \"MMKVLog.h\"\n#    include \"ScopedLock.hpp\"\n#    include <cerrno>\n#    include <utility>\n#    include <fcntl.h>\n#    include <sys/mman.h>\n#    include <sys/stat.h>\n#    include <unistd.h>\n#    include <sys/file.h>\n#    include <dirent.h>\n#    include <cstring>\n#    include <sys/sendfile.h>\n#    include <sys/syscall.h>\n\n#ifdef MMKV_ANDROID\n#include <dlfcn.h>\ntypedef int (*renameat2_t)(int old_dir_fd, const char* old_path, int new_dir_fd, const char* new_path, unsigned flags);\n#endif\n\n#ifndef RENAME_EXCHANGE\n#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */\n#endif\n\nnamespace mmkv {\n\nbool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    bool renamed = false;\n\n    // try renameat2() first\n#if defined(SYS_renameat2)\n#ifdef MMKV_ANDROID\n    static auto g_renameat2 = (renameat2_t) dlsym(RTLD_DEFAULT, \"renameat2\");\n    if (g_renameat2) {\n        renamed = (g_renameat2(AT_FDCWD, srcPath.c_str(), AT_FDCWD, dstPath.c_str(), RENAME_EXCHANGE) == 0);\n    }\n    if (!renamed && errno != ENOENT) {\n        MMKVWarning(\"fail on renameat2() [%s] to [%s], %d(%s)\", srcPath.c_str(), dstPath.c_str(), errno,\n                    strerror(errno));\n    }\n#endif\n    if (!renamed) {\n        renamed = (syscall(SYS_renameat2, AT_FDCWD, srcPath.c_str(), AT_FDCWD, dstPath.c_str(), RENAME_EXCHANGE) == 0);\n    }\n    if (!renamed && errno != ENOENT) {\n        MMKVWarning(\"fail on syscall(SYS_renameat2) [%s] to [%s], %d(%s)\", srcPath.c_str(), dstPath.c_str(), errno,\n                    strerror(errno));\n    }\n\n    if (renamed && (srcPath != dstPath)) {\n        ::unlink(srcPath.c_str());\n    }\n#endif // SYS_renameat2\n\n    if (!renamed) {\n        if (::rename(srcPath.c_str(), dstPath.c_str()) != 0) {\n            MMKVError(\"fail to rename [%s] to [%s], %d(%s)\", srcPath.c_str(), dstPath.c_str(), errno, strerror(errno));\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// do it by sendfile()\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {\n    if (dstFD < 0) {\n        return false;\n    }\n    File srcFile(srcPath, OpenFlag::ReadOnly);\n    if (!srcFile.isFileValid()) {\n        return false;\n    }\n    auto srcFileSize = srcFile.getActualFileSize();\n\n    lseek(dstFD, 0, SEEK_SET);\n    auto writtenSize = ::sendfile(dstFD, srcFile.getFd(), nullptr, srcFileSize);\n    auto ret = (writtenSize == srcFileSize);\n    if (!ret) {\n        if (writtenSize < 0) {\n            MMKVError(\"fail to sendfile() %s to fd[%d], %d(%s)\", srcPath.c_str(), dstFD, errno, strerror(errno));\n        } else {\n            MMKVError(\"sendfile() %s to fd[%d], written %lld < %zu\", srcPath.c_str(), dstFD, writtenSize, srcFileSize);\n        }\n    } else if (needTruncate) {\n        size_t dstFileSize = 0;\n        getFileSize(dstFD, dstFileSize);\n        if ((dstFileSize != srcFileSize) && (::ftruncate(dstFD, static_cast<off_t>(srcFileSize)) != 0)) {\n            MMKVError(\"fail to truncate [%d] to size [%zu], %d(%s)\", dstFD, srcFileSize, errno, strerror(errno));\n            ret = false;\n        }\n    }\n\n    if (ret) {\n        MMKVInfo(\"copy content from %s to fd[%d] finish\", srcPath.c_str(), dstFD);\n    }\n    return ret;\n}\n\n} // namespace mmkv\n\n#endif // defined(MMKV_ANDROID) || defined(MMKV_LINUX)\n"
  },
  {
    "path": "Core/MemoryFile_OSX.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MemoryFile.h\"\n#include \"MMKVLog.h\"\n\n#ifdef MMKV_IOS\n\nusing namespace std;\n\nnamespace mmkv {\n\nvoid tryResetFileProtection(const string &path) {\n    @autoreleasepool {\n        NSString *nsPath = [NSString stringWithUTF8String:path.c_str()];\n        NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:nsPath error:nullptr];\n        NSString *protection = [attr valueForKey:NSFileProtectionKey];\n        MMKVInfo(\"protection on [%@] is %@\", nsPath, protection);\n        if ([protection isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication] == NO) {\n            NSMutableDictionary *newAttr = [NSMutableDictionary dictionaryWithDictionary:attr];\n            [newAttr setObject:NSFileProtectionCompleteUntilFirstUserAuthentication forKey:NSFileProtectionKey];\n            NSError *err = nil;\n            [[NSFileManager defaultManager] setAttributes:newAttr ofItemAtPath:nsPath error:&err];\n            if (err != nil) {\n                MMKVError(\"fail to set attribute %@ on [%@]: %@\", NSFileProtectionCompleteUntilFirstUserAuthentication,\n                          nsPath, err);\n            }\n        }\n    }\n}\n\n} // namespace mmkv\n\n#endif // MMKV_IOS\n\n#ifdef MMKV_APPLE\n\n#include <copyfile.h>\n#include <unistd.h>\n\nnamespace mmkv {\n\nbool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    if (srcPath.empty() || dstPath.empty()) {\n        return false;\n    }\n    bool renamed = false;\n\n    // try atomic swap first\n    if (@available(iOS 10.0, watchOS 3.0, macOS 10.12, *)) {\n        // renameat2() equivalent\n        if (renamex_np(srcPath.c_str(), dstPath.c_str(), RENAME_SWAP) == 0) {\n            renamed = true;\n            if (srcPath != dstPath) {\n                ::unlink(srcPath.c_str());\n            }\n        } else if (errno != ENOENT) {\n            MMKVError(\"fail to renamex_np %s to %s, %s\", srcPath.c_str(), dstPath.c_str(), strerror(errno));\n        }\n    }\n\n    if (!renamed) {\n        // try old style rename\n        if (rename(srcPath.c_str(), dstPath.c_str()) != 0) {\n            MMKVError(\"fail to rename %s to %s, %s\", srcPath.c_str(), dstPath.c_str(), strerror(errno));\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    // prepare a temp file for atomic rename, avoid data corruption of sudden crash\n    NSString *uniqueFileName = [NSString stringWithFormat:@\"mmkv_%zu\", (size_t) NSDate.timeIntervalSinceReferenceDate];\n    NSString *tmpFile = [NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName];\n    if (copyfile(srcPath.c_str(), tmpFile.UTF8String, nullptr, COPYFILE_UNLINK | COPYFILE_CLONE) != 0) {\n        MMKVError(\"fail to copyfile [%s] to [%s], %s\", srcPath.c_str(), tmpFile.UTF8String, strerror(errno));\n        return false;\n    }\n    MMKVInfo(\"copyfile [%s] to [%s]\", srcPath.c_str(), tmpFile.UTF8String);\n\n    if (tryAtomicRename(tmpFile.UTF8String, dstPath.c_str())) {\n        MMKVInfo(\"copyfile [%s] to [%s] finish.\", srcPath.c_str(), dstPath.c_str());\n        return true;\n    }\n\n    MMKVInfo(\"rename fail, try copy file content instead.\");\n    auto ret = copyFileContent(tmpFile.UTF8String, dstPath);\n\n    unlink(tmpFile.UTF8String);\n    return ret;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);\n    if (!dstFile.isFileValid()) {\n        return false;\n    }\n    if (copyFileContent(srcPath, dstFile.getFd())) {\n        MMKVInfo(\"copy content from %s to fd[%s] finish\", srcPath.c_str(), dstPath.c_str());\n        return true;\n    }\n    MMKVError(\"fail to copyfile(): target file %s\", dstPath.c_str());\n    return false;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {\n    if (dstFD < 0) {\n        return false;\n    }\n\n    File srcFile(srcPath, OpenFlag::ReadOnly);\n    if (!srcFile.isFileValid()) {\n        return false;\n    }\n\n    // sendfile() equivalent\n    if (::fcopyfile(srcFile.getFd(), dstFD, nullptr, COPYFILE_ALL) == 0) {\n        MMKVInfo(\"copy content from %s to fd[%d] finish\", srcPath.c_str(), dstFD);\n        return true;\n    }\n    MMKVError(\"fail to copyfile(): %d(%s), source file %s\", errno, strerror(errno), srcPath.c_str());\n    return false;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {\n    return copyFileContent(srcPath, dstFD);\n}\n\nbool isDiskOfMMAPFileCorrupted(MemoryFile *file, bool &needReportReadFail) {\n    uint32_t info;\n    auto fd = file->getFd();\n    auto path = file->getPath().c_str();\n\n    auto oldPos = lseek(fd, 0, SEEK_CUR);\n    lseek(fd, 0, SEEK_SET);\n    auto size = read(fd, &info, sizeof(info));\n    auto err = errno;\n    lseek(fd, oldPos, SEEK_SET);\n\n    if (size <= 0) {\n        needReportReadFail = true;\n        MMKVError(\"fail to read [%s] from fd [%d], errno: %d (%s)\", path, fd, err, strerror(err));\n        if (err == EDEVERR || err == EILSEQ || err == EINVAL || err == ENXIO) {\n            MMKVWarning(\"file fail to read, consider it illegal, delete now: [%s]\", path);\n            return true;\n        }\n    }\n    file->cleanMayflyFD();\n    return false;\n}\n\n} // namespace mmkv\n\n#endif // MMKV_APPLE\n"
  },
  {
    "path": "Core/MemoryFile_Win32.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MemoryFile.h\"\n\n#ifdef MMKV_WIN32\n\n#    include \"InterProcessLock.h\"\n#    include \"MMBuffer.h\"\n#    include \"MMKVLog.h\"\n#    include \"ScopedLock.hpp\"\n#    include \"ThreadLock.h\"\n#    include <cassert>\n#    include <strsafe.h>\n#    include <filesystem>\n\nusing namespace std;\nnamespace fs = std::filesystem;\n\nnamespace mmkv {\n\nstatic bool getFileSize(MMKVFileHandle_t fd, size_t &size);\nstatic bool getFileSize(const wchar_t *filename, size_t &size);\nstatic bool ftruncate(MMKVFileHandle_t file, size_t size);\n\nFile::File(MMKVPath_t path, OpenFlag flag)\n    : m_path(std::move(path)), m_utf8Path(MMKVPath_t2String(m_path)), m_fd(INVALID_HANDLE_VALUE), m_flag(flag) {\n    open();\n}\n\nstatic pair<int, int> OpenFlag2NativeFlag(OpenFlag flag) {\n    int access = 0, create = OPEN_EXISTING;\n    if ((flag & OpenFlagRWMask) == OpenFlag::ReadWrite) {\n        access = (GENERIC_READ | GENERIC_WRITE);\n    } else if (flag & OpenFlag::ReadOnly) {\n        access |= GENERIC_READ;\n    } else if (flag & OpenFlag::WriteOnly) {\n        access |= GENERIC_WRITE;\n    }\n    if (flag & OpenFlag::Create) {\n        create = OPEN_ALWAYS;\n    }\n    if (flag & OpenFlag::Excel) {\n        access = CREATE_NEW;\n    }\n    if (flag & OpenFlag::Truncate) {\n        access = CREATE_ALWAYS;\n    }\n    return {access, create};\n}\n\nbool File::open() {\n    if (isFileValid()) {\n        return true;\n    }\n    auto pair = OpenFlag2NativeFlag(m_flag);\n    m_fd = CreateFile(m_path.c_str(), pair.first, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,\n                      pair.second, FILE_ATTRIBUTE_NORMAL, nullptr);\n    if (!isFileValid()) {\n        MMKVError(\"fail to open:[%s], flag %x, error %d\", m_utf8Path.c_str(), m_flag, GetLastError());\n        return false;\n    }\n    MMKVInfo(\"open fd[%p], flag %x, %s\", m_fd, m_flag, m_utf8Path.c_str());\n    return true;\n}\n\nvoid File::close() {\n    if (isFileValid()) {\n        MMKVInfo(\"closing fd[%p], %s\", m_fd, m_utf8Path.c_str());\n        if (CloseHandle(m_fd)) {\n            m_fd = INVALID_HANDLE_VALUE;\n        } else {\n            MMKVError(\"fail to close [%s], %d\", m_utf8Path.c_str(), GetLastError());\n        }\n    }\n}\n\nsize_t File::getActualFileSize() const {\n    size_t size = 0;\n    if (isFileValid()) {\n        mmkv::getFileSize(m_fd, size);\n    } else {\n        mmkv::getFileSize(m_path.c_str(), size);\n    }\n    return size;\n}\n\nMemoryFile::MemoryFile(MMKVPath_t path, size_t expectedCapacity, bool readOnly, bool mayflyFD)\n    : m_diskFile(std::move(path), readOnly ? OpenFlag::ReadOnly : (OpenFlag::ReadWrite | OpenFlag::Create))\n    , m_fileMapping(nullptr)\n    , m_ptr(nullptr)\n    , m_size(0)\n    , m_readOnly(readOnly)\n    , m_isMayflyFD(mayflyFD) {\n    reloadFromFile(expectedCapacity);\n}\n\nbool MemoryFile::truncate(size_t size, FileLock *fileLock) {\n    if (m_isMayflyFD) {\n        openIfNeeded();\n    }\n    if (!m_diskFile.isFileValid()) {\n        return false;\n    }\n    if (size == m_size) {\n        return true;\n    }\n    if (m_readOnly) {\n        // truncate readonly file not allow\n        return false;\n    }\n\n    auto oldSize = m_size;\n    m_size = size;\n    // round up to (n * pagesize)\n    if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {\n        m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;\n    }\n\n    // Win32 won't ftruncate a file if there's active file mmapping/handle, we have to unmmap/close ahead\n    bool needMMapOnFailure = false;\n    if (m_ptr) {\n        // if we have a valid file mapping before, we should restore it regardless\n        needMMapOnFailure = true;\n        if (!UnmapViewOfFile(m_ptr)) {\n            MMKVError(\"fail to munmap [%s], %d\", m_diskFile.getUTF8Path().c_str(), GetLastError());\n        }\n        m_ptr = nullptr;\n    }\n    if (m_fileMapping) {\n        CloseHandle(m_fileMapping);\n        m_fileMapping = nullptr;\n    }\n\n    if (!ftruncate(m_diskFile.getFd(), m_size)) {\n        MMKVError(\"fail to truncate [%s] to size %zu\", m_diskFile.getUTF8Path().c_str(), m_size);\n        m_size = oldSize;\n        if (needMMapOnFailure) {\n            mmapOrCleanup(fileLock);\n        }\n        return false;\n    }\n    if (m_size > oldSize) {\n        if (!zeroFillFile(m_diskFile.getFd(), oldSize, m_size - oldSize)) {\n            MMKVError(\"fail to zeroFile [%s] to size %zu\", m_diskFile.getUTF8Path().c_str(), m_size);\n            m_size = oldSize;\n            if (needMMapOnFailure) {\n                mmapOrCleanup(fileLock);\n            }\n            return false;\n        }\n    }\n\n    return mmapOrCleanup(fileLock);\n}\n\nbool MemoryFile::msync(SyncFlag syncFlag) {\n    if (m_readOnly) {\n        // there's no point in msync() readonly memory\n        return true;\n    }\n    if (m_ptr) {\n        if (FlushViewOfFile(m_ptr, m_size)) {\n            if (syncFlag == MMKV_SYNC && openIfNeeded()) {\n                auto ret = FlushFileBuffers(m_diskFile.getFd());\n                if (!ret) {\n                    MMKVError(\"fail to FlushFileBuffers [%s]:%d\", m_diskFile.getUTF8Path().c_str(), GetLastError());\n                }\n                cleanMayflyFD();\n                return ret;\n            }\n            return true;\n        }\n        MMKVError(\"fail to FlushViewOfFile [%s]:%d\", m_diskFile.getUTF8Path().c_str(), GetLastError());\n        return false;\n    }\n    return false;\n}\n\nbool MemoryFile::mmapOrCleanup(FileLock *fileLock) {\n    auto mode = m_readOnly ? PAGE_READONLY : PAGE_READWRITE;\n    m_fileMapping = CreateFileMapping(m_diskFile.getFd(), nullptr, mode, 0, 0, nullptr);\n    if (!m_fileMapping) {\n        MMKVError(\"fail to CreateFileMapping [%s], mode %x, %d\", m_diskFile.getUTF8Path().c_str(), mode,\n                  GetLastError());\n        return false;\n    } else {\n        auto viewMode = m_readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;\n        m_ptr = (char*)MapViewOfFile(m_fileMapping, viewMode, 0, 0, 0);\n        if (!m_ptr) {\n            MMKVError(\"fail to mmap [%s], mode %x, %d\", m_diskFile.getUTF8Path().c_str(), viewMode, GetLastError());\n\n            doCleanMemoryCache(true);\n            return false;\n        }\n        MMKVInfo(\"mmap to address [%p], [%s]\", m_ptr, m_diskFile.getUTF8Path().c_str());\n\n        if (m_isMayflyFD && fileLock) {\n            fileLock->destroyAndUnLock();\n        }\n\n        cleanMayflyFD();\n        return true;\n    }\n}\n\nvoid MemoryFile::reloadFromFile(size_t expectedCapacity) {\n    if (isFileValid()) {\n        MMKVWarning(\"calling reloadFromFile while the cache [%s] is still valid\", m_diskFile.getUTF8Path().c_str());\n        assert(0);\n        clearMemoryCache();\n    }\n    if (openIfNeeded()) {\n        FileLock fileLock(m_diskFile.getFd());\n        InterProcessLock lock(&fileLock, SharedLockType);\n        SCOPED_LOCK(&lock);\n\n        mmkv::getFileSize(m_diskFile.getFd(), m_size);\n        size_t expectedSize = std::max<size_t>(DEFAULT_MMAP_SIZE, roundUp<size_t>(expectedCapacity, DEFAULT_MMAP_SIZE));\n        // round up to (n * pagesize)\n        if (!m_readOnly && (m_size < expectedSize || (m_size % DEFAULT_MMAP_SIZE != 0))) {\n            InterProcessLock exclusiveLock(&fileLock, ExclusiveLockType);\n            SCOPED_LOCK(&exclusiveLock);\n\n            size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;;\n            roundSize = std::max<size_t>(expectedSize, roundSize);\n            truncate(roundSize, &fileLock);\n        } else {\n            mmapOrCleanup(&fileLock);\n        }\n    }\n}\n\nvoid MemoryFile::doCleanMemoryCache(bool forceClean) {\n    if (m_ptr) {\n        UnmapViewOfFile(m_ptr);\n        m_ptr = nullptr;\n    }\n    if (m_fileMapping) {\n        CloseHandle(m_fileMapping);\n        m_fileMapping = nullptr;\n    }\n    m_diskFile.close();\n}\n\nbool MemoryFile::openIfNeeded() {\n    if (!m_diskFile.isFileValid()) {\n        return m_diskFile.open();\n    }\n    return true;\n}\n\nvoid MemoryFile::cleanMayflyFD() {\n    if (m_isMayflyFD) {\n        if (m_diskFile.isFileValid()) {\n            m_diskFile.close();\n        }\n        if (m_fileMapping) {\n            CloseHandle(m_fileMapping);\n            m_fileMapping = nullptr;\n        }\n    }\n}\n\nsize_t MemoryFile::getActualFileSize() {\n    if (!m_isMayflyFD && !m_diskFile.isFileValid()) {\n        return 0;\n    }\n\n    return m_diskFile.getActualFileSize();\n}\n\nMMKVFileHandle_t MemoryFile::getFd() {\n    if (m_isMayflyFD) {\n        openIfNeeded();\n    }\n    return m_diskFile.getFd();\n}\n\nsize_t getPageSize() {\n    SYSTEM_INFO system_info;\n    GetSystemInfo(&system_info);\n    return system_info.dwPageSize;\n}\n\nMMKVPath_t absolutePath(const MMKVPath_t& path) {\n    fs::path relative_path(path);\n    fs::path absolute_path = fs::absolute(relative_path);\n    try {\n        fs::path normalized = fs::weakly_canonical(absolute_path);\n        return normalized.wstring();\n    } catch (std::exception &e) {\n        const auto &utf8Path = MMKVPath_t2String(absolute_path.wstring());\n        MMKVError(\"fail to weakly_canonical() path %s, error: %s\", utf8Path.c_str(), e.what());\n    }\n    return absolute_path.wstring();\n}\n\nbool isFileExist(const MMKVPath_t &nsFilePath) {\n    if (nsFilePath.empty()) {\n        return false;\n    }\n    auto attribute = GetFileAttributes(nsFilePath.c_str());\n    return (attribute != INVALID_FILE_ATTRIBUTES);\n}\n\nbool mkPath(const MMKVPath_t &str) {\n    wchar_t *path = _wcsdup(str.c_str());\n\n    bool done = false;\n    wchar_t *slash = path;\n\n    while (!done) {\n        slash += wcsspn(slash, L\"\\\\\");\n        slash += wcscspn(slash, L\"\\\\\");\n\n        done = (*slash == L'\\0');\n        *slash = L'\\0';\n\n        auto attribute = GetFileAttributes(path);\n        if (attribute == INVALID_FILE_ATTRIBUTES) {\n            if (!CreateDirectory(path, nullptr)) {\n                const auto &utf8Path = MMKVPath_t2String(str);\n                MMKVError(\"fail to create dir:%s, %d\", utf8Path.c_str(), GetLastError());\n                free(path);\n                return false;\n            }\n        } else if (!(attribute & FILE_ATTRIBUTE_DIRECTORY)) {\n            const auto &utf8Path = MMKVPath_t2String(str);\n            MMKVError(\"%s attribute:%d not a directory\", utf8Path.c_str(), attribute);\n            free(path);\n            return false;\n        }\n\n        *slash = L'\\\\';\n    }\n    free(path);\n    return true;\n}\n\nMMBuffer *readWholeFile(const MMKVPath_t &nsFilePath) {\n    MMBuffer *buffer = nullptr;\n    auto fd = CreateFile(nsFilePath.c_str(), GENERIC_READ | GENERIC_WRITE,\n                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,\n                         FILE_ATTRIBUTE_NORMAL, nullptr);\n    if (fd != INVALID_HANDLE_VALUE) {\n        size_t fileLength = 0;\n        getFileSize(fd, fileLength);\n        if (fileLength > 0) {\n            buffer = new MMBuffer(static_cast<size_t>(fileLength));\n            SetFilePointer(fd, 0, 0, FILE_BEGIN);\n            DWORD readSize = 0;\n            if (ReadFile(fd, buffer->getPtr(), (DWORD) fileLength, &readSize, nullptr)) {\n                //fileSize = readSize;\n            } else {\n                const auto &utf8Path = MMKVPath_t2String(nsFilePath);\n                MMKVWarning(\"fail to read %s: %d\", utf8Path.c_str(), GetLastError());\n                delete buffer;\n                buffer = nullptr;\n            }\n        }\n        CloseHandle(fd);\n    } else {\n        const auto &utf8Path = MMKVPath_t2String(nsFilePath);\n        MMKVWarning(\"fail to open %s: %d\", utf8Path.c_str(), GetLastError());\n    }\n    return buffer;\n}\n\nbool zeroFillFile(MMKVFileHandle_t file, size_t startPos, size_t size) {\n    if (file == INVALID_HANDLE_VALUE) {\n        return false;\n    }\n    if (size == 0) {\n        return true;\n    }\n\n    LARGE_INTEGER position = {};\n    position.QuadPart = startPos;\n    if (!SetFilePointerEx(file, position, nullptr, FILE_BEGIN)) {\n        MMKVError(\"fail to lseek fd[%p], error:%d\", file, GetLastError());\n        return false;\n    }\n\n    static const char zeros[4096] = {0};\n    while (size >= sizeof(zeros)) {\n        DWORD bytesWritten = 0;\n        if (!WriteFile(file, zeros, sizeof(zeros), &bytesWritten, nullptr)) {\n            MMKVError(\"fail to write fd[%p], error:%d\", file, GetLastError());\n            return false;\n        }\n        size -= bytesWritten;\n    }\n    if (size > 0) {\n        DWORD bytesWritten = 0;\n        if (!WriteFile(file, zeros, (DWORD) size, &bytesWritten, nullptr)) {\n            MMKVError(\"fail to write fd[%p], error:%d\", file, GetLastError());\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool ftruncate(MMKVFileHandle_t file, size_t size) {\n    LARGE_INTEGER large = {};\n    large.QuadPart = size;\n    if (SetFilePointerEx(file, large, 0, FILE_BEGIN)) {\n        if (SetEndOfFile(file)) {\n            return true;\n        }\n        MMKVError(\"fail to SetEndOfFile:%d\", GetLastError());\n        return false;\n    } else {\n        MMKVError(\"fail to SetFilePointer:%d\", GetLastError());\n        return false;\n    }\n}\n\nstatic bool getFileSize(MMKVFileHandle_t fd, size_t &size) {\n    LARGE_INTEGER filesize = {0};\n    if (GetFileSizeEx(fd, &filesize)) {\n        size = static_cast<size_t>(filesize.QuadPart);\n        return true;\n    }\n    return false;\n}\n\nbool getFileSize(const wchar_t *filename, size_t &size) {\n    WIN32_FILE_ATTRIBUTE_DATA fileAttr = {};\n    if (GetFileAttributesEx(filename, GetFileExInfoStandard, &fileAttr)) {\n        size = ((ULONGLONG)fileAttr.nFileSizeHigh << 32) | fileAttr.nFileSizeLow;\n        return true;\n    }\n    return false;\n}\n\nstatic pair<MMKVPath_t, MMKVFileHandle_t> createUniqueTempFile(const wchar_t *prefix) {\n    wchar_t lpTempPathBuffer[MAX_PATH];\n    //  Gets the temp path env string (no guarantee it's a valid path).\n    auto dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);\n    if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {\n        MMKVError(\"GetTempPath failed %d\", GetLastError());\n        return {L\"\", INVALID_HANDLE_VALUE};\n    }\n    //  Generates a temporary file name.\n    wchar_t szTempFileName[MAX_PATH];\n    if (!GetTempFileName(lpTempPathBuffer, prefix, 0, szTempFileName)) {\n        MMKVError(\"GetTempFileName failed %d\", GetLastError());\n        return {L\"\", INVALID_HANDLE_VALUE};\n    }\n    auto hTempFile =\n        CreateFile(szTempFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);\n    const auto &utf8Path = MMKVPath_t2String(szTempFileName);\n    if (hTempFile == INVALID_HANDLE_VALUE) {\n        MMKVError(\"fail to create unique temp file [%s], %d\", utf8Path.c_str(), GetLastError());\n        return {L\"\", INVALID_HANDLE_VALUE};\n    }\n    MMKVDebug(\"create unique temp file [%s] with fd[%p]\", utf8Path.c_str(), hTempFile);\n    return {MMKVPath_t(szTempFileName), hTempFile};\n}\n\nbool tryAtomicRename(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    if (MoveFileEx(srcPath.c_str(), dstPath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) == 0) {\n        const auto &utf8SrcPath = MMKVPath_t2String(srcPath);\n        const auto &utf8DstPath = MMKVPath_t2String(dstPath);\n        MMKVError(\"MoveFileEx [%s] to [%s] failed %d\", utf8SrcPath.c_str(), utf8DstPath.c_str(), GetLastError());\n        return false;\n    }\n    return true;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD, bool needTruncate) {\n    if (dstFD == INVALID_HANDLE_VALUE) {\n        return false;\n    }\n    bool ret = false;\n    File srcFile(srcPath, OpenFlag::ReadOnly);\n    if (!srcFile.isFileValid()) {\n        return false;\n    }\n    auto bufferSize = getPageSize();\n    auto buffer = (char *) malloc(bufferSize);\n    if (!buffer) {\n        MMKVError(\"fail to malloc size %zu, %d(%s)\", bufferSize, errno, strerror(errno));\n        goto errorOut;\n    }\n    SetFilePointer(dstFD, 0, 0, FILE_BEGIN);\n\n    // the Win32 platform don't have sendfile()/fcopyfile() equivalent, do it the hard way\n    while (true) {\n        DWORD sizeRead = 0;\n        if (!ReadFile(srcFile.getFd(), buffer, (DWORD) bufferSize, &sizeRead, nullptr)) {\n            MMKVError(\"fail to read %s: %d\", srcFile.getUTF8Path().c_str(), GetLastError());\n            goto errorOut;\n        }\n\n        DWORD sizeWrite = 0;\n        if (!WriteFile(dstFD, buffer, sizeRead, &sizeWrite, nullptr)) {\n            MMKVError(\"fail to write fd [%d], %d\", dstFD, GetLastError());\n            goto errorOut;\n        }\n\n        if (sizeRead < bufferSize) {\n            break;\n        }\n    }\n    if (needTruncate) {\n        size_t dstFileSize = 0;\n        getFileSize(dstFD, dstFileSize);\n        auto srcFileSize = srcFile.getActualFileSize();\n        if ((dstFileSize != srcFileSize) && !ftruncate(dstFD, static_cast<off_t>(srcFileSize))) {\n            MMKVError(\"fail to truncate [%d] to size [%zu]\", dstFD, srcFileSize);\n            goto errorOut;\n        }\n    }\n\n    ret = true;\n    MMKVInfo(\"copy content from %s to fd[%d] finish\", srcFile.getUTF8Path().c_str(), dstFD);\n\nerrorOut:\n    free(buffer);\n    return ret;\n}\n\n// copy to a temp file then rename it\n// this is the best we can do on Win32\nbool copyFile(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    auto pair = createUniqueTempFile(L\"MMKV\");\n    auto tmpFD = pair.second;\n    auto &tmpPath = pair.first;\n    if (tmpFD == INVALID_HANDLE_VALUE) {\n        return false;\n    }\n\n    bool renamed = false;\n    if (copyFileContent(srcPath, tmpFD, false)) {\n        const auto &utf8SrcPath = MMKVPath_t2String(srcPath);\n        const auto &utf8TmpPath = MMKVPath_t2String(tmpPath);\n        MMKVInfo(\"copied file [%s] to [%s]\", utf8SrcPath.c_str(), utf8TmpPath.c_str());\n        CloseHandle(tmpFD);\n        renamed = tryAtomicRename(tmpPath.c_str(), dstPath.c_str());\n        if (renamed) {\n            const auto &utf8DstPath = MMKVPath_t2String(dstPath);\n            MMKVInfo(\"copyfile [%s] to [%s] finish.\", utf8SrcPath.c_str(), utf8DstPath.c_str());\n        }\n    } else {\n        CloseHandle(tmpFD);\n    }\n\n    if (!renamed) {\n        DeleteFile(tmpPath.c_str());\n    }\n    return renamed;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, const MMKVPath_t &dstPath) {\n    File dstFile(dstPath, OpenFlag::WriteOnly | OpenFlag::Create | OpenFlag::Truncate);\n    if (!dstFile.isFileValid()) {\n        return false;\n    }\n    auto ret = copyFileContent(srcPath, dstFile.getFd(), false);\n    if (!ret) {\n        MMKVError(\"fail to copyfile(): target file %s\", dstFile.getUTF8Path().c_str());\n    } else {\n        const auto &utf8SrcPath = MMKVPath_t2String(srcPath);\n        MMKVInfo(\"copy content from %s to [%s] finish\", utf8SrcPath.c_str(), dstFile.getUTF8Path().c_str());\n    }\n    return ret;\n}\n\nbool copyFileContent(const MMKVPath_t &srcPath, MMKVFileHandle_t dstFD) {\n    return copyFileContent(srcPath, dstFD, true);\n}\n\nvoid walkInDir(const MMKVPath_t &dirPath,\n               WalkType type,\n               const std::function<void(const MMKVPath_t &, WalkType)> &walker) {\n    wchar_t szDir[MAX_PATH];\n    StringCchCopy(szDir, MAX_PATH, dirPath.c_str());\n    StringCchCat(szDir, MAX_PATH, L\"\\\\*\");\n\n    WIN32_FIND_DATA ffd;\n    auto hFind = FindFirstFile(szDir, &ffd);\n    if (hFind == INVALID_HANDLE_VALUE) {\n        return;\n    }\n\n    do {\n        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {\n            if (type & WalkFolder) {\n                if (wcscmp(ffd.cFileName, L\".\") == 0 || wcscmp(ffd.cFileName, L\"..\") == 0) {\n                    continue;\n                }\n                walker(dirPath + L\"\\\\\" + ffd.cFileName, WalkFolder);\n            }\n        } else if (type & WalkFile) {\n            walker(dirPath + L\"\\\\\" + ffd.cFileName, WalkFile);\n        }\n    } while (FindNextFile(hFind, &ffd) != 0);\n\n    auto dwError = GetLastError();\n    if (dwError != ERROR_NO_MORE_FILES) {\n        MMKVError(\"WalkInDir fail %d\", dwError);\n    }\n\n    FindClose(hFind);\n}\n\nbool isDiskOfMMAPFileCorrupted(MemoryFile *file, bool &needReportReadFail) {\n    // make sure the file is valid\n    __try {\n        auto filesize = file->getFileSize();\n        volatile uint8_t* ptr = (uint8_t*) file->getMemory();\n        // check the head of every page\n        for (size_t index = 0; index < filesize; index += DEFAULT_MMAP_SIZE) {\n            volatile uint8_t byte = ptr[index];\n            MMKVDebug(\"%zu byte of the file: 0x%x\", index, byte);\n        }\n        // check the very last byte of the file\n        if (filesize > 1) {\n            volatile uint8_t byte = ptr[filesize - 1];\n            MMKVDebug(\"%zu byte of the file: 0x%x\", filesize - 1, byte);\n        }\n    }\n    __except ((GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR || GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)\n              ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        needReportReadFail = true;\n        DWORD errorCode = GetExceptionCode();\n        MMKVError(\"fail to mmap [%s], %d\", file->getUTF8Path().c_str(), errorCode);\n        return true;\n    }\n    return false;\n}\n\nbool deleteFile(const MMKVPath_t &path) {\n    if (!DeleteFile(path.c_str())) {\n        const auto &utf8Path = MMKVPath_t2String(path);\n        MMKVError(\"failed to delete file [%s], %d\", utf8Path.c_str(), GetLastError());\n        return false;\n    }\n    return true;\n}\n\nstd::optional<MMKVPath_t> getUniqueFileName(const MMKVPath_t &folder, const MMKVPath_t &prefix) {\n    // Buffer for the resulting path\n    wchar_t tempFileName[MAX_PATH];\n    UINT uUnique = 0;\n\n    UINT result = GetTempFileName(folder.c_str(), prefix.c_str(), uUnique, tempFileName);\n    if (result == 0) {\n        const auto &utf8Folder = MMKVPath_t2String(folder);\n        MMKVError(\"failed to GetTempFileName file [%s], %d\", utf8Folder.c_str(), GetLastError());\n        return std::nullopt;\n    }\n\n    return std::wstring(tempFileName);\n}\n\n} // namespace mmkv\n\nstd::wstring string2MMKVPath_t(const std::string &str) {\n    auto length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);\n    auto buffer = new wchar_t[length];\n    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, length);\n    wstring result(buffer);\n    delete[] buffer;\n    return result;\n}\n\nstd::string MMKVPath_t2String(const MMKVPath_t &str) {\n    auto length = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, 0, 0);\n    auto buffer = new char[length];\n    WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, buffer, length, 0, 0);\n    string result(buffer);\n    delete[] buffer;\n    return result;\n}\n\n#endif // MMKV_WIN32\n"
  },
  {
    "path": "Core/MiniPBCoder.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MiniPBCoder.h\"\n#include \"CodedInputData.h\"\n#include \"CodedInputDataCrypt.h\"\n#include \"CodedOutputData.h\"\n#include \"PBEncodeItem.hpp\"\n#include \"PBUtility.h\"\n#include \"MMKVLog.h\"\n\n#ifdef MMKV_APPLE\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n#endif // MMKV_APPLE\n\nusing namespace std;\n\nnamespace mmkv {\n\nMiniPBCoder::MiniPBCoder() : m_encodeItems(new std::vector<PBEncodeItem>()) {\n}\n\nMiniPBCoder::MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter) : MiniPBCoder() {\n    m_inputBuffer = inputBuffer;\n#ifndef MMKV_DISABLE_CRYPT\n    if (crypter) {\n        m_inputDataDecrpt = new CodedInputDataCrypt(m_inputBuffer->getPtr(), m_inputBuffer->length(), *crypter);\n    } else {\n        m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());\n    }\n#else\n    m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());\n#endif // MMKV_DISABLE_CRYPT\n}\n\nMiniPBCoder::~MiniPBCoder() {\n    delete m_inputData;\n#ifndef MMKV_DISABLE_CRYPT\n    delete m_inputDataDecrpt;\n#endif\n    delete m_outputBuffer;\n    delete m_outputData;\n    delete m_encodeItems;\n}\n\n// encode\n\n// write object using prepared m_encodeItems[]\nvoid MiniPBCoder::writeRootObject() {\n    for (size_t index = 0, total = m_encodeItems->size(); index < total; index++) {\n        PBEncodeItem *encodeItem = &(*m_encodeItems)[index];\n        switch (encodeItem->type) {\n            case PBEncodeItemType_Data: {\n                m_outputData->writeData(*(encodeItem->value.bufferValue));\n                break;\n            }\n            case PBEncodeItemType_Container: {\n                m_outputData->writeUInt32(encodeItem->valueSize);\n                break;\n            }\n#ifdef MMKV_HAS_CPP20\n            case PBEncodeItemType_Int32: {\n                m_outputData->writeInt32(encodeItem->value.int32Value);\n                break;\n            }\n            case PBEncodeItemType_UInt32: {\n                m_outputData->writeUInt32(encodeItem->value.uint32Value);\n                break;\n            }\n            case PBEncodeItemType_Int64: {\n                m_outputData->writeInt64(encodeItem->value.int64Value);\n                break;\n            }\n            case PBEncodeItemType_UInt64: {\n                m_outputData->writeUInt64(encodeItem->value.uint64Value);\n                break;\n            }\n#endif // MMKV_HAS_CPP20\n            case PBEncodeItemType_String: {\n                m_outputData->writeString(*(encodeItem->value.strValue));\n                break;\n            }\n#ifdef MMKV_APPLE\n            case PBEncodeItemType_NSString: {\n                m_outputData->writeUInt32(encodeItem->valueSize);\n                if (encodeItem->valueSize > 0 && encodeItem->value.tmpObjectValue != nullptr) {\n                    auto obj = (__bridge NSData *) encodeItem->value.tmpObjectValue;\n                    MMBuffer buffer(obj, MMBufferNoCopy);\n                    m_outputData->writeRawData(buffer);\n                }\n                break;\n            }\n            case PBEncodeItemType_NSData: {\n                m_outputData->writeUInt32(encodeItem->valueSize);\n                if (encodeItem->valueSize > 0 && encodeItem->value.objectValue != nullptr) {\n                    auto obj = (__bridge NSData *) encodeItem->value.objectValue;\n                    MMBuffer buffer(obj, MMBufferNoCopy);\n                    m_outputData->writeRawData(buffer);\n                }\n                break;\n            }\n            case PBEncodeItemType_NSDate: {\n                NSDate *oDate = (__bridge NSDate *) encodeItem->value.objectValue;\n                m_outputData->writeDouble(oDate.timeIntervalSince1970);\n                break;\n            }\n#endif // MMKV_APPLE\n            case PBEncodeItemType_None: {\n                MMKVError(\"%d\", encodeItem->type);\n                break;\n            }\n        }\n    }\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const MMBuffer &buffer) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Data;\n        encodeItem->value.bufferValue = &buffer;\n        encodeItem->valueSize = static_cast<uint32_t>(buffer.length());\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const MMKVVector &vec) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto &itr : vec) {\n            const auto &key = itr.first;\n            const auto &value = itr.second;\n#    ifdef MMKV_APPLE\n            if (key.length <= 0) {\n#    else\n            if (key.length() <= 0) {\n#    endif\n                continue;\n            }\n\n            size_t keyIndex = prepareObjectForEncode(key);\n            if (keyIndex < m_encodeItems->size()) {\n                size_t valueIndex = prepareObjectForEncode(value);\n                if (valueIndex < m_encodeItems->size()) {\n                    (*m_encodeItems)[index].valueSize += (*m_encodeItems)[keyIndex].compiledSize;\n                    (*m_encodeItems)[index].valueSize += (*m_encodeItems)[valueIndex].compiledSize;\n                } else {\n                    m_encodeItems->pop_back(); // pop key\n                }\n            }\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nMMBuffer MiniPBCoder::writePreparedItems(size_t index) {\n    try {\n        PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr;\n        if (oItem && oItem->compiledSize > 0) {\n            m_outputBuffer = new MMBuffer(oItem->compiledSize);\n            m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length());\n\n            writeRootObject();\n        }\n\n        return std::move(*m_outputBuffer);\n    } catch (const std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n        return MMBuffer();\n    } catch (...) {\n        MMKVError(\"encode fail\");\n        return MMBuffer();\n    }\n}\n\nMMBuffer MiniPBCoder::encodeDataWithObject(const MMBuffer &obj) {\n    try {\n        auto valueSize = static_cast<uint32_t>(obj.length());\n        auto compiledSize = pbRawVarint32Size(valueSize) + valueSize;\n        MMBuffer result(compiledSize);\n        CodedOutputData output(result.getPtr(), result.length());\n        output.writeData(obj);\n        return result;\n    } catch (const std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n        return MMBuffer();\n    } catch (...) {\n        MMKVError(\"prepare encode fail\");\n        return MMBuffer();\n    }\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const string &str) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_String;\n        encodeItem->value.strValue = &str;\n        encodeItem->valueSize = static_cast<uint32_t>(str.size());\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const MMKV_STRING_CONTAINER &v) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto &str : v) {\n            size_t itemIndex = prepareObjectForEncode(str);\n            if (itemIndex < m_encodeItems->size()) {\n                (*m_encodeItems)[index].valueSize += (*m_encodeItems)[itemIndex].compiledSize;\n            }\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\n#ifdef MMKV_HAS_CPP20\n\nsize_t MiniPBCoder::prepareObjectForEncode(const std::span<const int32_t> &vec) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto value : vec) {\n            PBEncodeItem item;\n            item.type = PBEncodeItemType_Int32;\n            item.value.int32Value = value;\n            item.compiledSize = pbInt32Size(value);\n\n            (*m_encodeItems)[index].valueSize += item.compiledSize;\n            m_encodeItems->push_back(std::move(item));\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const std::span<const uint32_t> &vec) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto value : vec) {\n            PBEncodeItem item;\n            item.type = PBEncodeItemType_UInt32;\n            item.value.uint32Value = value;\n            item.compiledSize = pbUInt32Size(value);\n\n            (*m_encodeItems)[index].valueSize += item.compiledSize;\n            m_encodeItems->push_back(std::move(item));\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const std::span<const int64_t> &vec) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto value : vec) {\n            PBEncodeItem item;\n            item.type = PBEncodeItemType_Int64;\n            item.value.int64Value = value;\n            item.compiledSize = pbInt64Size(value);\n\n            (*m_encodeItems)[index].valueSize += item.compiledSize;\n            m_encodeItems->push_back(std::move(item));\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nsize_t MiniPBCoder::prepareObjectForEncode(const std::span<const uint64_t> &vec) {\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n    {\n        encodeItem->type = PBEncodeItemType_Container;\n        encodeItem->value.bufferValue = nullptr;\n\n        for (const auto value : vec) {\n            PBEncodeItem item;\n            item.type = PBEncodeItemType_UInt64;\n            item.value.uint64Value = value;\n            item.compiledSize = pbUInt64Size(value);\n\n            (*m_encodeItems)[index].valueSize += item.compiledSize;\n            m_encodeItems->push_back(std::move(item));\n        }\n\n        encodeItem = &(*m_encodeItems)[index];\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\n#endif // MMKV_HAS_CPP20\n\nvector<string> MiniPBCoder::decodeOneVector() {\n    vector<string> v;\n\n    m_inputData->readInt32();\n\n    while (!m_inputData->isAtEnd()) {\n        auto value = m_inputData->readString();\n        v.push_back(std::move(value));\n    }\n\n    return v;\n}\n\n#ifdef MMKV_HAS_CPP20\n\nbool MiniPBCoder::decodeOneVector(std::vector<bool> &result) {\n    try {\n        auto size = m_inputData->readUInt32();\n        result.reserve(size / pbBoolSize());\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readBool();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<int32_t> &result) {\n    try {\n        m_inputData->readInt32();\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readInt32();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<uint32_t> &result) {\n    try {\n        m_inputData->readInt32();\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readUInt32();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<int64_t> &result) {\n    try {\n        m_inputData->readInt32();\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readInt64();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<uint64_t> &result) {\n    try {\n        m_inputData->readInt32();\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readUInt64();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<float> &result) {\n    try {\n        auto size = m_inputData->readUInt32();\n        result.reserve(size / pbFloatSize());\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readFloat();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\nbool MiniPBCoder::decodeOneVector(std::vector<double> &result) {\n    try {\n        auto size = m_inputData->readUInt32();\n        result.reserve(size / pbDoubleSize());\n\n        while (!m_inputData->isAtEnd()) {\n            auto value = m_inputData->readDouble();\n            result.push_back(value);\n        }\n        return true;\n    } catch (std::exception &exception) {\n        MMKVError(\"%s\", exception.what());\n    } catch (...) {\n        MMKVError(\"decode fail\");\n    }\n    return false;\n}\n\n#endif // MMKV_HAS_CPP20\n\n#ifndef MMKV_APPLE\nvoid MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t position, bool greedy) {\n    auto block = [position, this](MMKVMap &dictionary) {\n        if (position) {\n            m_inputData->seek(position);\n        } else {\n            m_inputData->readInt32();\n        }\n        while (!m_inputData->isAtEnd()) {\n            KeyValueHolder kvHolder;\n            const auto &key = m_inputData->readString(kvHolder);\n            if (key.length() > 0) {\n                m_inputData->readData(kvHolder);\n                if (kvHolder.valueSize > 0) {\n                    dictionary[key] = std::move(kvHolder);\n                } else {\n                    auto itr = dictionary.find(key);\n                    if (itr != dictionary.end()) {\n                        dictionary.erase(itr);\n                    }\n                }\n            }\n        }\n    };\n\n    if (greedy) {\n        try {\n            block(dic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"prepare encode fail\");\n        }\n    } else {\n        try {\n            MMKVMap tmpDic;\n            block(tmpDic);\n            dic.swap(tmpDic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"prepare encode fail\");\n        }\n    }\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nvoid MiniPBCoder::decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy) {\n    auto block = [position, this](MMKVMapCrypt &dictionary) {\n        if (position) {\n            m_inputDataDecrpt->seek(position);\n        } else {\n            m_inputDataDecrpt->readInt32();\n        }\n        while (!m_inputDataDecrpt->isAtEnd()) {\n            KeyValueHolderCrypt kvHolder;\n            const auto &key = m_inputDataDecrpt->readString(kvHolder);\n            if (key.length() > 0) {\n                m_inputDataDecrpt->readData(kvHolder);\n                if (kvHolder.realValueSize() > 0) {\n                    dictionary[key] = std::move(kvHolder);\n                } else {\n                    auto itr = dictionary.find(key);\n                    if (itr != dictionary.end()) {\n                        dictionary.erase(itr);\n                    }\n                }\n            }\n        }\n    };\n\n    if (greedy) {\n        try {\n            block(dic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"prepare encode fail\");\n        }\n    } else {\n        try {\n            MMKVMapCrypt tmpDic;\n            block(tmpDic);\n            dic.swap(tmpDic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"prepare encode fail\");\n        }\n    }\n}\n#endif // !MMKV_APPLE\n#    endif // MMKV_DISABLE_CRYPT\n\nvector<string> MiniPBCoder::decodeVector(const MMBuffer &oData) {\n    MiniPBCoder oCoder(&oData);\n    return oCoder.decodeOneVector();\n}\n\n#ifdef MMKV_HAS_CPP20\nMMBuffer MiniPBCoder::getEncodeData(const std::vector<bool> &value) {\n    auto valueLength = static_cast<uint32_t>(value.size() * pbBoolSize());\n    auto size = pbRawVarint32Size(valueLength) + valueLength;\n    auto buffer = MMBuffer(size);\n    CodedOutputData output(buffer.getPtr(), size);\n    output.writeUInt32(valueLength);\n\n    for (auto single : value) {\n        output.writeBool(single);\n    }\n    return buffer;\n}\n\nMMBuffer MiniPBCoder::getEncodeData(const std::span<const float> &value) {\n    auto valueLength = static_cast<uint32_t>(value.size() * pbFloatSize());\n    auto size = pbRawVarint32Size(valueLength) + valueLength;\n    auto buffer = MMBuffer(size);\n    CodedOutputData output(buffer.getPtr(), size);\n    output.writeUInt32(valueLength);\n\n    for (auto single : value) {\n        output.writeFloat(single);\n    }\n    return buffer;\n}\n\nMMBuffer MiniPBCoder::getEncodeData(const std::span<const double> &value) {\n    auto valueLength = static_cast<uint32_t>(value.size() * pbDoubleSize());\n    auto size = pbRawVarint32Size(valueLength) + valueLength;\n    auto buffer = MMBuffer(size);\n    CodedOutputData output(buffer.getPtr(), size);\n    output.writeUInt32(valueLength);\n\n    for (auto single : value) {\n        output.writeDouble(single);\n    }\n    return buffer;\n}\n#endif // MMKV_HAS_CPP20\n\nvoid MiniPBCoder::decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {\n    MiniPBCoder oCoder(&oData);\n    oCoder.decodeOneMap(dic, position, false);\n}\n\nvoid MiniPBCoder::greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {\n    MiniPBCoder oCoder(&oData);\n    oCoder.decodeOneMap(dic, position, true);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nvoid MiniPBCoder::decodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {\n    MiniPBCoder oCoder(&oData, crypter);\n    oCoder.decodeOneMap(dic, position, false);\n}\n\nvoid MiniPBCoder::greedyDecodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {\n    MiniPBCoder oCoder(&oData, crypter);\n    oCoder.decodeOneMap(dic, position, true);\n}\n\n#endif\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/MiniPBCoder.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_MINIPBCODER_H\n#define MMKV_MINIPBCODER_H\n#ifdef __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include \"MMBuffer.h\"\n#include <cstdint>\n#ifdef MMKV_HAS_CPP20\n#  include <span>\n#  define MMKV_STRING_CONTAINER std::span<const std::string>\n#else\n#  define MMKV_STRING_CONTAINER std::vector<std::string>\n#endif\n\n\nnamespace mmkv {\n\nclass CodedInputData;\nclass CodedOutputData;\nclass AESCrypt;\nclass CodedInputDataCrypt;\nstruct PBEncodeItem;\n\nclass MMKV_EXPORT MiniPBCoder {\n    const MMBuffer *m_inputBuffer = nullptr;\n    CodedInputData *m_inputData = nullptr;\n    CodedInputDataCrypt *m_inputDataDecrpt = nullptr;\n\n    MMBuffer *m_outputBuffer = nullptr;\n    CodedOutputData *m_outputData = nullptr;\n    std::vector<PBEncodeItem> *m_encodeItems = nullptr;\n\n    MiniPBCoder();\n    explicit MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter = nullptr);\n    ~MiniPBCoder();\n\n    void writeRootObject();\n\n    size_t prepareObjectForEncode(const MMKVVector &vec);\n    size_t prepareObjectForEncode(const MMBuffer &buffer);\n\n    template <typename T>\n    MMBuffer getEncodeData(const T &obj) {\n        size_t index = prepareObjectForEncode(obj);\n        return writePreparedItems(index);\n    }\n\n    MMBuffer writePreparedItems(size_t index);\n\n    void decodeOneMap(MMKVMap &dic, size_t position, bool greedy);\n#ifndef MMKV_DISABLE_CRYPT\n    void decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy);\n#endif\n\n    size_t prepareObjectForEncode(const std::string &str);\n    size_t prepareObjectForEncode(const MMKV_STRING_CONTAINER &vector);\n    std::vector<std::string> decodeOneVector();\n#ifdef MMKV_HAS_CPP20\n    size_t prepareObjectForEncode(const std::span<const int32_t> &vec);\n    size_t prepareObjectForEncode(const std::span<const uint32_t> &vec);\n    size_t prepareObjectForEncode(const std::span<const int64_t> &vec);\n    size_t prepareObjectForEncode(const std::span<const uint64_t> &vec);\n\n    bool decodeOneVector(std::vector<bool> &result);\n    bool decodeOneVector(std::vector<int32_t> &result);\n    bool decodeOneVector(std::vector<uint32_t> &result);\n    bool decodeOneVector(std::vector<int64_t> &result);\n    bool decodeOneVector(std::vector<uint64_t> &result);\n    bool decodeOneVector(std::vector<float> &result);\n    bool decodeOneVector(std::vector<double> &result);\n\n    // special case for fixed size types\n    MMBuffer getEncodeData(const std::vector<bool> &obj);\n    MMBuffer getEncodeData(const std::span<const float> &obj);\n    MMBuffer getEncodeData(const std::span<const double> &obj);\n#endif // MMKV_HAS_CPP20\n\n#if defined(MMKV_APPLE) && defined(__OBJC__)\n    // NSString, NSData, NSDate\n    size_t prepareObjectForEncode(__unsafe_unretained NSObject *obj);\n#endif\n\npublic:\n    template <typename T>\n    static MMBuffer encodeDataWithObject(const T &obj) {\n        MiniPBCoder pbCoder;\n        return pbCoder.getEncodeData(obj);\n    }\n\n    // opt encoding a single MMBuffer\n    static MMBuffer encodeDataWithObject(const MMBuffer &obj);\n\n    // return empty result if there's any error\n    static void decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position = 0);\n\n    // decode as much data as possible before any error happens\n    static void greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position = 0);\n\n#ifndef MMKV_DISABLE_CRYPT\n    // return empty result if there's any error\n    static void decodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position = 0);\n\n    // decode as much data as possible before any error happens\n    static void greedyDecodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position = 0);\n#endif // MMKV_DISABLE_CRYPT\n\n    static std::vector<std::string> decodeVector(const MMBuffer &oData);\n\n    template <typename T>\n    static bool decodeVector(const MMBuffer &oData, std::vector<T> &result) {\n        MiniPBCoder oCoder(&oData);\n        return oCoder.decodeOneVector(result);\n    }\n#if defined(MMKV_APPLE) && defined(__OBJC__)\n    // NSString, NSData, NSDate\n    static NSObject *decodeObject(const MMBuffer &oData, Class cls);\n\n    static bool isCompatibleClass(Class cls);\n#endif\n\n    // just forbid it for possibly misuse\n    explicit MiniPBCoder(const MiniPBCoder &other) = delete;\n    MiniPBCoder &operator=(const MiniPBCoder &other) = delete;\n};\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_MINIPBCODER_H\n"
  },
  {
    "path": "Core/MiniPBCoder_OSX.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MiniPBCoder.h\"\n#include \"MMKVLog.h\"\n\n#ifdef MMKV_APPLE\n\n#    include \"CodedInputData.h\"\n#    include \"CodedInputDataCrypt.h\"\n#    include \"CodedOutputData.h\"\n#    include \"MMBuffer.h\"\n#    include \"PBEncodeItem.hpp\"\n#    include \"PBUtility.h\"\n#    include <string>\n#    include <vector>\n\n#    if __has_feature(objc_arc)\n#        error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#    endif\n\nusing namespace std;\n\nnamespace mmkv {\n\nsize_t MiniPBCoder::prepareObjectForEncode(__unsafe_unretained NSObject *obj) {\n    if (!obj) {\n        return m_encodeItems->size();\n    }\n    m_encodeItems->push_back(PBEncodeItem());\n    PBEncodeItem *encodeItem = &(m_encodeItems->back());\n    size_t index = m_encodeItems->size() - 1;\n\n    if ([obj isKindOfClass:[NSString class]]) {\n        NSString *str = (NSString *) obj;\n        encodeItem->type = PBEncodeItemType_NSString;\n        NSData *buffer = [[str dataUsingEncoding:NSUTF8StringEncoding] retain];\n        encodeItem->value.tmpObjectValue = (__bridge void *) buffer;\n        encodeItem->valueSize = static_cast<uint32_t>(buffer.length);\n    } else if ([obj isKindOfClass:[NSDate class]]) {\n        NSDate *oDate = (NSDate *) obj;\n        encodeItem->type = PBEncodeItemType_NSDate;\n        encodeItem->value.objectValue = (__bridge void *) oDate;\n        encodeItem->valueSize = pbDoubleSize();\n        encodeItem->compiledSize = encodeItem->valueSize;\n        return index; // double has fixed compilesize\n    } else if ([obj isKindOfClass:[NSData class]]) {\n        NSData *oData = (NSData *) obj;\n        encodeItem->type = PBEncodeItemType_NSData;\n        encodeItem->value.objectValue = (__bridge void *) oData;\n        encodeItem->valueSize = static_cast<uint32_t>(oData.length);\n    } else {\n        m_encodeItems->pop_back();\n        MMKVError(\"%@ not recognized\", NSStringFromClass(obj.class));\n        return m_encodeItems->size();\n    }\n    encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;\n\n    return index;\n}\n\nvoid MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t position, bool greedy) {\n    auto block = [position, this](MMKVMap &dictionary) {\n        if (position) {\n            m_inputData->seek(position);\n        } else {\n            m_inputData->readInt32();\n        }\n        while (!m_inputData->isAtEnd()) {\n            KeyValueHolder kvHolder;\n            const auto &key = m_inputData->readNSString(kvHolder);\n            if (key.length > 0) {\n                m_inputData->readData(kvHolder);\n                auto itr = dictionary.find(key);\n                if (itr != dictionary.end()) {\n                    if (kvHolder.valueSize > 0) {\n                        itr->second = std::move(kvHolder);\n                    } else {\n                        auto oldKey = itr->first;\n                        dictionary.erase(itr);\n                        [oldKey release];\n                    }\n                } else {\n                    if (kvHolder.valueSize > 0) {\n                        dictionary.emplace(key, std::move(kvHolder));\n                        [key retain];\n                    }\n                }\n            }\n        }\n    };\n\n    if (greedy) {\n        try {\n            block(dic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    } else {\n        try {\n            MMKVMap tmpDic;\n            block(tmpDic);\n            dic.swap(tmpDic);\n            for (auto &pair : tmpDic) {\n                [pair.first release];\n            }\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nvoid MiniPBCoder::decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy) {\n    auto block = [position, this](MMKVMapCrypt &dictionary) {\n        if (position) {\n            m_inputDataDecrpt->seek(position);\n        } else {\n            m_inputDataDecrpt->readInt32();\n        }\n        while (!m_inputDataDecrpt->isAtEnd()) {\n            KeyValueHolderCrypt kvHolder;\n            const auto &key = m_inputDataDecrpt->readNSString(kvHolder);\n            if (key.length > 0) {\n                m_inputDataDecrpt->readData(kvHolder);\n                auto itr = dictionary.find(key);\n                if (itr != dictionary.end()) {\n                    if (kvHolder.realValueSize() > 0) {\n                        itr->second = std::move(kvHolder);\n                    } else {\n                        auto oldKey = itr->first;\n                        dictionary.erase(itr);\n                        [oldKey release];\n                    }\n                } else {\n                    if (kvHolder.realValueSize() > 0) {\n                        dictionary.emplace(key, std::move(kvHolder));\n                        [key retain];\n                    }\n                }\n            }\n        }\n    };\n\n    if (greedy) {\n        try {\n            block(dic);\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    } else {\n        try {\n            MMKVMapCrypt tmpDic;\n            block(tmpDic);\n            dic.swap(tmpDic);\n            for (auto &pair : tmpDic) {\n                [pair.first release];\n            }\n        } catch (std::exception &exception) {\n            MMKVError(\"%s\", exception.what());\n        } catch (...) {\n            MMKVError(\"decode fail\");\n        }\n    }\n}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nNSObject *MiniPBCoder::decodeObject(const MMBuffer &oData, Class cls) {\n    if (!cls || oData.length() == 0) {\n        return nil;\n    }\n    CodedInputData input(oData.getPtr(), oData.length());\n    if (cls == [NSString class]) {\n        return input.readNSString();\n    } else if (cls == [NSMutableString class]) {\n        return [NSMutableString stringWithString:input.readNSString()];\n    } else if (cls == [NSData class]) {\n        return input.readNSData();\n    } else if (cls == [NSMutableData class]) {\n        return [NSMutableData dataWithData:input.readNSData()];\n    } else if (cls == [NSDate class]) {\n        return [NSDate dateWithTimeIntervalSince1970:input.readDouble()];\n    } else {\n        MMKVError(\"%@ not recognized\", NSStringFromClass(cls));\n    }\n\n    return nil;\n}\n\nbool MiniPBCoder::isCompatibleClass(Class cls) {\n    if (cls == [NSString class]) {\n        return true;\n    }\n    if (cls == [NSMutableString class]) {\n        return true;\n    }\n    if (cls == [NSData class]) {\n        return true;\n    }\n    if (cls == [NSMutableData class]) {\n        return true;\n    }\n    if (cls == [NSDate class]) {\n        return true;\n    }\n\n    return false;\n}\n\n} // namespace mmkv\n\n#endif // MMKV_APPLE\n"
  },
  {
    "path": "Core/PBEncodeItem.hpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_PBENCODEITEM_HPP\n#define MMKV_PBENCODEITEM_HPP\n#ifdef  __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include \"MMBuffer.h\"\n#include <cstdint>\n#include <memory.h>\n\nnamespace mmkv {\n\nenum PBEncodeItemType {\n    PBEncodeItemType_None,\n    PBEncodeItemType_Data,\n    PBEncodeItemType_Container,\n    PBEncodeItemType_String,\n#ifdef MMKV_HAS_CPP20\n    PBEncodeItemType_Int32,\n    PBEncodeItemType_UInt32,\n    PBEncodeItemType_Int64,\n    PBEncodeItemType_UInt64,\n//    PBEncodeItemType_Bool,\n//    PBEncodeItemType_Float,\n//    PBEncodeItemType_Double,\n#endif // MMKV_HAS_CPP20\n#ifdef MMKV_APPLE\n    PBEncodeItemType_NSString,\n    PBEncodeItemType_NSData,\n    PBEncodeItemType_NSDate,\n#endif\n};\n\nstruct PBEncodeItem {\n    PBEncodeItemType type;\n    uint32_t compiledSize;\n    uint32_t valueSize;\n    union {\n        const MMBuffer *bufferValue;\n#ifdef MMKV_HAS_CPP20\n//        bool boolValue;\n        int32_t int32Value;\n        int64_t int64Value;\n        uint32_t uint32Value;\n        uint64_t uint64Value;\n#endif // MMKV_HAS_CPP20\n        //        float floatValue;\n//        double doubleValue;\n        const std::string *strValue;\n#ifdef MMKV_APPLE\n        void *objectValue;\n        void *tmpObjectValue; // this object should be released on dealloc\n#endif\n    } value;\n\n    PBEncodeItem() : type(PBEncodeItemType_None), compiledSize(0), valueSize(0) { memset(&value, 0, sizeof(value)); }\n\n#ifndef MMKV_APPLE\n    // opt std::vector.push_back() on slow_path\n    PBEncodeItem(PBEncodeItem &&other) = default;\n#else\n    // opt std::vector.push_back() on slow_path\n    PBEncodeItem(PBEncodeItem &&other)\n        : type(other.type), compiledSize(other.compiledSize), valueSize(other.valueSize), value(other.value) {\n        // omit unnecessary CFRetain() & CFRelease()\n        other.type = PBEncodeItemType_None;\n    }\n\n    ~PBEncodeItem() {\n        if (type == PBEncodeItemType_NSString) {\n            if (value.tmpObjectValue) {\n                CFRelease(value.tmpObjectValue);\n            }\n        }\n    }\n#endif // MMKV_APPLE\n};\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_PBENCODEITEM_HPP\n"
  },
  {
    "path": "Core/PBUtility.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"MMBuffer.h\"\n#include \"PBUtility.h\"\n\nnamespace mmkv {\n\nuint32_t pbRawVarint32Size(uint32_t value) {\n    if ((value & (0xffffffff << 7)) == 0) {\n        return 1;\n    } else if ((value & (0xffffffff << 14)) == 0) {\n        return 2;\n    } else if ((value & (0xffffffff << 21)) == 0) {\n        return 3;\n    } else if ((value & (0xffffffff << 28)) == 0) {\n        return 4;\n    }\n    return 5;\n}\n\nuint32_t pbUInt64Size(uint64_t value) {\n    if ((value & (0xffffffffffffffffL << 7)) == 0) {\n        return 1;\n    } else if ((value & (0xffffffffffffffffL << 14)) == 0) {\n        return 2;\n    } else if ((value & (0xffffffffffffffffL << 21)) == 0) {\n        return 3;\n    } else if ((value & (0xffffffffffffffffL << 28)) == 0) {\n        return 4;\n    } else if ((value & (0xffffffffffffffffL << 35)) == 0) {\n        return 5;\n    } else if ((value & (0xffffffffffffffffL << 42)) == 0) {\n        return 6;\n    } else if ((value & (0xffffffffffffffffL << 49)) == 0) {\n        return 7;\n    } else if ((value & (0xffffffffffffffffL << 56)) == 0) {\n        return 8;\n    } else if ((value & (0xffffffffffffffffL << 63)) == 0) {\n        return 9;\n    }\n    return 10;\n}\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/PBUtility.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_PBUTILITY_H\n#define MMKV_PBUTILITY_H\n#ifdef  __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#include <cstdint>\n\nnamespace mmkv {\n\ntemplate <typename T, typename P>\nunion Converter {\n    static_assert(sizeof(T) == sizeof(P), \"size not match\");\n    T first;\n    P second;\n};\n\nstatic inline int64_t Float64ToInt64(double v) {\n    Converter<double, int64_t> converter;\n    converter.first = v;\n    return converter.second;\n}\n\nstatic inline int32_t Float32ToInt32(float v) {\n    Converter<float, int32_t> converter;\n    converter.first = v;\n    return converter.second;\n}\n\nstatic inline double Int64ToFloat64(int64_t v) {\n    Converter<double, int64_t> converter;\n    converter.second = v;\n    return converter.first;\n}\n\nstatic inline float Int32ToFloat32(int32_t v) {\n    Converter<float, int32_t> converter;\n    converter.second = v;\n    return converter.first;\n}\n\nstatic inline uint64_t Int64ToUInt64(int64_t v) {\n    Converter<int64_t, uint64_t> converter;\n    converter.first = v;\n    return converter.second;\n}\n\nstatic inline int64_t UInt64ToInt64(uint64_t v) {\n    Converter<int64_t, uint64_t> converter;\n    converter.second = v;\n    return converter.first;\n}\n\nstatic inline uint32_t Int32ToUInt32(int32_t v) {\n    Converter<int32_t, uint32_t> converter;\n    converter.first = v;\n    return converter.second;\n}\n\nstatic inline int32_t UInt32ToInt32(uint32_t v) {\n    Converter<int32_t, uint32_t> converter;\n    converter.second = v;\n    return converter.first;\n}\n\nstatic inline int32_t logicalRightShift32(int32_t value, uint32_t spaces) {\n    return UInt32ToInt32((Int32ToUInt32(value) >> spaces));\n}\n\nstatic inline int64_t logicalRightShift64(int64_t value, uint32_t spaces) {\n    return UInt64ToInt64((Int64ToUInt64(value) >> spaces));\n}\n\nconstexpr uint32_t LittleEdian32Size = 4;\n\nconstexpr uint32_t pbFloatSize() {\n    return LittleEdian32Size;\n}\n\nconstexpr uint32_t pbFixed32Size() {\n    return LittleEdian32Size;\n}\n\nconstexpr uint32_t LittleEdian64Size = 8;\n\nconstexpr uint32_t pbDoubleSize() {\n    return LittleEdian64Size;\n}\n\nconstexpr uint32_t pbBoolSize() {\n    return 1;\n}\n\nextern uint32_t pbRawVarint32Size(uint32_t value);\n\nstatic inline uint32_t pbRawVarint32Size(int32_t value) {\n    return pbRawVarint32Size(Int32ToUInt32(value));\n}\n\nextern uint32_t pbUInt64Size(uint64_t value);\n\nstatic inline uint32_t pbInt64Size(int64_t value) {\n    return pbUInt64Size(Int64ToUInt64(value));\n}\n\nstatic inline uint32_t pbInt32Size(int32_t value) {\n    if (value >= 0) {\n        return pbRawVarint32Size(value);\n    } else {\n        return 10;\n    }\n}\n\nstatic inline uint32_t pbUInt32Size(uint32_t value) {\n    return pbRawVarint32Size(value);\n}\n\nstatic inline uint32_t pbMMBufferSize(const MMBuffer &data) {\n    auto valueLength = static_cast<uint32_t>(data.length());\n    return valueLength + pbUInt32Size(valueLength);\n}\n\nconstexpr uint32_t Fixed32Size = pbFixed32Size();\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_PBUTILITY_H\n"
  },
  {
    "path": "Core/ScopedLock.hpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_SCOPEDLOCK_HPP\n#define MMKV_SCOPEDLOCK_HPP\n#ifdef __cplusplus\n\nnamespace mmkv {\n\ntemplate <typename T>\nclass ScopedLock {\n    T *m_lock;\n\n    void lock() {\n        if (m_lock) {\n            m_lock->lock();\n        }\n    }\n\n    void unlock() {\n        if (m_lock) {\n            m_lock->unlock();\n        }\n    }\n\npublic:\n    explicit ScopedLock(T *oLock) : m_lock(oLock) {\n        MMKV_ASSERT(m_lock);\n        lock();\n    }\n\n    ~ScopedLock() {\n        unlock();\n        m_lock = nullptr;\n    }\n\n    // just forbid it for possibly misuse\n    explicit ScopedLock(const ScopedLock<T> &other) = delete;\n    ScopedLock &operator=(const ScopedLock<T> &other) = delete;\n};\n\n} // namespace mmkv\n\n#include <type_traits>\n\n#define SCOPED_LOCK(lock) _SCOPEDLOCK(lock, __COUNTER__)\n#define _SCOPEDLOCK(lock, counter) __SCOPEDLOCK(lock, counter)\n#define __SCOPEDLOCK(lock, counter)                                                                                    \\\n    mmkv::ScopedLock<std::remove_pointer<decltype(lock)>::type> __scopedLock##counter(lock)\n\n#endif\n#endif //MMKV_SCOPEDLOCK_HPP\n"
  },
  {
    "path": "Core/ThreadLock.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ThreadLock.h\"\n#include \"MMKVLog.h\"\n\n#if MMKV_USING_PTHREAD\n\nusing namespace std;\n\nnamespace mmkv {\n\nThreadLock::ThreadLock() : m_lock({}) {\n    pthread_mutexattr_t attr;\n    pthread_mutexattr_init(&attr);\n    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\n\n    pthread_mutex_init(&m_lock, &attr);\n\n    pthread_mutexattr_destroy(&attr);\n}\n\nThreadLock::~ThreadLock() {\n    pthread_mutex_unlock(&m_lock);\n\n    pthread_mutex_destroy(&m_lock);\n}\n\nvoid ThreadLock::lock() {\n    auto ret = pthread_mutex_lock(&m_lock);\n    if (ret != 0) {\n        MMKVError(\"fail to lock %p, ret=%d, errno=%s\", &m_lock, ret, strerror(errno));\n    }\n}\n\nvoid ThreadLock::unlock() {\n    auto ret = pthread_mutex_unlock(&m_lock);\n    if (ret != 0) {\n        MMKVError(\"fail to unlock %p, ret=%d, errno=%s\", &m_lock, ret, strerror(errno));\n    }\n}\n\nbool ThreadLock::try_lock() {\n    auto ret = pthread_mutex_trylock(&m_lock);\n    return (ret == 0);\n}\n\nvoid ThreadLock::initialize() {\n    return;\n}\n\nvoid ThreadLock::ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)()) {\n    pthread_once(onceToken, callback);\n}\n\n} // namespace mmkv\n\n#endif // MMKV_USING_PTHREAD\n"
  },
  {
    "path": "Core/ThreadLock.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKV_THREADLOCK_H\n#define MMKV_THREADLOCK_H\n\n#ifdef  __cplusplus\n\n#include \"MMKVPredef.h\"\n\n#ifndef MMKV_WIN32\n#    include <pthread.h>\n#    define MMKV_USING_PTHREAD 1\n#endif\n\n#ifndef MMKV_USING_PTHREAD\n#    include <atomic>\n#endif\n\nnamespace mmkv {\n\n#if MMKV_USING_PTHREAD\n#    define ThreadOnceToken_t pthread_once_t\n#    define ThreadOnceUninitialized PTHREAD_ONCE_INIT\n#else\nenum ThreadOnceTokenEnum : int32_t { ThreadOnceUninitialized = 0, ThreadOnceInitializing, ThreadOnceInitialized };\nusing ThreadOnceToken_t = std::atomic<ThreadOnceTokenEnum>;\n#endif\n\nclass ThreadLock {\n#if MMKV_USING_PTHREAD\n    pthread_mutex_t m_lock;\n#else\n    CRITICAL_SECTION m_lock;\n#endif\n\npublic:\n    ThreadLock();\n    ~ThreadLock();\n\n    void initialize();\n\n    void lock();\n    void unlock();\n\n#ifndef MMKV_WIN32\n    bool try_lock();\n#endif\n\n    static void ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)(void));\n\n#ifdef MMKV_WIN32\n    static void Sleep(int ms);\n#endif\n\n    // just forbid it for possibly misuse\n    explicit ThreadLock(const ThreadLock &other) = delete;\n    ThreadLock &operator=(const ThreadLock &other) = delete;\n};\n\n} // namespace mmkv\n\n#endif\n#endif //MMKV_THREADLOCK_H\n"
  },
  {
    "path": "Core/ThreadLock_Win32.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ThreadLock.h\"\n\n#if !(MMKV_USING_PTHREAD)\n\n#    include \"MMKVLog.h\"\n#    include <atomic>\n#    include <cassert>\n\nnamespace mmkv {\n\nThreadLock::ThreadLock() : m_lock{0} {\n}\n\nThreadLock::~ThreadLock() {\n    DeleteCriticalSection(&m_lock);\n}\n\nvoid ThreadLock::initialize() {\n    // TODO: a better spin count?\n    if (!InitializeCriticalSectionAndSpinCount(&m_lock, 1024)) {\n        MMKVError(\"fail to init critical section:%d\", GetLastError());\n    }\n}\n\nvoid ThreadLock::lock() {\n    EnterCriticalSection(&m_lock);\n}\n\nvoid ThreadLock::unlock() {\n    LeaveCriticalSection(&m_lock);\n}\n\nvoid ThreadLock::ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)()) {\n    if (!onceToken || !callback) {\n        assert(onceToken);\n        assert(callback);\n        return;\n    }\n    while (true) {\n        auto expected = ThreadOnceUninitialized;\n        atomic_compare_exchange_weak(onceToken, &expected, ThreadOnceInitializing);\n        switch (expected) {\n            case ThreadOnceInitialized:\n                return;\n            case ThreadOnceUninitialized:\n                callback();\n                onceToken->store(ThreadOnceInitialized);\n                return;\n            case ThreadOnceInitializing: {\n                // another thread is initializing, let's wait for 1ms\n                ThreadLock::Sleep(1);\n                break;\n            }\n            default: {\n                MMKVError(\"should never happen:%d\", expected);\n                assert(0);\n                return;\n            }\n        }\n    }\n}\n\nvoid ThreadLock::Sleep(int ms) {\n    ::Sleep(ms);\n}\n\n} // namespace mmkv\n\n#endif // MMKV_USING_PTHREAD\n"
  },
  {
    "path": "Core/aes/AESCrypt.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"AESCrypt.h\"\n#include \"openssl/openssl_aes.h\"\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <ctime>\n#include \"../MMKVLog.h\"\n#include \"../MemoryFile.h\"\n\nnamespace mmkv {\n\n// assuming size in [1, 5]\nuint32_t AESCrypt::randomItemSizeHolder(uint32_t size) {\n    constexpr uint32_t ItemSizeHolders[] = {0, 0x80, 0x4000, 0x200000, 0x10000000, 0};\n    auto ItemSizeHolderMin = ItemSizeHolders[size - 1];\n    auto ItemSizeHolderMax = ItemSizeHolders[size] - 1;\n\n    srand((unsigned) time(nullptr));\n    auto result = static_cast<uint32_t>(rand());\n    result = result % (ItemSizeHolderMax - ItemSizeHolderMin + 1);\n    result += ItemSizeHolderMin;\n    return result;\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nusing namespace openssl;\n\nAESCrypt::AESCrypt(const void *key, size_t keyLength, const void *iv, size_t ivLength, bool aes256)\n    : m_isAES256(aes256) {\n    if (key && keyLength > 0) {\n        auto maxKeyLen = getMaxKeyLength();\n        memcpy(m_key, key, (keyLength > maxKeyLen) ? maxKeyLen : keyLength);\n\n        resetIV(iv, ivLength);\n\n        m_aesKey = new AES_KEY;\n        memset(m_aesKey, 0, sizeof(AES_KEY));\n        int ret = AES_set_encrypt_key(m_key, getMaxKeyBitLength(), m_aesKey);\n        MMKV_ASSERT(ret == 0);\n    }\n}\n\nAESCrypt::AESCrypt(const AESCrypt &other, const AESCryptStatus &status)\n    : m_isClone(true), m_isAES256(other.m_isAES256), m_number(status.m_number) {\n    //memcpy(m_key, other.m_key, sizeof(m_key));\n    memcpy(m_vector, status.m_vector, sizeof(m_vector));\n    m_aesKey = other.m_aesKey;\n}\n\nAESCrypt::~AESCrypt() {\n    if (!m_isClone) {\n        delete m_aesKey;\n        delete m_aesRollbackKey;\n    }\n}\n\nvoid AESCrypt::resetIV(const void *iv, size_t ivLength) {\n    m_number = 0;\n    if (iv && ivLength > 0) {\n        memcpy(m_vector, iv, (ivLength > AES_IV_LEN) ? AES_IV_LEN : ivLength);\n    } else {\n        memcpy(m_vector, m_key, AES_IV_LEN);\n    }\n}\n\nvoid AESCrypt::resetStatus(const AESCryptStatus &status) {\n    m_number = status.m_number;\n    memcpy(m_vector, status.m_vector, AES_IV_LEN);\n}\n\nvoid AESCrypt::getKey(void *output) const {\n    if (output) {\n        memcpy(output, m_key, getMaxKeyLength());\n    }\n}\n\nvoid AESCrypt::encrypt(const void *input, void *output, size_t length) {\n    if (!input || !output || length == 0) {\n        return;\n    }\n    AES_cfb128_encrypt((const uint8_t *) input, (uint8_t *) output, length, m_aesKey, m_vector, &m_number);\n}\n\nvoid AESCrypt::decrypt(const void *input, void *output, size_t length) {\n    if (!input || !output || length == 0) {\n        return;\n    }\n    AES_cfb128_decrypt((const uint8_t *) input, (uint8_t *) output, length, m_aesKey, m_vector, &m_number);\n}\n\nvoid AESCrypt::fillRandomIV(void *vector) {\n    if (!vector) {\n        return;\n    }\n    srand((unsigned) time(nullptr));\n    int *ptr = (int *) vector;\n    for (uint32_t i = 0; i < AES_IV_LEN / sizeof(int); i++) {\n        ptr[i] = rand();\n    }\n}\n\nstatic inline void\nRollback_cfb_decrypt(const uint8_t *input, const uint8_t *output, size_t len, AES_KEY *key, AESCryptStatus &status) {\n    auto ivec = status.m_vector;\n    auto n = status.m_number;\n\n    while (n && len) {\n        auto c = *(--output);\n        ivec[--n] = *(--input) ^ c;\n        len--;\n    }\n    if (n == 0 && (status.m_number != 0)) {\n        AES_decrypt(ivec, ivec, key);\n    }\n    while (len >= 16) {\n        len -= 16;\n        output -= 16;\n        input -= 16;\n        for (; n < 16; n += sizeof(size_t)) {\n            size_t t = *(size_t *) (output + n);\n            *(size_t *) (ivec + n) = *(size_t *) (input + n) ^ t;\n        }\n        n = 0;\n        AES_decrypt(ivec, ivec, key);\n    }\n    if (len) {\n        n = 16;\n        do {\n            auto c = *(--output);\n            ivec[--n] = *(--input) ^ c;\n            len--;\n        } while (len);\n    }\n\n    status.m_number = n;\n}\n\nvoid AESCrypt::statusBeforeDecrypt(const void *input, const void *output, size_t length, AESCryptStatus &status) {\n    if (length == 0) {\n        return;\n    }\n    if (!m_aesRollbackKey) {\n        m_aesRollbackKey = new AES_KEY;\n        memset(m_aesRollbackKey, 0, sizeof(AES_KEY));\n        int ret = AES_set_decrypt_key(m_key, getMaxKeyBitLength(), m_aesRollbackKey);\n        MMKV_ASSERT(ret == 0);\n    }\n    getCurStatus(status);\n    Rollback_cfb_decrypt((const uint8_t *) input, (const uint8_t *) output, length, m_aesRollbackKey, status);\n}\n\nvoid AESCrypt::getCurStatus(AESCryptStatus &status) {\n    status.m_number = static_cast<uint8_t>(m_number);\n    memcpy(status.m_vector, m_vector, sizeof(m_vector));\n}\n\nAESCrypt AESCrypt::cloneWithStatus(const AESCryptStatus &status) const {\n    return AESCrypt(*this, status);\n}\n\n#    ifdef MMKV_DEBUG\n\nvoid testRandomPlaceHolder() {\n    for (uint32_t size = 1; size < 6; size++) {\n        auto holder = AESCrypt::randomItemSizeHolder(size);\n        MMKVInfo(\"holder 0x%x for size %u\", holder, size);\n    }\n}\n\n// check if AESCrypt is encrypt-decrypt full-duplex\nvoid AESCrypt::testAESCrypt(const void *key, size_t keyLength, const uint8_t plainText[], size_t textLength) {\n    static bool hasIV = false;\n    static uint8_t iv[AES_IV_LEN];\n    if (!hasIV) {\n        hasIV = true;\n        srand((unsigned) time(nullptr));\n        for (uint32_t i = 0; i < AES_IV_LEN; i++) {\n            iv[i] = (uint8_t) rand();\n        }\n    }\n    auto aes256 = (keyLength > AES_KEY_LEN);\n    AESCrypt crypt1(key, keyLength, iv, sizeof(iv), aes256);\n    AESCrypt crypt2(key, keyLength, iv, sizeof(iv), aes256);\n\n    auto encryptText = new uint8_t[DEFAULT_MMAP_SIZE];\n    auto decryptText = new uint8_t[DEFAULT_MMAP_SIZE];\n    memset(encryptText, 0, DEFAULT_MMAP_SIZE);\n    memset(decryptText, 0, DEFAULT_MMAP_SIZE);\n\n    /* in-place encryption & decryption\n     memcpy(encryptText, plainText, textLength);\n     crypt1.encrypt(encryptText, encryptText, textLength);\n     crypt2.decrypt(encryptText, encryptText, textLength);\n     return;\n     */\n    AES_KEY decryptKey;\n    AES_set_decrypt_key(crypt1.m_key, crypt1.getMaxKeyBitLength(), &decryptKey);\n\n    size_t actualSize = 0;\n    bool flip = false;\n    for (const uint8_t *ptr = plainText; ptr < plainText + textLength;) {\n        auto tokenPtr = (const uint8_t *) strchr((const char *) ptr, ' ');\n        size_t size = 0;\n        if (!tokenPtr) {\n            size = static_cast<size_t>(plainText + textLength - ptr);\n        } else {\n            size = static_cast<size_t>(tokenPtr - ptr + 1);\n        }\n\n        AESCrypt *decrypter;\n        uint32_t oldNum;\n        uint8_t oldVector[sizeof(crypt1.m_vector)];\n\n        flip = !flip;\n        if (flip) {\n            crypt1.encrypt(plainText + actualSize, encryptText + actualSize, size);\n\n            decrypter = &crypt2;\n            oldNum = decrypter->m_number;\n            memcpy(oldVector, decrypter->m_vector, sizeof(oldVector));\n            crypt2.decrypt(encryptText + actualSize, decryptText + actualSize, size);\n        } else {\n            crypt2.encrypt(plainText + actualSize, encryptText + actualSize, size);\n\n            decrypter = &crypt1;\n            oldNum = decrypter->m_number;\n            memcpy(oldVector, decrypter->m_vector, sizeof(oldVector));\n            crypt1.decrypt(encryptText + actualSize, decryptText + actualSize, size);\n        }\n        // that's why AESCrypt can be full-duplex\n        assert(crypt1.m_number == crypt2.m_number);\n        assert(0 == memcmp(crypt1.m_vector, crypt2.m_vector, sizeof(crypt1.m_vector)));\n\n        // how rollback works\n        AESCryptStatus status;\n        decrypter->statusBeforeDecrypt(encryptText + actualSize + size, decryptText + actualSize + size, size, status);\n        assert(oldNum == status.m_number);\n        assert(0 == memcmp(oldVector, status.m_vector, sizeof(oldVector)));\n\n        actualSize += size;\n        ptr += size;\n    }\n    MMKVInfo(\"AES CFB decode: %s\", decryptText);\n    assert(memcmp(plainText, decryptText, textLength) == 0);\n\n    delete[] encryptText;\n    delete[] decryptText;\n}\n\nvoid AESCrypt::testAESCrypt() {\n    testRandomPlaceHolder();\n\n    {\n        const uint8_t plainText[] = \"Hello, OpenSSL-mmkv::AESCrypt::testAESCrypt() with AES CFB 128.\";\n        constexpr size_t textLength = sizeof(plainText) - 1;\n        const uint8_t key[] = \"TheAESKey\";\n        constexpr size_t keyLength = sizeof(key) - 1;\n        testAESCrypt(key, keyLength, plainText, textLength);\n    }\n    {\n        const uint8_t plainText256[] = \"Hello, OpenSSL-mmkv::AESCrypt::testAESCrypt() with AES CFB 256.\";\n        constexpr size_t textLength256 = sizeof(plainText256) - 1;\n        const uint8_t key256[] = \"TheVeryLooooooooongAESKey\";\n        constexpr size_t keyLength256 = sizeof(key256) - 1;\n        testAESCrypt(key256, keyLength256, plainText256, textLength256);\n    }\n}\n\n#    endif // MMKV_DEBUG\n#endif     // MMKV_DISABLE_CRYPT\n\n} // namespace mmkv\n"
  },
  {
    "path": "Core/aes/AESCrypt.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef AES_CRYPT_H_\n#define AES_CRYPT_H_\n#ifdef __cplusplus\n\n#include \"../MMKVPredef.h\"\n#include <cstddef>\n#include <cstdint>\n\n#ifdef MMKV_DISABLE_CRYPT\n\nnamespace mmkv {\nclass AESCrypt {\npublic:\n    static uint32_t randomItemSizeHolder(uint32_t size);\n};\n}\n\n#else\n\nnamespace openssl {\nstruct AES_KEY;\n}\n\nnamespace mmkv {\n\n#pragma pack(push, 1)\n\nstruct AESCryptStatus {\n    uint8_t m_number;\n    uint8_t m_vector[AES_IV_LEN];\n};\n\n#pragma pack(pop)\n\nclass CodedInputDataCrypt;\n\n// a AES CFB-128 encrypt-decrypt full-duplex wrapper\nclass AESCrypt {\n    bool m_isClone = false;\n    const bool m_isAES256 = false;\n    uint32_t m_number = 0;\n    openssl::AES_KEY *m_aesKey = nullptr;\n    openssl::AES_KEY *m_aesRollbackKey = nullptr;\n    uint8_t m_key[AES256_KEY_LEN] = {};\n\npublic:\n    uint8_t m_vector[AES_IV_LEN] = {};\n\nprivate:\n    // for cloneWithStatus()\n    AESCrypt(const AESCrypt &other, const AESCryptStatus &status);\n\npublic:\n    AESCrypt(const void *key, size_t keyLength, const void *iv = nullptr, size_t ivLength = 0, bool aes256 = false);\n    AESCrypt(AESCrypt &&other) = default;\n\n    ~AESCrypt();\n\n    void encrypt(const void *input, void *output, size_t length);\n\n    void decrypt(const void *input, void *output, size_t length);\n\n    void getCurStatus(AESCryptStatus &status);\n    void statusBeforeDecrypt(const void *input, const void *output, size_t length, AESCryptStatus &status);\n\n    AESCrypt cloneWithStatus(const AESCryptStatus &status) const;\n\n    void resetIV(const void *iv = nullptr, size_t ivLength = 0);\n    void resetStatus(const AESCryptStatus &status);\n\n    // output must have [AES_KEY_LEN/AES256_KEY_LEN] space\n    void getKey(void *output) const;\n\n    uint32_t getMaxKeyLength() const { return m_isAES256 ? AES256_KEY_LEN : AES_KEY_LEN; }\n    int getMaxKeyBitLength() const { return m_isAES256 ? AES256_KEY_BITSET_LEN : AES_KEY_BITSET_LEN; }\n\n    static void fillRandomIV(void *vector);\n    static uint32_t randomItemSizeHolder(uint32_t size);\n\n    // just forbid it for possibly misuse\n    explicit AESCrypt(const AESCrypt &other) = delete;\n    AESCrypt &operator=(const AESCrypt &other) = delete;\n\n    friend CodedInputDataCrypt;\n\n#ifdef MMKV_DEBUG\n    // check if AESCrypt is encrypt-decrypt full-duplex\n    static void testAESCrypt();\n    static void testAESCrypt(const void *key, size_t keyLength, const uint8_t plainText[], size_t textLength);\n#endif\n};\n\n} // namespace mmkv\n\n#endif // MMKV_DISABLE_CRYPT\n#endif // __cplusplus\n#endif /* AES_CRYPT_H_ */\n"
  },
  {
    "path": "Core/aes/openssl/openssl_aes.h",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#ifndef HEADER_AES_H\n# define HEADER_AES_H\n#ifdef  __cplusplus\n\n#include \"openssl_opensslconf.h\"\n#include \"openssl_arm_arch.h\"\n#include <cstdint>\n#include <stddef.h>\n#include \"../../MMKVPredef.h\"\n\n#ifndef MMKV_DISABLE_CRYPT\n\nnamespace openssl {\n\n/*\n * Because array size can't be a const in C, the following two are macros.\n * Both sizes are in bytes.\n */\n# define AES_MAXNR 14\n# define AES_BLOCK_SIZE 16\n\n/* This should be a hidden type, but EVP requires that the size be known */\nstruct AES_KEY {\n# ifdef AES_LONG\n    unsigned long rd_key[4 * (AES_MAXNR + 1)];\n# else\n    unsigned int rd_key[4 * (AES_MAXNR + 1)];\n# endif\n    int rounds;\n};\n\nvoid AES_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t length, const AES_KEY *key, uint8_t *ivec, uint32_t *num);\nvoid AES_cfb128_decrypt(const uint8_t *in, uint8_t *out, size_t length, const AES_KEY *key, uint8_t *ivec, uint32_t *num);\n\n} // namespace openssl\n\n#if __ARM_MAX_ARCH__ > 0\n\nextern \"C\" int openssl_aes_arm_set_encrypt_key(const uint8_t *userKey, const int bits, void *key);\nextern \"C\" int openssl_aes_arm_set_decrypt_key(const uint8_t *userKey, const int bits, void *key);\nextern \"C\" void openssl_aes_arm_encrypt(const uint8_t *in, uint8_t *out, const void *key);\nextern \"C\" void openssl_aes_arm_decrypt(const uint8_t *in, uint8_t *out, const void *key);\n\n#if !defined(__linux__) || defined(MMKV_OHOS)\n\n#define AES_set_encrypt_key(userKey, bits, key) openssl_aes_arm_set_encrypt_key(userKey, bits, key)\n#define AES_set_decrypt_key(userKey, bits, key) openssl_aes_arm_set_decrypt_key(userKey, bits, key)\n#define AES_encrypt(in, out, key) openssl_aes_arm_encrypt(in, out, key)\n#define AES_decrypt(in, out, key) openssl_aes_arm_decrypt(in, out, key)\n\n#else // __linux__ && !MMKV_OHOS\n\ntypedef int (*aes_set_encrypt_t)(const uint8_t *userKey, const int bits, void *key);\ntypedef int (*aes_set_decrypt_t)(const uint8_t *userKey, const int bits, void *key);\ntypedef void (*aes_encrypt_t)(const uint8_t *in, uint8_t *out, const void *key);\ntypedef void (*aes_decrypt_t)(const uint8_t *in, uint8_t *out, const void *key);\n\nnamespace openssl {\n\nint AES_C_set_encrypt_key(const uint8_t *userKey, const int bits, void *key);\nint AES_C_set_decrypt_key(const uint8_t *userKey, const int bits, void *key);\nvoid AES_C_encrypt(const uint8_t *in, uint8_t *out, const void *key);\nvoid AES_C_decrypt(const uint8_t *in, uint8_t *out, const void *key);\n\nextern aes_set_encrypt_t AES_set_encrypt_key;\nextern aes_set_decrypt_t AES_set_decrypt_key;\nextern aes_encrypt_t AES_encrypt;\nextern aes_decrypt_t AES_decrypt;\n\n} // namespace openssl\n\n#endif // __linux__ && !MMKV_OHOS\n\n#else // __ARM_MAX_ARCH__ <= 0\n\nnamespace openssl {\n\nint AES_set_encrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key);\nint AES_set_decrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key);\nvoid AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);\nvoid AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);\n\n} // namespace openssl\n\n#endif // __ARM_MAX_ARCH__ <= 0\n\n#endif // MMKV_DISABLE_CRYPT\n#endif // __cplusplus\n#endif // HEADER_AES_H\n"
  },
  {
    "path": "Core/aes/openssl/openssl_aes_core.cpp",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n/**\n * rijndael-alg-fst.c\n *\n * @version 3.0 (December 2000)\n *\n * Optimised ANSI C code for the Rijndael cipher (now AES)\n *\n * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>\n * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>\n * @author Paulo Barreto <paulo.barreto@terra.com.br>\n *\n * This code is hereby placed in the public domain.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS\n * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Note: rewritten a little bit to provide error control and an OpenSSL-\n   compatible API */\n\n#include <cassert>\n#include <cstdlib>\n#include \"openssl_aes.h\"\n#include \"openssl_aes_locl.h\"\n\n#ifndef MMKV_DISABLE_CRYPT\n\nnamespace openssl {\n\n#if (__ARM_MAX_ARCH__ > 0) && defined(__linux__) && !defined(MMKV_OHOS)\n\naes_set_encrypt_t AES_set_encrypt_key = openssl::AES_C_set_encrypt_key;\naes_set_decrypt_t AES_set_decrypt_key = openssl::AES_C_set_decrypt_key;\naes_encrypt_t AES_encrypt = openssl::AES_C_encrypt;\naes_encrypt_t AES_decrypt = openssl::AES_C_decrypt;\n\n#endif // (__ARM_MAX_ARCH__ > 0 && defined(__linux__) && !defined(MMKV_OHOS)\n\n#if (__ARM_MAX_ARCH__ <= 0) || (defined(__linux__) && !defined(MMKV_OHOS))\n\n/*-\nTe0[x] = S [x].[02, 01, 01, 03];\nTe1[x] = S [x].[03, 02, 01, 01];\nTe2[x] = S [x].[01, 03, 02, 01];\nTe3[x] = S [x].[01, 01, 03, 02];\n\nTd0[x] = Si[x].[0e, 09, 0d, 0b];\nTd1[x] = Si[x].[0b, 0e, 09, 0d];\nTd2[x] = Si[x].[0d, 0b, 0e, 09];\nTd3[x] = Si[x].[09, 0d, 0b, 0e];\nTd4[x] = Si[x].[01];\n*/\n\nstatic const u32 Te0[256] = {\n    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,\n    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,\n    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,\n    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,\n    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,\n    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,\n    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,\n    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,\n    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,\n    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,\n    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,\n    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,\n    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,\n    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,\n    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,\n    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,\n    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,\n    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,\n    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,\n    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,\n    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,\n    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,\n    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,\n    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,\n    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,\n    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,\n    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,\n    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,\n    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,\n    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,\n    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,\n    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,\n    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,\n    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,\n    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,\n    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,\n    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,\n    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,\n    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,\n    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,\n    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,\n    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,\n    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,\n    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,\n    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,\n    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,\n    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,\n    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,\n    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,\n    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,\n    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,\n    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,\n    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,\n    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,\n    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,\n    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,\n    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,\n    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,\n    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,\n    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,\n    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,\n    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,\n    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,\n    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,\n};\nstatic const u32 Te1[256] = {\n    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,\n    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,\n    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,\n    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,\n    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,\n    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,\n    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,\n    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,\n    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,\n    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,\n    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,\n    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,\n    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,\n    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,\n    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,\n    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,\n    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,\n    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,\n    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,\n    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,\n    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,\n    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,\n    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,\n    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,\n    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,\n    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,\n    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,\n    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,\n    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,\n    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,\n    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,\n    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,\n    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,\n    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,\n    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,\n    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,\n    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,\n    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,\n    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,\n    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,\n    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,\n    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,\n    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,\n    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,\n    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,\n    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,\n    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,\n    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,\n    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,\n    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,\n    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,\n    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,\n    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,\n    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,\n    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,\n    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,\n    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,\n    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,\n    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,\n    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,\n    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,\n    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,\n    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,\n    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,\n};\nstatic const u32 Te2[256] = {\n    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,\n    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,\n    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,\n    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,\n    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,\n    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,\n    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,\n    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,\n    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,\n    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,\n    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,\n    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,\n    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,\n    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,\n    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,\n    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,\n    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,\n    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,\n    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,\n    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,\n    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,\n    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,\n    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,\n    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,\n    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,\n    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,\n    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,\n    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,\n    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,\n    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,\n    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,\n    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,\n    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,\n    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,\n    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,\n    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,\n    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,\n    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,\n    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,\n    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,\n    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,\n    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,\n    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,\n    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,\n    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,\n    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,\n    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,\n    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,\n    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,\n    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,\n    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,\n    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,\n    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,\n    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,\n    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,\n    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,\n    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,\n    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,\n    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,\n    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,\n    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,\n    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,\n    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,\n    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,\n};\nstatic const u32 Te3[256] = {\n    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,\n    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,\n    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,\n    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,\n    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,\n    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,\n    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,\n    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,\n    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,\n    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,\n    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,\n    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,\n    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,\n    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,\n    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,\n    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,\n    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,\n    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,\n    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,\n    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,\n    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,\n    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,\n    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,\n    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,\n    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,\n    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,\n    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,\n    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,\n    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,\n    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,\n    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,\n    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,\n    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,\n    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,\n    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,\n    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,\n    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,\n    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,\n    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,\n    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,\n    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,\n    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,\n    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,\n    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,\n    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,\n    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,\n    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,\n    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,\n    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,\n    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,\n    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,\n    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,\n    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,\n    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,\n    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,\n    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,\n    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,\n    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,\n    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,\n    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,\n    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,\n    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,\n    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,\n    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,\n};\nstatic const u32 Td0[256] = {\n    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,\n    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,\n    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,\n    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,\n    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,\n    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,\n    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,\n    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,\n    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,\n    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,\n    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,\n    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,\n    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,\n    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,\n    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,\n    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,\n    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,\n    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,\n    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,\n    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,\n    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,\n    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,\n    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,\n    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,\n    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,\n    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,\n    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,\n    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,\n    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,\n    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,\n    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,\n    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,\n    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,\n    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,\n    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,\n    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,\n    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,\n    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,\n    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,\n    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,\n    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,\n    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,\n    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,\n    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,\n    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,\n    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,\n    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,\n    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,\n    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,\n    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,\n    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,\n    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,\n    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,\n    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,\n    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,\n    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,\n    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,\n    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,\n    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,\n    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,\n    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,\n    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,\n    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,\n    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,\n};\nstatic const u32 Td1[256] = {\n    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,\n    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,\n    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,\n    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,\n    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,\n    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,\n    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,\n    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,\n    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,\n    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,\n    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,\n    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,\n    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,\n    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,\n    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,\n    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,\n    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,\n    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,\n    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,\n    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,\n    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,\n    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,\n    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,\n    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,\n    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,\n    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,\n    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,\n    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,\n    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,\n    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,\n    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,\n    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,\n    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,\n    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,\n    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,\n    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,\n    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,\n    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,\n    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,\n    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,\n    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,\n    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,\n    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,\n    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,\n    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,\n    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,\n    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,\n    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,\n    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,\n    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,\n    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,\n    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,\n    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,\n    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,\n    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,\n    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,\n    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,\n    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,\n    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,\n    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,\n    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,\n    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,\n    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,\n    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,\n};\nstatic const u32 Td2[256] = {\n    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,\n    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,\n    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,\n    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,\n    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,\n    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,\n    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,\n    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,\n    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,\n    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,\n    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,\n    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,\n    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,\n    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,\n    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,\n    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,\n    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,\n    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,\n    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,\n    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,\n    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,\n    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,\n    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,\n    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,\n    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,\n    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,\n    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,\n    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,\n    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,\n    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,\n    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,\n    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,\n    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,\n    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,\n    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,\n    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,\n    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,\n    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,\n    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,\n    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,\n    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,\n    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,\n    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,\n    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,\n    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,\n    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,\n    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,\n    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,\n    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,\n    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,\n    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,\n    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,\n    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,\n    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,\n    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,\n    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,\n    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,\n    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,\n    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,\n    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,\n    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,\n    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,\n    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,\n    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,\n};\nstatic const u32 Td3[256] = {\n    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,\n    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,\n    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,\n    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,\n    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,\n    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,\n    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,\n    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,\n    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,\n    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,\n    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,\n    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,\n    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,\n    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,\n    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,\n    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,\n    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,\n    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,\n    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,\n    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,\n    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,\n    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,\n    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,\n    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,\n    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,\n    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,\n    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,\n    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,\n    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,\n    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,\n    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,\n    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,\n    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,\n    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,\n    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,\n    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,\n    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,\n    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,\n    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,\n    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,\n    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,\n    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,\n    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,\n    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,\n    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,\n    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,\n    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,\n    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,\n    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,\n    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,\n    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,\n    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,\n    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,\n    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,\n    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,\n    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,\n    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,\n    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,\n    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,\n    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,\n    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,\n    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,\n    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,\n    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,\n};\nstatic const u8 Td4[256] = {\n    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,\n    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,\n    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,\n    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,\n    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,\n    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,\n    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,\n    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,\n    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,\n    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,\n    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,\n    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,\n    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,\n    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,\n    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,\n    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,\n    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,\n    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,\n    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,\n    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,\n    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,\n    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,\n    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,\n    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,\n    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,\n    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,\n    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,\n    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,\n    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,\n    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,\n    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,\n    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,\n};\nstatic const u32 rcon[] = {\n    0x01000000, 0x02000000, 0x04000000, 0x08000000,\n    0x10000000, 0x20000000, 0x40000000, 0x80000000,\n    0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */\n};\n\n/**\n * Expand the cipher key into the encryption key schedule.\n */\n#if (__ARM_MAX_ARCH__ <= 0)\nint AES_set_encrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key) {\n#else\nint AES_C_set_encrypt_key(const uint8_t *userKey, const int bits, void *k) {\n    auto key = (AES_KEY*) k;\n#endif\n    u32 *rk;\n    int i = 0;\n    u32 temp;\n\n    if (!userKey || !key)\n        return -1;\n    if (bits != 128 && bits != 192 && bits != 256)\n        return -2;\n\n    rk = key->rd_key;\n\n    if (bits == 128)\n        key->rounds = 10;\n    else if (bits == 192)\n        key->rounds = 12;\n    else\n        key->rounds = 14;\n\n    rk[0] = GETU32(userKey     );\n    rk[1] = GETU32(userKey +  4);\n    rk[2] = GETU32(userKey +  8);\n    rk[3] = GETU32(userKey + 12);\n    if (bits == 128) {\n        while (1) {\n            temp  = rk[3];\n            rk[4] = rk[0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[5] = rk[1] ^ rk[4];\n            rk[6] = rk[2] ^ rk[5];\n            rk[7] = rk[3] ^ rk[6];\n            if (++i == 10) {\n                return 0;\n            }\n            rk += 4;\n        }\n    }\n    rk[4] = GETU32(userKey + 16);\n    rk[5] = GETU32(userKey + 20);\n    if (bits == 192) {\n        while (1) {\n            temp = rk[ 5];\n            rk[ 6] = rk[ 0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[ 7] = rk[ 1] ^ rk[ 6];\n            rk[ 8] = rk[ 2] ^ rk[ 7];\n            rk[ 9] = rk[ 3] ^ rk[ 8];\n            if (++i == 8) {\n                return 0;\n            }\n            rk[10] = rk[ 4] ^ rk[ 9];\n            rk[11] = rk[ 5] ^ rk[10];\n            rk += 6;\n        }\n    }\n    rk[6] = GETU32(userKey + 24);\n    rk[7] = GETU32(userKey + 28);\n    if (bits == 256) {\n        while (1) {\n            temp = rk[ 7];\n            rk[ 8] = rk[ 0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[ 9] = rk[ 1] ^ rk[ 8];\n            rk[10] = rk[ 2] ^ rk[ 9];\n            rk[11] = rk[ 3] ^ rk[10];\n            if (++i == 7) {\n                return 0;\n            }\n            temp = rk[11];\n            rk[12] = rk[ 4] ^\n                (Te2[(temp >> 24)       ] & 0xff000000) ^\n                (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp >>  8) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp      ) & 0xff] & 0x000000ff);\n            rk[13] = rk[ 5] ^ rk[12];\n            rk[14] = rk[ 6] ^ rk[13];\n            rk[15] = rk[ 7] ^ rk[14];\n\n            rk += 8;\n            }\n    }\n    return 0;\n}\n\n/**\n * Expand the cipher key into the decryption key schedule.\n */\n#if (__ARM_MAX_ARCH__ <= 0)\nint AES_set_decrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key) {\n#else\nint AES_C_set_decrypt_key(const uint8_t *userKey, const int bits, void *k) {\n    auto key = (AES_KEY*) k;\n#endif\n    u32 *rk;\n    int i, j, status;\n    u32 temp;\n\n    /* first, start with an encryption schedule */\n    status = AES_set_encrypt_key(userKey, bits, key);\n    if (status < 0)\n        return status;\n\n    rk = key->rd_key;\n\n    /* invert the order of the round keys: */\n    for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {\n        temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;\n        temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;\n        temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;\n        temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;\n    }\n    /* apply the inverse MixColumn transform to all round keys but the first and the last: */\n    for (i = 1; i < (key->rounds); i++) {\n        rk += 4;\n        rk[0] =\n            Td0[Te1[(rk[0] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[0] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[0]      ) & 0xff] & 0xff];\n        rk[1] =\n            Td0[Te1[(rk[1] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[1] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[1]      ) & 0xff] & 0xff];\n        rk[2] =\n            Td0[Te1[(rk[2] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[2] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[2]      ) & 0xff] & 0xff];\n        rk[3] =\n            Td0[Te1[(rk[3] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[3] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[3]      ) & 0xff] & 0xff];\n    }\n    return 0;\n}\n\n\n/*\n * Encrypt a single block\n * in and out can overlap\n */\n#if (__ARM_MAX_ARCH__ <= 0)\nvoid AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {\n#else\nvoid AES_C_encrypt(const uint8_t *in, uint8_t *out, const void *k) {\n    auto key = (const AES_KEY*) k;\n#endif\n    const u32 *rk;\n    u32 s0, s1, s2, s3, t0, t1, t2, t3;\n    int r;\n\n    assert(in && out && key);\n    rk = key->rd_key;\n\n    /*\n     * map byte array block to cipher state\n     * and add initial round key:\n     */\n    s0 = GETU32(in     ) ^ rk[0];\n    s1 = GETU32(in +  4) ^ rk[1];\n    s2 = GETU32(in +  8) ^ rk[2];\n    s3 = GETU32(in + 12) ^ rk[3];\n\n    /*\n     * Nr - 1 full rounds:\n     */\n    r = key->rounds >> 1;\n    for (;;) {\n        t0 =\n            Te0[(s0 >> 24)       ] ^\n            Te1[(s1 >> 16) & 0xff] ^\n            Te2[(s2 >>  8) & 0xff] ^\n            Te3[(s3      ) & 0xff] ^\n            rk[4];\n        t1 =\n            Te0[(s1 >> 24)       ] ^\n            Te1[(s2 >> 16) & 0xff] ^\n            Te2[(s3 >>  8) & 0xff] ^\n            Te3[(s0      ) & 0xff] ^\n            rk[5];\n        t2 =\n            Te0[(s2 >> 24)       ] ^\n            Te1[(s3 >> 16) & 0xff] ^\n            Te2[(s0 >>  8) & 0xff] ^\n            Te3[(s1      ) & 0xff] ^\n            rk[6];\n        t3 =\n            Te0[(s3 >> 24)       ] ^\n            Te1[(s0 >> 16) & 0xff] ^\n            Te2[(s1 >>  8) & 0xff] ^\n            Te3[(s2      ) & 0xff] ^\n            rk[7];\n\n        rk += 8;\n        if (--r == 0) {\n            break;\n        }\n\n        s0 =\n            Te0[(t0 >> 24)       ] ^\n            Te1[(t1 >> 16) & 0xff] ^\n            Te2[(t2 >>  8) & 0xff] ^\n            Te3[(t3      ) & 0xff] ^\n            rk[0];\n        s1 =\n            Te0[(t1 >> 24)       ] ^\n            Te1[(t2 >> 16) & 0xff] ^\n            Te2[(t3 >>  8) & 0xff] ^\n            Te3[(t0      ) & 0xff] ^\n            rk[1];\n        s2 =\n            Te0[(t2 >> 24)       ] ^\n            Te1[(t3 >> 16) & 0xff] ^\n            Te2[(t0 >>  8) & 0xff] ^\n            Te3[(t1      ) & 0xff] ^\n            rk[2];\n        s3 =\n            Te0[(t3 >> 24)       ] ^\n            Te1[(t0 >> 16) & 0xff] ^\n            Te2[(t1 >>  8) & 0xff] ^\n            Te3[(t2      ) & 0xff] ^\n            rk[3];\n    }\n    /*\n     * apply last round and\n     * map cipher state to byte array block:\n     */\n    s0 =\n        (Te2[(t0 >> 24)       ] & 0xff000000) ^\n        (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t2 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t3      ) & 0xff] & 0x000000ff) ^\n        rk[0];\n    PUTU32(out     , s0);\n    s1 =\n        (Te2[(t1 >> 24)       ] & 0xff000000) ^\n        (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t3 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t0      ) & 0xff] & 0x000000ff) ^\n        rk[1];\n    PUTU32(out +  4, s1);\n    s2 =\n        (Te2[(t2 >> 24)       ] & 0xff000000) ^\n        (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t0 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t1      ) & 0xff] & 0x000000ff) ^\n        rk[2];\n    PUTU32(out +  8, s2);\n    s3 =\n        (Te2[(t3 >> 24)       ] & 0xff000000) ^\n        (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t1 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t2      ) & 0xff] & 0x000000ff) ^\n        rk[3];\n    PUTU32(out + 12, s3);\n}\n\n/*\n * Decrypt a single block\n * in and out can overlap\n */\n#if (__ARM_MAX_ARCH__ <= 0)\nvoid AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {\n#else\nvoid AES_C_decrypt(const uint8_t *in, uint8_t *out, const void *k) {\n    auto key = (AES_KEY*) k;\n#endif\n    const u32 *rk;\n    u32 s0, s1, s2, s3, t0, t1, t2, t3;\n    int r;\n\n    assert(in && out && key);\n    rk = key->rd_key;\n\n    /*\n     * map byte array block to cipher state\n     * and add initial round key:\n     */\n    s0 = GETU32(in     ) ^ rk[0];\n    s1 = GETU32(in +  4) ^ rk[1];\n    s2 = GETU32(in +  8) ^ rk[2];\n    s3 = GETU32(in + 12) ^ rk[3];\n\n    /*\n     * Nr - 1 full rounds:\n     */\n    r = key->rounds >> 1;\n    for (;;) {\n        t0 =\n            Td0[(s0 >> 24)       ] ^\n            Td1[(s3 >> 16) & 0xff] ^\n            Td2[(s2 >>  8) & 0xff] ^\n            Td3[(s1      ) & 0xff] ^\n            rk[4];\n        t1 =\n            Td0[(s1 >> 24)       ] ^\n            Td1[(s0 >> 16) & 0xff] ^\n            Td2[(s3 >>  8) & 0xff] ^\n            Td3[(s2      ) & 0xff] ^\n            rk[5];\n        t2 =\n            Td0[(s2 >> 24)       ] ^\n            Td1[(s1 >> 16) & 0xff] ^\n            Td2[(s0 >>  8) & 0xff] ^\n            Td3[(s3      ) & 0xff] ^\n            rk[6];\n        t3 =\n            Td0[(s3 >> 24)       ] ^\n            Td1[(s2 >> 16) & 0xff] ^\n            Td2[(s1 >>  8) & 0xff] ^\n            Td3[(s0      ) & 0xff] ^\n            rk[7];\n\n        rk += 8;\n        if (--r == 0) {\n            break;\n        }\n\n        s0 =\n            Td0[(t0 >> 24)       ] ^\n            Td1[(t3 >> 16) & 0xff] ^\n            Td2[(t2 >>  8) & 0xff] ^\n            Td3[(t1      ) & 0xff] ^\n            rk[0];\n        s1 =\n            Td0[(t1 >> 24)       ] ^\n            Td1[(t0 >> 16) & 0xff] ^\n            Td2[(t3 >>  8) & 0xff] ^\n            Td3[(t2      ) & 0xff] ^\n            rk[1];\n        s2 =\n            Td0[(t2 >> 24)       ] ^\n            Td1[(t1 >> 16) & 0xff] ^\n            Td2[(t0 >>  8) & 0xff] ^\n            Td3[(t3      ) & 0xff] ^\n            rk[2];\n        s3 =\n            Td0[(t3 >> 24)       ] ^\n            Td1[(t2 >> 16) & 0xff] ^\n            Td2[(t1 >>  8) & 0xff] ^\n            Td3[(t0      ) & 0xff] ^\n            rk[3];\n    }\n\n    /*\n     * apply last round and\n     * map cipher state to byte array block:\n     */\n    s0 =\n        ((u32)Td4[(t0 >> 24)       ] << 24) ^\n        ((u32)Td4[(t3 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t2 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t1      ) & 0xff])       ^\n        rk[0];\n    PUTU32(out     , s0);\n    s1 =\n        ((u32)Td4[(t1 >> 24)       ] << 24) ^\n        ((u32)Td4[(t0 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t3 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t2      ) & 0xff])       ^\n        rk[1];\n    PUTU32(out +  4, s1);\n    s2 =\n        ((u32)Td4[(t2 >> 24)       ] << 24) ^\n        ((u32)Td4[(t1 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t0 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t3      ) & 0xff])       ^\n        rk[2];\n    PUTU32(out +  8, s2);\n    s3 =\n        ((u32)Td4[(t3 >> 24)       ] << 24) ^\n        ((u32)Td4[(t2 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t1 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t0      ) & 0xff])       ^\n        rk[3];\n    PUTU32(out + 12, s3);\n}\n\n#endif // (__ARM_MAX_ARCH__ <= 0) || (defined(__linux__) && !defined(MMKV_OHOS))\n\n} // namespace openssl\n\n#endif // MMKV_DISABLE_CRYPT\n"
  },
  {
    "path": "Core/aes/openssl/openssl_aes_locl.h",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#ifndef HEADER_AES_LOCL_H\n# define HEADER_AES_LOCL_H\n#ifdef  __cplusplus\n\n# include \"openssl_opensslconf.h\"\n# include <stdio.h>\n# include <stdlib.h>\n# include <string.h>\n# include <stdint.h>\n\n# if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))\n#  define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)\n#  define GETU32(p) SWAP(*((u32 *)(p)))\n#  define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }\n# else\n#  define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))\n#  define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }\n# endif\n\n# ifdef AES_LONG\ntypedef unsigned long u32;\n# else\ntypedef unsigned int u32;\n# endif\ntypedef unsigned short u16;\ntypedef unsigned char u8;\n\n#endif\n#endif                          /* !HEADER_AES_LOCL_H */\n"
  },
  {
    "path": "Core/aes/openssl/openssl_aesv8-armx.S",
    "content": "#include \"openssl_arm_arch.h\"\n#include \"../../MMKVPredef.h\"\n\n#if (__ARM_MAX_ARCH__ > 7) && !defined(MMKV_DISABLE_CRYPT)\n\n.text\n\n.align\t5\nLrcon:\n.long\t0x01,0x01,0x01,0x01\n.long\t0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d\t// rotate-n-splat\n.long\t0x1b,0x1b,0x1b,0x1b\n\n#ifndef __linux__\n.globl\t_openssl_aes_arm_set_encrypt_key\n\n.align\t5\n_openssl_aes_arm_set_encrypt_key:\n#else // __linux__\n.globl\topenssl_aes_arm_set_encrypt_key\n\n.align\t5\nopenssl_aes_arm_set_encrypt_key:\n#endif // __linux__\n\nLenc_key:\n\tstp\tx29,x30,[sp,#-16]!\n\tadd\tx29,sp,#0\n\tmov\tx3,#-1\n\tcmp\tx0,#0\n\tb.eq\tLenc_key_abort\n\tcmp\tx2,#0\n\tb.eq\tLenc_key_abort\n\tmov\tx3,#-2\n\tcmp\tw1,#128\n\tb.lt\tLenc_key_abort\n\tcmp\tw1,#256\n\tb.gt\tLenc_key_abort\n\ttst\tw1,#0x3f\n\tb.ne\tLenc_key_abort\n\n\tadr\tx3,Lrcon\n\tcmp\tw1,#192\n\n\teor\tv0.16b,v0.16b,v0.16b\n\tld1\t{v3.16b},[x0],#16\n\tmov\tw1,#8\t\t// reuse w1\n\tld1\t{v1.4s,v2.4s},[x3],#32\n\n\tb.lt\tLoop128\n\tb.eq\tL192\n\tb\tL256\n\n.align\t4\nLoop128:\n\ttbl\tv6.16b,{v3.16b},v2.16b\n\text\tv5.16b,v0.16b,v3.16b,#12\n\tst1\t{v3.4s},[x2],#16\n\taese\tv6.16b,v0.16b\n\tsubs\tw1,w1,#1\n\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv6.16b,v6.16b,v1.16b\n\teor\tv3.16b,v3.16b,v5.16b\n\tshl\tv1.16b,v1.16b,#1\n\teor\tv3.16b,v3.16b,v6.16b\n\tb.ne\tLoop128\n\n\tld1\t{v1.4s},[x3]\n\n\ttbl\tv6.16b,{v3.16b},v2.16b\n\text\tv5.16b,v0.16b,v3.16b,#12\n\tst1\t{v3.4s},[x2],#16\n\taese\tv6.16b,v0.16b\n\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv6.16b,v6.16b,v1.16b\n\teor\tv3.16b,v3.16b,v5.16b\n\tshl\tv1.16b,v1.16b,#1\n\teor\tv3.16b,v3.16b,v6.16b\n\n\ttbl\tv6.16b,{v3.16b},v2.16b\n\text\tv5.16b,v0.16b,v3.16b,#12\n\tst1\t{v3.4s},[x2],#16\n\taese\tv6.16b,v0.16b\n\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv6.16b,v6.16b,v1.16b\n\teor\tv3.16b,v3.16b,v5.16b\n\teor\tv3.16b,v3.16b,v6.16b\n\tst1\t{v3.4s},[x2]\n\tadd\tx2,x2,#0x50\n\n\tmov\tw12,#10\n\tb\tLdone\n\n.align\t4\nL192:\n\tld1\t{v4.8b},[x0],#8\n\tmovi\tv6.16b,#8\t\t\t// borrow v6.16b\n\tst1\t{v3.4s},[x2],#16\n\tsub\tv2.16b,v2.16b,v6.16b\t// adjust the mask\n\nLoop192:\n\ttbl\tv6.16b,{v4.16b},v2.16b\n\text\tv5.16b,v0.16b,v3.16b,#12\n\tst1\t{v4.8b},[x2],#8\n\taese\tv6.16b,v0.16b\n\tsubs\tw1,w1,#1\n\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\n\tdup\tv5.4s,v3.s[3]\n\teor\tv5.16b,v5.16b,v4.16b\n\teor\tv6.16b,v6.16b,v1.16b\n\text\tv4.16b,v0.16b,v4.16b,#12\n\tshl\tv1.16b,v1.16b,#1\n\teor\tv4.16b,v4.16b,v5.16b\n\teor\tv3.16b,v3.16b,v6.16b\n\teor\tv4.16b,v4.16b,v6.16b\n\tst1\t{v3.4s},[x2],#16\n\tb.ne\tLoop192\n\n\tmov\tw12,#12\n\tadd\tx2,x2,#0x20\n\tb\tLdone\n\n.align\t4\nL256:\n\tld1\t{v4.16b},[x0]\n\tmov\tw1,#7\n\tmov\tw12,#14\n\tst1\t{v3.4s},[x2],#16\n\nLoop256:\n\ttbl\tv6.16b,{v4.16b},v2.16b\n\text\tv5.16b,v0.16b,v3.16b,#12\n\tst1\t{v4.4s},[x2],#16\n\taese\tv6.16b,v0.16b\n\tsubs\tw1,w1,#1\n\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv3.16b,v3.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv6.16b,v6.16b,v1.16b\n\teor\tv3.16b,v3.16b,v5.16b\n\tshl\tv1.16b,v1.16b,#1\n\teor\tv3.16b,v3.16b,v6.16b\n\tst1\t{v3.4s},[x2],#16\n\tb.eq\tLdone\n\n\tdup\tv6.4s,v3.s[3]\t\t// just splat\n\text\tv5.16b,v0.16b,v4.16b,#12\n\taese\tv6.16b,v0.16b\n\n\teor\tv4.16b,v4.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv4.16b,v4.16b,v5.16b\n\text\tv5.16b,v0.16b,v5.16b,#12\n\teor\tv4.16b,v4.16b,v5.16b\n\n\teor\tv4.16b,v4.16b,v6.16b\n\tb\tLoop256\n\nLdone:\n\tstr\tw12,[x2]\n\tmov\tx3,#0\n\nLenc_key_abort:\n\tmov\tx0,x3\t\t\t// return value\n\tldr\tx29,[sp],#16\n\tret\n\n#ifndef __linux__\n.globl    _openssl_aes_arm_set_decrypt_key\n\n.align    5\n_openssl_aes_arm_set_decrypt_key:\n#else // __linux__\n.globl    openssl_aes_arm_set_decrypt_key\n\n.align    5\nopenssl_aes_arm_set_decrypt_key:\n#endif // __linux__\n    stp    x29,x30,[sp,#-16]!\n    add    x29,sp,#0\n    bl    Lenc_key\n\n    cmp    x0,#0\n    b.ne    Ldec_key_abort\n\n    sub    x2,x2,#240        // restore original x2\n    mov    x4,#-16\n    add    x0,x2,x12,lsl#4    // end of key schedule\n\n    ld1    {v0.4s},[x2]\n    ld1    {v1.4s},[x0]\n    st1    {v0.4s},[x0],x4\n    st1    {v1.4s},[x2],#16\n\nLoop_imc:\n    ld1    {v0.4s},[x2]\n    ld1    {v1.4s},[x0]\n    aesimc    v0.16b,v0.16b\n    aesimc    v1.16b,v1.16b\n    st1    {v0.4s},[x0],x4\n    st1    {v1.4s},[x2],#16\n    cmp    x0,x2\n    b.hi    Loop_imc\n\n    ld1    {v0.4s},[x2]\n    aesimc    v0.16b,v0.16b\n    st1    {v0.4s},[x0]\n\n    eor    x0,x0,x0        // return value\nLdec_key_abort:\n    ldp    x29,x30,[sp],#16\n    ret\n\n#ifndef __linux__\n.globl\t_openssl_aes_arm_encrypt\n\n.align\t5\n_openssl_aes_arm_encrypt:\n#else // __linux__\n.globl\topenssl_aes_arm_encrypt\n\n.align\t5\nopenssl_aes_arm_encrypt:\n#endif // __linux__\n\n\tldr\tw3,[x2,#240]\n\tld1\t{v0.4s},[x2],#16\n\tld1\t{v2.16b},[x0]\n\tsub\tw3,w3,#2\n\tld1\t{v1.4s},[x2],#16\n\nLoop_enc:\n\taese\tv2.16b,v0.16b\n\taesmc\tv2.16b,v2.16b\n\tld1\t{v0.4s},[x2],#16\n\tsubs\tw3,w3,#2\n\taese\tv2.16b,v1.16b\n\taesmc\tv2.16b,v2.16b\n\tld1\t{v1.4s},[x2],#16\n\tb.gt\tLoop_enc\n\n\taese\tv2.16b,v0.16b\n\taesmc\tv2.16b,v2.16b\n\tld1\t{v0.4s},[x2]\n\taese\tv2.16b,v1.16b\n\teor\tv2.16b,v2.16b,v0.16b\n\n\tst1\t{v2.16b},[x1]\n\tret\n\n#ifndef __linux__\n.globl    _openssl_aes_arm_decrypt\n\n.align    5\n_openssl_aes_arm_decrypt:\n#else  // __linux__\n.globl    openssl_aes_arm_decrypt\n\n.align    5\nopenssl_aes_arm_decrypt:\n#endif  // __linux__\n\n    ldr    w3,[x2,#240]\n    ld1    {v0.4s},[x2],#16\n    ld1    {v2.16b},[x0]\n    sub    w3,w3,#2\n    ld1    {v1.4s},[x2],#16\n\nLoop_dec:\n    aesd    v2.16b,v0.16b\n    aesimc    v2.16b,v2.16b\n    ld1    {v0.4s},[x2],#16\n    subs    w3,w3,#2\n    aesd    v2.16b,v1.16b\n    aesimc    v2.16b,v2.16b\n    ld1    {v1.4s},[x2],#16\n    b.gt    Loop_dec\n\n    aesd    v2.16b,v0.16b\n    aesimc    v2.16b,v2.16b\n    ld1    {v0.4s},[x2]\n    aesd    v2.16b,v1.16b\n    eor    v2.16b,v2.16b,v0.16b\n\n    st1    {v2.16b},[x1]\n    ret\n\n#endif\n"
  },
  {
    "path": "Core/aes/openssl/openssl_arm_arch.h",
    "content": "/*\n * Copyright 2011-2018 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#ifndef __ARM_ARCH_H__\n# define __ARM_ARCH_H__\n\n# if !defined(__ARM_ARCH__)\n#  if defined(__CC_ARM)\n#   define __ARM_ARCH__ __TARGET_ARCH_ARM\n#   if defined(__BIG_ENDIAN)\n#    define __ARMEB__\n#   else\n#    define __ARMEL__\n#   endif\n#  elif defined(__GNUC__)\n#   if   defined(__aarch64__)\n#    define __ARM_ARCH__ 8\n#    if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__\n#     define __ARMEB__\n#    else\n#     define __ARMEL__\n#    endif\n  /*\n   * Why doesn't gcc define __ARM_ARCH__? Instead it defines\n   * bunch of below macros. See all_architectires[] table in\n   * gcc/config/arm/arm.c. On a side note it defines\n   * __ARMEL__/__ARMEB__ for little-/big-endian.\n   */\n#   elif defined(__ARM_ARCH)\n#    define __ARM_ARCH__ __ARM_ARCH\n#   elif defined(__ARM_ARCH_8A__)\n#    define __ARM_ARCH__ 8\n#   elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__)     || \\\n        defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__)     || \\\n        defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7K__)\n#    define __ARM_ARCH__ 7\n#   elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__)     || \\\n        defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__)     || \\\n        defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__)    || \\\n        defined(__ARM_ARCH_6T2__)\n#    define __ARM_ARCH__ 6\n#   elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__)     || \\\n        defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__)    || \\\n        defined(__ARM_ARCH_5TEJ__)\n#    define __ARM_ARCH__ 5\n#   elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)\n#    define __ARM_ARCH__ 4\n#   else\n//#    error \"unsupported ARM architecture\"\n#    define __ARM_ARCH__ 0\n#   endif\n#  endif\n# endif\n\n# if !defined(__ARM_MAX_ARCH__)\n#  define __ARM_MAX_ARCH__ __ARM_ARCH__\n# endif\n\n# if __ARM_MAX_ARCH__<__ARM_ARCH__\n#  error \"__ARM_MAX_ARCH__ can't be less than __ARM_ARCH__\"\n# elif __ARM_MAX_ARCH__!=__ARM_ARCH__\n#  if __ARM_ARCH__<7 && __ARM_MAX_ARCH__>=7 && defined(__ARMEB__)\n#   error \"can't build universal big-endian binary\"\n#  endif\n# endif\n\n# ifndef __ASSEMBLER__\nextern unsigned int OPENSSL_armcap_P;\n# endif\n\n# define ARMV7_NEON      (1<<0)\n# define ARMV7_TICK      (1<<1)\n# define ARMV8_AES       (1<<2)\n# define ARMV8_SHA1      (1<<3)\n# define ARMV8_SHA256    (1<<4)\n# define ARMV8_PMULL     (1<<5)\n\n#endif\n"
  },
  {
    "path": "Core/aes/openssl/openssl_cfb128.cpp",
    "content": "/*\n * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#include \"openssl_aes.h\"\n#include \"../../MMKVPredef.h\"\n#include <cstring>\n\n#ifndef  MMKV_DISABLE_CRYPT\n\nnamespace openssl {\n\n/*\n * The input and output encrypted as though 128bit cfb mode is being used.\n * The extra state information to record how much of the 128bit block we have\n * used is contained in *num;\n */\nvoid AES_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len, const AES_KEY *key, uint8_t ivec[16], uint32_t *num)\n{\n    auto n = *num;\n\n    while (n && len) {\n        *(out++) = ivec[n] ^= *(in++);\n        --len;\n        n = (n + 1) % 16;\n    }\n    while (len >= 16) {\n        AES_encrypt(ivec, ivec, key);\n        for (; n < 16; n += sizeof(size_t)) {\n            *(size_t *)(out + n) =\n                *(size_t *)(ivec + n) ^= *(size_t *)(in + n);\n        }\n        len -= 16;\n        out += 16;\n        in += 16;\n        n = 0;\n    }\n    if (len) {\n        AES_encrypt(ivec, ivec, key);\n        while (len--) {\n            out[n] = ivec[n] ^= in[n];\n            ++n;\n        }\n    }\n\n    *num = n;\n}\n\n/*\n* The input and output encrypted as though 128bit cfb mode is being used.\n* The extra state information to record how much of the 128bit block we have\n* used is contained in *num;\n*/\nvoid AES_cfb128_decrypt(const uint8_t *in, uint8_t *out, size_t len, const AES_KEY *key, uint8_t ivec[16], uint32_t *num)\n{\n    auto n = *num;\n\n    while (n && len) {\n        uint8_t c = *(in++);\n        *(out++) = ivec[n] ^ c;\n        ivec[n] = c;\n        --len;\n        n = (n + 1) % 16;\n    }\n    while (len >= 16) {\n        AES_encrypt(ivec, ivec, key);\n        for (; n < 16; n += sizeof(size_t)) {\n            size_t t = *(size_t *)(in + n);\n            *(size_t *)(out + n) = *(size_t *)(ivec + n) ^ t;\n            *(size_t *)(ivec + n) = t;\n        }\n        len -= 16;\n        out += 16;\n        in += 16;\n        n = 0;\n    }\n    if (len) {\n        AES_encrypt(ivec, ivec, key);\n        while (len--) {\n            uint8_t c = in[n];\n            out[n] = ivec[n] ^ c;\n            ivec[n] = c;\n            ++n;\n        }\n    }\n\n    *num = n;\n}\n\n} // namespace openssl\n\n#endif //  MMKV_DISABLE_CRYPT\n"
  },
  {
    "path": "Core/aes/openssl/openssl_md32_common.h",
    "content": "/*\n * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n/*-\n * This is a generic 32 bit \"collector\" for message digest algorithms.\n * Whenever needed it collects input character stream into chunks of\n * 32 bit values and invokes a block function that performs actual hash\n * calculations.\n *\n * Porting guide.\n *\n * Obligatory macros:\n *\n * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN\n *      this macro defines byte order of input stream.\n * HASH_CBLOCK\n *      size of a unit chunk HASH_BLOCK operates on.\n * HASH_LONG\n *      has to be at least 32 bit wide.\n * HASH_CTX\n *      context structure that at least contains following\n *      members:\n *              typedef struct {\n *                      ...\n *                      HASH_LONG       Nl,Nh;\n *                      either {\n *                      HASH_LONG       data[HASH_LBLOCK];\n *                      unsigned char   data[HASH_CBLOCK];\n *                      };\n *                      unsigned int    num;\n *                      ...\n *                      } HASH_CTX;\n *      data[] vector is expected to be zeroed upon first call to\n *      HASH_UPDATE.\n * HASH_UPDATE\n *      name of \"Update\" function, implemented here.\n * HASH_TRANSFORM\n *      name of \"Transform\" function, implemented here.\n * HASH_FINAL\n *      name of \"Final\" function, implemented here.\n * HASH_BLOCK_DATA_ORDER\n *      name of \"block\" function capable of treating *unaligned* input\n *      message in original (data) byte order, implemented externally.\n * HASH_MAKE_STRING\n *      macro converting context variables to an ASCII hash string.\n *\n * MD5 example:\n *\n *      #define DATA_ORDER_IS_LITTLE_ENDIAN\n *\n *      #define HASH_LONG               MD5_LONG\n *      #define HASH_CTX                MD5_CTX\n *      #define HASH_CBLOCK             MD5_CBLOCK\n *      #define HASH_UPDATE             MD5_Update\n *      #define HASH_TRANSFORM          MD5_Transform\n *      #define HASH_FINAL              MD5_Final\n *      #define HASH_BLOCK_DATA_ORDER   md5_block_data_order\n */\n\n\n#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)\n# error \"DATA_ORDER must be defined!\"\n#endif\n\n#ifndef HASH_CBLOCK\n# error \"HASH_CBLOCK must be defined!\"\n#endif\n#ifndef HASH_LONG\n# error \"HASH_LONG must be defined!\"\n#endif\n#ifndef HASH_CTX\n# error \"HASH_CTX must be defined!\"\n#endif\n\n#ifndef HASH_UPDATE\n# error \"HASH_UPDATE must be defined!\"\n#endif\n//#ifndef HASH_TRANSFORM\n//# error \"HASH_TRANSFORM must be defined!\"\n//#endif\n#ifndef HASH_FINAL\n# error \"HASH_FINAL must be defined!\"\n#endif\n\n#ifndef HASH_BLOCK_DATA_ORDER\n# error \"HASH_BLOCK_DATA_ORDER must be defined!\"\n#endif\n\n#define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))\n\n#if defined(DATA_ORDER_IS_BIG_ENDIAN)\n\n# define HOST_c2l(c,l)  (l =(((unsigned long)(*((c)++)))<<24),          \\\n                         l|=(((unsigned long)(*((c)++)))<<16),          \\\n                         l|=(((unsigned long)(*((c)++)))<< 8),          \\\n                         l|=(((unsigned long)(*((c)++)))    )           )\n# define HOST_l2c(l,c)  (*((c)++)=(unsigned char)(((l)>>24)&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)    )&0xff),      \\\n                         l)\n\n#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)\n\n# define HOST_c2l(c,l)  (l =(((unsigned long)(*((c)++)))    ),          \\\n                         l|=(((unsigned long)(*((c)++)))<< 8),          \\\n                         l|=(((unsigned long)(*((c)++)))<<16),          \\\n                         l|=(((unsigned long)(*((c)++)))<<24)           )\n# define HOST_l2c(l,c)  (*((c)++)=(unsigned char)(((l)    )&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \\\n                         *((c)++)=(unsigned char)(((l)>>24)&0xff),      \\\n                         l)\n\n#endif\n\n/*\n * Time for some action :-)\n */\n\nint HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)\n{\n    auto data = (const unsigned char *) data_;\n    unsigned char *p;\n    HASH_LONG l;\n    size_t n;\n\n    if (len == 0)\n        return 1;\n\n    l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;\n    if (l < c->Nl)              /* overflow */\n        c->Nh++;\n    c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on\n                                       * 16-bit */\n    c->Nl = l;\n\n    n = c->num;\n    if (n != 0) {\n        p = (unsigned char *)c->data;\n\n        if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {\n            memcpy(p + n, data, HASH_CBLOCK - n);\n            HASH_BLOCK_DATA_ORDER(c, p, 1);\n            n = HASH_CBLOCK - n;\n            data += n;\n            len -= n;\n            c->num = 0;\n            /*\n             * We use memset rather than OPENSSL_cleanse() here deliberately.\n             * Using OPENSSL_cleanse() here could be a performance issue. It\n             * will get properly cleansed on finalisation so this isn't a\n             * security problem.\n             */\n            memset(p, 0, HASH_CBLOCK); /* keep it zeroed */\n        } else {\n            memcpy(p + n, data, len);\n            c->num += (unsigned int)len;\n            return 1;\n        }\n    }\n\n    n = len / HASH_CBLOCK;\n    if (n > 0) {\n        HASH_BLOCK_DATA_ORDER(c, data, n);\n        n *= HASH_CBLOCK;\n        data += n;\n        len -= n;\n    }\n\n    if (len != 0) {\n        p = (unsigned char *)c->data;\n        c->num = (unsigned int)len;\n        memcpy(p, data, len);\n    }\n    return 1;\n}\n\n//void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)\n//{\n//    HASH_BLOCK_DATA_ORDER(c, data, 1);\n//}\n\nint HASH_FINAL(unsigned char *md, HASH_CTX *c)\n{\n    unsigned char *p = (unsigned char *)c->data;\n    size_t n = c->num;\n\n    p[n] = 0x80;                /* there is always room for one */\n    n++;\n\n    if (n > (HASH_CBLOCK - 8)) {\n        memset(p + n, 0, HASH_CBLOCK - n);\n        n = 0;\n        HASH_BLOCK_DATA_ORDER(c, p, 1);\n    }\n    memset(p + n, 0, HASH_CBLOCK - 8 - n);\n\n    p += HASH_CBLOCK - 8;\n#if   defined(DATA_ORDER_IS_BIG_ENDIAN)\n    (void)HOST_l2c(c->Nh, p);\n    (void)HOST_l2c(c->Nl, p);\n#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)\n    (void)HOST_l2c(c->Nl, p);\n    (void)HOST_l2c(c->Nh, p);\n#endif\n    p -= HASH_CBLOCK;\n    HASH_BLOCK_DATA_ORDER(c, p, 1);\n    c->num = 0;\n\n#ifndef HASH_MAKE_STRING\n# error \"HASH_MAKE_STRING must be defined!\"\n#else\n    HASH_MAKE_STRING(c, md);\n#endif\n\n    return 1;\n}\n\n#ifndef MD32_REG_T\n# if (defined(_LP64) && _LP64)\n#  define MD32_REG_T long\n/*\n * This comment was originally written for MD5, which is why it\n * discusses A-D. But it basically applies to all 32-bit digests,\n * which is why it was moved to common header file.\n *\n * In case you wonder why A-D are declared as long and not\n * as MD5_LONG. Doing so results in slight performance\n * boost on LP64 architectures. The catch is we don't\n * really care if 32 MSBs of a 64-bit register get polluted\n * with eventual overflows as we *save* only 32 LSBs in\n * *either* case. Now declaring 'em long excuses the compiler\n * from keeping 32 MSBs zeroed resulting in 13% performance\n * improvement under SPARC Solaris7/64 and 5% under AlphaLinux.\n * Well, to be honest it should say that this *prevents*\n * performance degradation.\n */\n# else\n/*\n * Above is not absolute and there are LP64 compilers that\n * generate better code if MD32_REG_T is defined int. The above\n * pre-processor condition reflects the circumstances under which\n * the conclusion was made and is subject to further extension.\n */\n#  define MD32_REG_T int\n# endif\n#endif\n"
  },
  {
    "path": "Core/aes/openssl/openssl_md5.h",
    "content": "/*\n * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#ifndef HEADER_MD5_H\n# define HEADER_MD5_H\n#ifdef  __cplusplus\n\n# include \"openssl_opensslconf.h\"\n\n# ifndef OPENSSL_NO_MD5\n# include <stddef.h>\n\nnamespace openssl {\n\n/*\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n * ! MD5_LONG has to be at least 32 bits wide.                     !\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n */\n# define MD5_LONG unsigned int\n\n# define MD5_CBLOCK      64\n# define MD5_LBLOCK      (MD5_CBLOCK/4)\n# define MD5_DIGEST_LENGTH 16\n\ntypedef struct MD5state_st {\n    MD5_LONG A, B, C, D;\n    MD5_LONG Nl, Nh;\n    MD5_LONG data[MD5_LBLOCK];\n    unsigned int num;\n} MD5_CTX;\n\nint MD5_Init(MD5_CTX *c);\nint MD5_Update(MD5_CTX *c, const void *data, size_t len);\nint MD5_Final(unsigned char *md, MD5_CTX *c);\nunsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);\n//void MD5_Transform(MD5_CTX *c, const unsigned char *b);\n\n} // namespace openssl\n# endif\n\n#endif\n#endif\n"
  },
  {
    "path": "Core/aes/openssl/openssl_md5_dgst.cpp",
    "content": "/*\n * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#include <stdio.h>\n#include \"openssl_md5_locl.h\"\n\nnamespace openssl {\n/*\n * Implemented from RFC1321 The MD5 Message-Digest Algorithm\n */\n\n#define INIT_DATA_A (unsigned long)0x67452301L\n#define INIT_DATA_B (unsigned long)0xefcdab89L\n#define INIT_DATA_C (unsigned long)0x98badcfeL\n#define INIT_DATA_D (unsigned long)0x10325476L\n\nint MD5_Init(MD5_CTX *c)\n{\n    memset(c, 0, sizeof(*c));\n    c->A = INIT_DATA_A;\n    c->B = INIT_DATA_B;\n    c->C = INIT_DATA_C;\n    c->D = INIT_DATA_D;\n    return 1;\n}\n\n#ifndef md5_block_data_order\n# ifdef X\n#  undef X\n# endif\nvoid md5_block_data_order(MD5_CTX *c, const void *data_, size_t num)\n{\n    auto data = (const unsigned char *) data_;\n    unsigned MD32_REG_T A, B, C, D, l;\n# ifndef MD32_XARRAY\n    /* See comment in crypto/sha/sha_locl.h for details. */\n    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,\n        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;\n#  define X(i)   XX##i\n# else\n    MD5_LONG XX[MD5_LBLOCK];\n#  define X(i)   XX[i]\n# endif\n\n    A = c->A;\n    B = c->B;\n    C = c->C;\n    D = c->D;\n\n    for (; num--;) {\n        (void)HOST_c2l(data, l);\n        X(0) = l;\n        (void)HOST_c2l(data, l);\n        X(1) = l;\n        /* Round 0 */\n        R0(A, B, C, D, X(0), 7, 0xd76aa478L);\n        (void)HOST_c2l(data, l);\n        X(2) = l;\n        R0(D, A, B, C, X(1), 12, 0xe8c7b756L);\n        (void)HOST_c2l(data, l);\n        X(3) = l;\n        R0(C, D, A, B, X(2), 17, 0x242070dbL);\n        (void)HOST_c2l(data, l);\n        X(4) = l;\n        R0(B, C, D, A, X(3), 22, 0xc1bdceeeL);\n        (void)HOST_c2l(data, l);\n        X(5) = l;\n        R0(A, B, C, D, X(4), 7, 0xf57c0fafL);\n        (void)HOST_c2l(data, l);\n        X(6) = l;\n        R0(D, A, B, C, X(5), 12, 0x4787c62aL);\n        (void)HOST_c2l(data, l);\n        X(7) = l;\n        R0(C, D, A, B, X(6), 17, 0xa8304613L);\n        (void)HOST_c2l(data, l);\n        X(8) = l;\n        R0(B, C, D, A, X(7), 22, 0xfd469501L);\n        (void)HOST_c2l(data, l);\n        X(9) = l;\n        R0(A, B, C, D, X(8), 7, 0x698098d8L);\n        (void)HOST_c2l(data, l);\n        X(10) = l;\n        R0(D, A, B, C, X(9), 12, 0x8b44f7afL);\n        (void)HOST_c2l(data, l);\n        X(11) = l;\n        R0(C, D, A, B, X(10), 17, 0xffff5bb1L);\n        (void)HOST_c2l(data, l);\n        X(12) = l;\n        R0(B, C, D, A, X(11), 22, 0x895cd7beL);\n        (void)HOST_c2l(data, l);\n        X(13) = l;\n        R0(A, B, C, D, X(12), 7, 0x6b901122L);\n        (void)HOST_c2l(data, l);\n        X(14) = l;\n        R0(D, A, B, C, X(13), 12, 0xfd987193L);\n        (void)HOST_c2l(data, l);\n        X(15) = l;\n        R0(C, D, A, B, X(14), 17, 0xa679438eL);\n        R0(B, C, D, A, X(15), 22, 0x49b40821L);\n        /* Round 1 */\n        R1(A, B, C, D, X(1), 5, 0xf61e2562L);\n        R1(D, A, B, C, X(6), 9, 0xc040b340L);\n        R1(C, D, A, B, X(11), 14, 0x265e5a51L);\n        R1(B, C, D, A, X(0), 20, 0xe9b6c7aaL);\n        R1(A, B, C, D, X(5), 5, 0xd62f105dL);\n        R1(D, A, B, C, X(10), 9, 0x02441453L);\n        R1(C, D, A, B, X(15), 14, 0xd8a1e681L);\n        R1(B, C, D, A, X(4), 20, 0xe7d3fbc8L);\n        R1(A, B, C, D, X(9), 5, 0x21e1cde6L);\n        R1(D, A, B, C, X(14), 9, 0xc33707d6L);\n        R1(C, D, A, B, X(3), 14, 0xf4d50d87L);\n        R1(B, C, D, A, X(8), 20, 0x455a14edL);\n        R1(A, B, C, D, X(13), 5, 0xa9e3e905L);\n        R1(D, A, B, C, X(2), 9, 0xfcefa3f8L);\n        R1(C, D, A, B, X(7), 14, 0x676f02d9L);\n        R1(B, C, D, A, X(12), 20, 0x8d2a4c8aL);\n        /* Round 2 */\n        R2(A, B, C, D, X(5), 4, 0xfffa3942L);\n        R2(D, A, B, C, X(8), 11, 0x8771f681L);\n        R2(C, D, A, B, X(11), 16, 0x6d9d6122L);\n        R2(B, C, D, A, X(14), 23, 0xfde5380cL);\n        R2(A, B, C, D, X(1), 4, 0xa4beea44L);\n        R2(D, A, B, C, X(4), 11, 0x4bdecfa9L);\n        R2(C, D, A, B, X(7), 16, 0xf6bb4b60L);\n        R2(B, C, D, A, X(10), 23, 0xbebfbc70L);\n        R2(A, B, C, D, X(13), 4, 0x289b7ec6L);\n        R2(D, A, B, C, X(0), 11, 0xeaa127faL);\n        R2(C, D, A, B, X(3), 16, 0xd4ef3085L);\n        R2(B, C, D, A, X(6), 23, 0x04881d05L);\n        R2(A, B, C, D, X(9), 4, 0xd9d4d039L);\n        R2(D, A, B, C, X(12), 11, 0xe6db99e5L);\n        R2(C, D, A, B, X(15), 16, 0x1fa27cf8L);\n        R2(B, C, D, A, X(2), 23, 0xc4ac5665L);\n        /* Round 3 */\n        R3(A, B, C, D, X(0), 6, 0xf4292244L);\n        R3(D, A, B, C, X(7), 10, 0x432aff97L);\n        R3(C, D, A, B, X(14), 15, 0xab9423a7L);\n        R3(B, C, D, A, X(5), 21, 0xfc93a039L);\n        R3(A, B, C, D, X(12), 6, 0x655b59c3L);\n        R3(D, A, B, C, X(3), 10, 0x8f0ccc92L);\n        R3(C, D, A, B, X(10), 15, 0xffeff47dL);\n        R3(B, C, D, A, X(1), 21, 0x85845dd1L);\n        R3(A, B, C, D, X(8), 6, 0x6fa87e4fL);\n        R3(D, A, B, C, X(15), 10, 0xfe2ce6e0L);\n        R3(C, D, A, B, X(6), 15, 0xa3014314L);\n        R3(B, C, D, A, X(13), 21, 0x4e0811a1L);\n        R3(A, B, C, D, X(4), 6, 0xf7537e82L);\n        R3(D, A, B, C, X(11), 10, 0xbd3af235L);\n        R3(C, D, A, B, X(2), 15, 0x2ad7d2bbL);\n        R3(B, C, D, A, X(9), 21, 0xeb86d391L);\n\n        A = c->A += A;\n        B = c->B += B;\n        C = c->C += C;\n        D = c->D += D;\n    }\n}\n#endif\n\n} // namespace openssl\n"
  },
  {
    "path": "Core/aes/openssl/openssl_md5_locl.h",
    "content": "/*\n * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n#ifdef  __cplusplus\n\n#include <stdlib.h>\n#include <string.h>\n#include \"openssl_md5.h\"\n\nnamespace openssl {\n\nvoid md5_block_data_order(MD5_CTX *c, const void *p, size_t num);\n\n#define DATA_ORDER_IS_LITTLE_ENDIAN\n\n#define HASH_LONG               MD5_LONG\n#define HASH_CTX                MD5_CTX\n#define HASH_CBLOCK             MD5_CBLOCK\n#define HASH_UPDATE             MD5_Update\n//#define HASH_TRANSFORM          MD5_Transform\n#define HASH_FINAL              MD5_Final\n#define HASH_MAKE_STRING(c,s)   do {    \\\n        unsigned long ll;               \\\n        ll=(c)->A; (void)HOST_l2c(ll,(s));      \\\n        ll=(c)->B; (void)HOST_l2c(ll,(s));      \\\n        ll=(c)->C; (void)HOST_l2c(ll,(s));      \\\n        ll=(c)->D; (void)HOST_l2c(ll,(s));      \\\n        } while (0)\n#define HASH_BLOCK_DATA_ORDER   md5_block_data_order\n\n#include \"openssl_md32_common.h\"\n\n/*-\n#define F(x,y,z)        (((x) & (y))  |  ((~(x)) & (z)))\n#define G(x,y,z)        (((x) & (z))  |  ((y) & (~(z))))\n*/\n\n/*\n * As pointed out by Wei Dai, the above can be simplified to the code\n * below.  Wei attributes these optimizations to Peter Gutmann's\n * SHS code, and he attributes it to Rich Schroeppel.\n */\n#define F(b,c,d)        ((((c) ^ (d)) & (b)) ^ (d))\n#define G(b,c,d)        ((((b) ^ (c)) & (d)) ^ (c))\n#define H(b,c,d)        ((b) ^ (c) ^ (d))\n#define I(b,c,d)        (((~(d)) | (b)) ^ (c))\n\n#define R0(a,b,c,d,k,s,t) { \\\n        a+=((k)+(t)+F((b),(c),(d))); \\\n        a=ROTATE(a,s); \\\n        a+=b; };\\\n\n#define R1(a,b,c,d,k,s,t) { \\\n        a+=((k)+(t)+G((b),(c),(d))); \\\n        a=ROTATE(a,s); \\\n        a+=b; };\n\n#define R2(a,b,c,d,k,s,t) { \\\n        a+=((k)+(t)+H((b),(c),(d))); \\\n        a=ROTATE(a,s); \\\n        a+=b; };\n\n#define R3(a,b,c,d,k,s,t) { \\\n        a+=((k)+(t)+I((b),(c),(d))); \\\n        a=ROTATE(a,s); \\\n        a+=b; };\n\n} // namespace openssl\n\n#endif\n"
  },
  {
    "path": "Core/aes/openssl/openssl_md5_one.cpp",
    "content": "/*\n * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#include <stdio.h>\n#include <string.h>\n#include \"openssl_md5.h\"\n\nnamespace openssl {\n\nunsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)\n{\n    MD5_CTX c;\n    static unsigned char m[MD5_DIGEST_LENGTH];\n\n    if (md == nullptr)\n        md = m;\n    if (!MD5_Init(&c))\n        return nullptr;\n    MD5_Update(&c, d, n);\n    MD5_Final(md, &c);\n    return md;\n}\n\n} // namespace openssl\n"
  },
  {
    "path": "Core/aes/openssl/openssl_opensslconf.h",
    "content": "/* opensslconf.h */\n/* WARNING: Generated automatically from opensslconf.h.in by Configure. */\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n/* OpenSSL was configured with the following options: */\n#ifndef OPENSSL_SYSNAME_iOS\n# define OPENSSL_SYSNAME_iOS\n#endif\n#ifndef OPENSSL_DOING_MAKEDEPEND\n\n\n#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128\n# define OPENSSL_NO_EC_NISTP_64_GCC_128\n#endif\n#ifndef OPENSSL_NO_GMP\n# define OPENSSL_NO_GMP\n#endif\n#ifndef OPENSSL_NO_JPAKE\n# define OPENSSL_NO_JPAKE\n#endif\n#ifndef OPENSSL_NO_KRB5\n# define OPENSSL_NO_KRB5\n#endif\n#ifndef OPENSSL_NO_LIBUNBOUND\n# define OPENSSL_NO_LIBUNBOUND\n#endif\n#ifndef OPENSSL_NO_MD2\n# define OPENSSL_NO_MD2\n#endif\n#ifndef OPENSSL_NO_RC5\n# define OPENSSL_NO_RC5\n#endif\n#ifndef OPENSSL_NO_RFC3779\n# define OPENSSL_NO_RFC3779\n#endif\n#ifndef OPENSSL_NO_SCTP\n# define OPENSSL_NO_SCTP\n#endif\n#ifndef OPENSSL_NO_SSL_TRACE\n# define OPENSSL_NO_SSL_TRACE\n#endif\n#ifndef OPENSSL_NO_SSL2\n# define OPENSSL_NO_SSL2\n#endif\n#ifndef OPENSSL_NO_STORE\n# define OPENSSL_NO_STORE\n#endif\n#ifndef OPENSSL_NO_UNIT_TEST\n# define OPENSSL_NO_UNIT_TEST\n#endif\n#ifndef OPENSSL_NO_WEAK_SSL_CIPHERS\n# define OPENSSL_NO_WEAK_SSL_CIPHERS\n#endif\n\n#endif /* OPENSSL_DOING_MAKEDEPEND */\n\n#ifndef OPENSSL_THREADS\n# define OPENSSL_THREADS\n#endif\n#ifndef OPENSSL_NO_DYNAMIC_ENGINE\n# define OPENSSL_NO_DYNAMIC_ENGINE\n#endif\n\n/* The OPENSSL_NO_* macros are also defined as NO_* if the application\n   asks for it.  This is a transient feature that is provided for those\n   who haven't had the time to do the appropriate changes in their\n   applications.  */\n#ifdef OPENSSL_ALGORITHM_DEFINES\n# if defined(OPENSSL_NO_EC_NISTP_64_GCC_128) && !defined(NO_EC_NISTP_64_GCC_128)\n#  define NO_EC_NISTP_64_GCC_128\n# endif\n# if defined(OPENSSL_NO_GMP) && !defined(NO_GMP)\n#  define NO_GMP\n# endif\n# if defined(OPENSSL_NO_JPAKE) && !defined(NO_JPAKE)\n#  define NO_JPAKE\n# endif\n# if defined(OPENSSL_NO_KRB5) && !defined(NO_KRB5)\n#  define NO_KRB5\n# endif\n# if defined(OPENSSL_NO_LIBUNBOUND) && !defined(NO_LIBUNBOUND)\n#  define NO_LIBUNBOUND\n# endif\n# if defined(OPENSSL_NO_MD2) && !defined(NO_MD2)\n#  define NO_MD2\n# endif\n# if defined(OPENSSL_NO_RC5) && !defined(NO_RC5)\n#  define NO_RC5\n# endif\n# if defined(OPENSSL_NO_RFC3779) && !defined(NO_RFC3779)\n#  define NO_RFC3779\n# endif\n# if defined(OPENSSL_NO_SCTP) && !defined(NO_SCTP)\n#  define NO_SCTP\n# endif\n# if defined(OPENSSL_NO_SSL_TRACE) && !defined(NO_SSL_TRACE)\n#  define NO_SSL_TRACE\n# endif\n# if defined(OPENSSL_NO_SSL2) && !defined(NO_SSL2)\n#  define NO_SSL2\n# endif\n# if defined(OPENSSL_NO_STORE) && !defined(NO_STORE)\n#  define NO_STORE\n# endif\n# if defined(OPENSSL_NO_UNIT_TEST) && !defined(NO_UNIT_TEST)\n#  define NO_UNIT_TEST\n# endif\n# if defined(OPENSSL_NO_WEAK_SSL_CIPHERS) && !defined(NO_WEAK_SSL_CIPHERS)\n#  define NO_WEAK_SSL_CIPHERS\n# endif\n#endif\n\n/* crypto/opensslconf.h.in */\n\n/* Generate 80386 code? */\n#undef I386_ONLY\n\n#if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */\n#if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR)\n#define ENGINESDIR \"/compile/openssl-ios-build-shell-script-master/openssl-1.0.2k-build/armv7/lib/engines\"\n#define OPENSSLDIR \"/compile/openssl-ios-build-shell-script-master/openssl-1.0.2k-build/armv7/ssl\"\n#endif\n#endif\n\n#undef OPENSSL_UNISTD\n#define OPENSSL_UNISTD <unistd.h>\n\n#undef OPENSSL_EXPORT_VAR_AS_FUNCTION\n\n#if defined(HEADER_IDEA_H) && !defined(IDEA_INT)\n#define IDEA_INT unsigned int\n#endif\n\n#if defined(HEADER_MD2_H) && !defined(MD2_INT)\n#define MD2_INT unsigned int\n#endif\n\n#if defined(HEADER_RC2_H) && !defined(RC2_INT)\n/* I need to put in a mod for the alpha - eay */\n#define RC2_INT unsigned int\n#endif\n\n#if defined(HEADER_RC4_H)\n#if !defined(RC4_INT)\n/* using int types make the structure larger but make the code faster\n * on most boxes I have tested - up to %20 faster. */\n/*\n * I don't know what does \"most\" mean, but declaring \"int\" is a must on:\n * - Intel P6 because partial register stalls are very expensive;\n * - elder Alpha because it lacks byte load/store instructions;\n */\n#define RC4_INT unsigned char\n#endif\n#if !defined(RC4_CHUNK)\n/*\n * This enables code handling data aligned at natural CPU word\n * boundary. See crypto/rc4/rc4_enc.c for further details.\n */\n#define RC4_CHUNK unsigned long\n#endif\n#endif\n\n#if (defined(HEADER_NEW_DES_H) || defined(HEADER_DES_H)) && !defined(DES_LONG)\n/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a\n * %20 speed up (longs are 8 bytes, int's are 4). */\n#ifndef DES_LONG\n#define DES_LONG unsigned long\n#endif\n#endif\n\n#if defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H)\n#define CONFIG_HEADER_BN_H\n#define BN_LLONG\n\n/* Should we define BN_DIV2W here? */\n\n/* Only one for the following should be defined */\n#undef SIXTY_FOUR_BIT_LONG\n#undef SIXTY_FOUR_BIT\n#define THIRTY_TWO_BIT\n#endif\n\n#if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H)\n#define CONFIG_HEADER_RC4_LOCL_H\n/* if this is defined data[i] is used instead of *data, this is a %20\n * speedup on x86 */\n#undef RC4_INDEX\n#endif\n\n#if defined(HEADER_BF_LOCL_H) && !defined(CONFIG_HEADER_BF_LOCL_H)\n#define CONFIG_HEADER_BF_LOCL_H\n#define BF_PTR\n#endif /* HEADER_BF_LOCL_H */\n\n#if defined(HEADER_DES_LOCL_H) && !defined(CONFIG_HEADER_DES_LOCL_H)\n#define CONFIG_HEADER_DES_LOCL_H\n#ifndef DES_DEFAULT_OPTIONS\n/* the following is tweaked from a config script, that is why it is a\n * protected undef/define */\n#ifndef DES_PTR\n#undef DES_PTR\n#endif\n\n/* This helps C compiler generate the correct code for multiple functional\n * units.  It reduces register dependancies at the expense of 2 more\n * registers */\n#ifndef DES_RISC1\n#undef DES_RISC1\n#endif\n\n#ifndef DES_RISC2\n#undef DES_RISC2\n#endif\n\n#if defined(DES_RISC1) && defined(DES_RISC2)\n#error YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!!\n#endif\n\n/* Unroll the inner loop, this sometimes helps, sometimes hinders.\n * Very mucy CPU dependant */\n#ifndef DES_UNROLL\n#define DES_UNROLL\n#endif\n\n/* These default values were supplied by\n * Peter Gutman <pgut001@cs.auckland.ac.nz>\n * They are only used if nothing else has been defined */\n#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL)\n/* Special defines which change the way the code is built depending on the\n   CPU and OS.  For SGI machines you can use _MIPS_SZLONG (32 or 64) to find\n   even newer MIPS CPU's, but at the moment one size fits all for\n   optimization options.  Older Sparc's work better with only UNROLL, but\n   there's no way to tell at compile time what it is you're running on */\n \n#if defined( __sun ) || defined ( sun )\t\t/* Newer Sparc's */\n#  define DES_PTR\n#  define DES_RISC1\n#  define DES_UNROLL\n#elif defined( __ultrix )\t/* Older MIPS */\n#  define DES_PTR\n#  define DES_RISC2\n#  define DES_UNROLL\n#elif defined( __osf1__ )\t/* Alpha */\n#  define DES_PTR\n#  define DES_RISC2\n#elif defined ( _AIX )\t\t/* RS6000 */\n  /* Unknown */\n#elif defined( __hpux )\t\t/* HP-PA */\n  /* Unknown */\n#elif defined( __aux )\t\t/* 68K */\n  /* Unknown */\n#elif defined( __dgux )\t\t/* 88K (but P6 in latest boxes) */\n#  define DES_UNROLL\n#elif defined( __sgi )\t\t/* Newer MIPS */\n#  define DES_PTR\n#  define DES_RISC2\n#  define DES_UNROLL\n#elif defined(i386) || defined(__i386__)\t/* x86 boxes, should be gcc */\n#  define DES_PTR\n#  define DES_RISC1\n#  define DES_UNROLL\n#endif /* Systems-specific speed defines */\n#endif\n\n#endif /* DES_DEFAULT_OPTIONS */\n#endif /* HEADER_DES_LOCL_H */\n#ifdef  __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Core/core.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"aes\\AESCrypt.cpp\" />\r\n    <ClCompile Include=\"aes\\openssl\\openssl_aes_core.cpp\" />\r\n    <ClCompile Include=\"aes\\openssl\\openssl_cfb128.cpp\" />\r\n    <ClCompile Include=\"aes\\openssl\\openssl_md5_dgst.cpp\" />\r\n    <ClCompile Include=\"aes\\openssl\\openssl_md5_one.cpp\" />\r\n    <ClCompile Include=\"CodedInputData.cpp\" />\r\n    <ClCompile Include=\"CodedInputDataCrypt.cpp\" />\r\n    <ClCompile Include=\"CodedOutputData.cpp\" />\r\n    <ClCompile Include=\"crc32\\zlib\\crc32.cpp\" />\r\n    <ClCompile Include=\"InterProcessLock.cpp\" />\r\n    <ClCompile Include=\"InterProcessLock_Win32.cpp\" />\r\n    <ClCompile Include=\"KeyValueHolder.cpp\" />\r\n    <ClCompile Include=\"MemoryFile_Win32.cpp\" />\r\n    <ClCompile Include=\"MiniPBCoder.cpp\" />\r\n    <ClCompile Include=\"MMBuffer.cpp\" />\r\n    <ClCompile Include=\"MMKV.cpp\" />\r\n    <ClCompile Include=\"MMKVLog.cpp\" />\r\n    <ClCompile Include=\"MMKV_IO.cpp\" />\r\n    <ClCompile Include=\"PBUtility.cpp\" />\r\n    <ClCompile Include=\"ThreadLock_Win32.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"aes\\AESCrypt.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_aes.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_aes_locl.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_arm_arch.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md32_common.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md5.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md5_locl.h\" />\r\n    <ClInclude Include=\"aes\\openssl\\openssl_opensslconf.h\" />\r\n    <ClInclude Include=\"CodedInputData.h\" />\r\n    <ClInclude Include=\"CodedInputDataCrypt.h\" />\r\n    <ClInclude Include=\"CodedOutputData.h\" />\r\n    <ClInclude Include=\"crc32\\Checksum.h\" />\r\n    <ClInclude Include=\"crc32\\zlib\\crc32.h\" />\r\n    <ClInclude Include=\"crc32\\zlib\\zconf.h\" />\r\n    <ClInclude Include=\"crc32\\zlib\\zutil.h\" />\r\n    <ClInclude Include=\"InterProcessLock.h\" />\r\n    <ClInclude Include=\"KeyValueHolder.h\" />\r\n    <ClInclude Include=\"MemoryFile.h\" />\r\n    <ClInclude Include=\"MiniPBCoder.h\" />\r\n    <ClInclude Include=\"MMBuffer.h\" />\r\n    <ClInclude Include=\"MMKV.h\" />\r\n    <ClInclude Include=\"MMKVLog.h\" />\r\n    <ClInclude Include=\"MMKVMetaInfo.hpp\" />\r\n    <ClInclude Include=\"MMKVPredef.h\" />\r\n    <ClInclude Include=\"MMKV_IO.h\" />\r\n    <ClInclude Include=\"PBEncodeItem.hpp\" />\r\n    <ClInclude Include=\"PBUtility.h\" />\r\n    <ClInclude Include=\"ScopedLock.hpp\" />\r\n    <ClInclude Include=\"ThreadLock.h\" />\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}</ProjectGuid>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <Platform>Win32</Platform>\r\n    <ProjectName>core</ProjectName>\r\n    <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup>\r\n    <_ProjectFileVersion>10.0.20506.1</_ProjectFileVersion>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">$(SolutionDir)$(Configuration)\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">$(Configuration)\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">mmkv</TargetName>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">mmkv</TargetName>\r\n    <TargetExt Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">.lib</TargetExt>\r\n    <TargetExt Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">.lib</TargetExt>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">$(SolutionDir)$(Configuration)\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">$(Configuration)\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">mmkv</TargetName>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">mmkv</TargetName>\r\n    <TargetExt Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">.lib</TargetExt>\r\n    <TargetExt Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">.lib</TargetExt>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <AdditionalOptions>%(AdditionalOptions) -std:c++latest</AdditionalOptions>\r\n      <AssemblerListingLocation>Debug/</AssemblerListingLocation>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <CompileAs>CompileAsCpp</CompileAs>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <ExceptionHandling>Sync</ExceptionHandling>\r\n      <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>\r\n      <Optimization>Disabled</Optimization>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <RuntimeTypeInfo>true</RuntimeTypeInfo>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ObjectFileName>$(IntDir)</ObjectFileName>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;CMAKE_INTDIR=\\\"Debug\\\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Midl>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>\r\n      <HeaderFileName>%(Filename).h</HeaderFileName>\r\n      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>\r\n      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>\r\n      <ProxyFileName>%(Filename)_p.c</ProxyFileName>\r\n    </Midl>\r\n    <Lib>\r\n      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>\r\n    </Lib>\r\n    <PostBuildEvent>\r\n      <Command>for %%f in (\"$(ProjectDir)MMKV.h\", \"$(ProjectDir)MMBuffer.h\",  \"$(ProjectDir)MMKVPredef.h\", \"$(ProjectDir)MiniPBCoder.h\") do xcopy /y /i %%f \"$(OutDir)\\include\\MMKV\\\"</Command>\r\n    </PostBuildEvent>\r\n    <PostBuildEvent>\r\n      <Message>Copying Headers</Message>\r\n    </PostBuildEvent>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <AdditionalOptions>%(AdditionalOptions) -std:c++latest</AdditionalOptions>\r\n      <AssemblerListingLocation>Debug/</AssemblerListingLocation>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <CompileAs>CompileAsCpp</CompileAs>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <ExceptionHandling>Sync</ExceptionHandling>\r\n      <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>\r\n      <Optimization>Disabled</Optimization>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <RuntimeTypeInfo>true</RuntimeTypeInfo>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PreprocessorDefinitions>_WINDOWS;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ObjectFileName>$(IntDir)</ObjectFileName>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;CMAKE_INTDIR=\\\"Debug\\\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Midl>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>\r\n      <HeaderFileName>%(Filename).h</HeaderFileName>\r\n      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>\r\n      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>\r\n      <ProxyFileName>%(Filename)_p.c</ProxyFileName>\r\n    </Midl>\r\n    <Lib>\r\n      <AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>\r\n    </Lib>\r\n    <PostBuildEvent>\r\n      <Command>for %%f in (\"$(ProjectDir)MMKV.h\", \"$(ProjectDir)MMBuffer.h\",  \"$(ProjectDir)MMKVPredef.h\", \"$(ProjectDir)MiniPBCoder.h\") do xcopy /y /i %%f \"$(OutDir)\\include\\MMKV\\\"</Command>\r\n    </PostBuildEvent>\r\n    <PostBuildEvent>\r\n      <Message>Copying Headers</Message>\r\n    </PostBuildEvent>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <AdditionalOptions>%(AdditionalOptions) -std:c++latest</AdditionalOptions>\r\n      <AssemblerListingLocation>Release/</AssemblerListingLocation>\r\n      <CompileAs>CompileAsCpp</CompileAs>\r\n      <ExceptionHandling>Sync</ExceptionHandling>\r\n      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <RuntimeTypeInfo>true</RuntimeTypeInfo>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ObjectFileName>$(IntDir)</ObjectFileName>\r\n      <DebugInformationFormat>\r\n      </DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;CMAKE_INTDIR=\\\"Release\\\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Midl>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>\r\n      <HeaderFileName>%(Filename).h</HeaderFileName>\r\n      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>\r\n      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>\r\n      <ProxyFileName>%(Filename)_p.c</ProxyFileName>\r\n    </Midl>\r\n    <Lib>\r\n      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>\r\n    </Lib>\r\n    <PostBuildEvent>\r\n      <Command>for %%f in (\"$(ProjectDir)MMKV.h\", \"$(ProjectDir)MMBuffer.h\",  \"$(ProjectDir)MMKVPredef.h\", \"$(ProjectDir)MiniPBCoder.h\") do xcopy /y /i %%f \"$(OutDir)\\include\\MMKV\\\"</Command>\r\n    </PostBuildEvent>\r\n    <PostBuildEvent>\r\n      <Message>Copying Headers</Message>\r\n    </PostBuildEvent>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <AdditionalOptions>%(AdditionalOptions) -std:c++latest</AdditionalOptions>\r\n      <AssemblerListingLocation>Release/</AssemblerListingLocation>\r\n      <CompileAs>CompileAsCpp</CompileAs>\r\n      <ExceptionHandling>Sync</ExceptionHandling>\r\n      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <RuntimeTypeInfo>true</RuntimeTypeInfo>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ObjectFileName>$(IntDir)</ObjectFileName>\r\n      <DebugInformationFormat>\r\n      </DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;CMAKE_INTDIR=\\\"Release\\\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Midl>\r\n      <AdditionalIncludeDirectories>Z:\\mmkv\\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>\r\n      <HeaderFileName>%(Filename).h</HeaderFileName>\r\n      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>\r\n      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>\r\n      <ProxyFileName>%(Filename)_p.c</ProxyFileName>\r\n    </Midl>\r\n    <Lib>\r\n      <AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>\r\n    </Lib>\r\n    <PostBuildEvent>\r\n      <Command>for %%f in (\"$(ProjectDir)MMKV.h\", \"$(ProjectDir)MMBuffer.h\",  \"$(ProjectDir)MMKVPredef.h\", \"$(ProjectDir)MiniPBCoder.h\") do xcopy /y /i %%f \"$(OutDir)\\include\\MMKV\\\"</Command>\r\n    </PostBuildEvent>\r\n    <PostBuildEvent>\r\n      <Message>Copying Headers</Message>\r\n    </PostBuildEvent>\r\n  </ItemDefinitionGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Core/core.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{A23FB4D7-B12B-3111-88E9-80ECCF93DBA8}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{047F57F7-8EBB-3570-BBF5-21BCA0934FD5}</UniqueIdentifier>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"CodedInputData.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"CodedOutputData.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"aes\\AESCrypt.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"crc32\\zlib\\crc32.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"InterProcessLock.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MemoryFile_Win32.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MMBuffer.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MMKV.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MMKVLog.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MiniPBCoder.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"PBUtility.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"ThreadLock_Win32.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"InterProcessLock_Win32.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"aes\\openssl\\openssl_aes_core.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"aes\\openssl\\openssl_cfb128.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"aes\\openssl\\openssl_md5_dgst.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"aes\\openssl\\openssl_md5_one.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"KeyValueHolder.cpp\" />\r\n    <ClCompile Include=\"CodedInputDataCrypt.cpp\" />\r\n    <ClCompile Include=\"MMKV_IO.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"CodedInputData.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"CodedOutputData.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"InterProcessLock.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MemoryFile.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MiniPBCoder.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MMBuffer.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MMKV.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MMKVLog.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"crc32\\zlib\\crc32.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"crc32\\zlib\\zconf.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"crc32\\zlib\\zutil.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\AESCrypt.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"crc32\\Checksum.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MMKVMetaInfo.hpp\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MMKVPredef.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"PBEncodeItem.hpp\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"PBUtility.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"ScopedLock.hpp\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"ThreadLock.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_aes.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_aes_locl.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md5.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md5_locl.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_opensslconf.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_md32_common.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"aes\\openssl\\openssl_arm_arch.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"KeyValueHolder.h\" />\r\n    <ClInclude Include=\"CodedInputDataCrypt.h\" />\r\n    <ClInclude Include=\"MMKV_IO.h\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Core/crc32/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2020 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets the minimum version of CMake required to build the native library.\n\ncmake_minimum_required(VERSION 3.10.0)\n\nproject(z)\n\nIF(APPLE)\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\n\nadd_library( # Sets the name of the library.\n             z\n\n             # Sets the library as a shared library.\n             STATIC\n\n             # Provides a relative path to your source file(s).\n             zlib\n             Checksum.h\n             crc32_armv8.cpp\n        )\n\n\nset_target_properties(z PROPERTIES\n            CXX_STANDARD 17\n            CXX_EXTENSIONS OFF\n            )\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\n\n"
  },
  {
    "path": "Core/crc32/Checksum.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CHECKSUM_H\n#define CHECKSUM_H\n#ifdef __cplusplus\n\n#include \"../MMKVPredef.h\"\n#include <cstdint>\n\n#if MMKV_EMBED_ZLIB\n\n#    include \"zlib/zconf.h\"\n\nnamespace zlib {\n\nuLong crc32(uLong crc, const Bytef *buf, z_size_t len);\n\n} // namespace zlib\n\n#    define ZLIB_CRC32(crc, buf, len) zlib::crc32(crc, buf, len)\n\n#else // MMKV_EMBED_ZLIB\n\n#    include <zlib.h>\n// some old version of zlib doesn't define z_size_t\n#    ifndef z_size_t\n       typedef size_t z_size_t;\n#    endif\n#    define ZLIB_CRC32(crc, buf, len) ::crc32(crc, buf, static_cast<uInt>(len))\n\n#endif // MMKV_EMBED_ZLIB\n\n\n#if defined(__aarch64__) && defined(__linux__)\n\n#    define MMKV_USE_ARMV8_CRC32\n\nnamespace mmkv {\nuint32_t armv8_crc32(uint32_t crc, const uint8_t *buf, size_t len);\n}\n\n#   ifdef MMKV_OHOS\n// getauxval(AT_HWCAP) in OHOS returns wrong value, we just assume all OHOS device have crc32 instr\n#       define CRC32 mmkv::armv8_crc32\n#   else\n// have to check CPU's instruction set dynamically\ntypedef uint32_t (*CRC32_Func_t)(uint32_t crc, const uint8_t *buf, size_t len);\nextern CRC32_Func_t CRC32;\n#   endif\n\n#else // defined(__aarch64__) && defined(__linux__)\n\n#    define CRC32(crc, buf, len) ZLIB_CRC32(crc, buf, len)\n\n#endif // defined(__aarch64__) && defined(__linux__)\n\n#endif // __cplusplus\n#endif // CHECKSUM_H\n"
  },
  {
    "path": "Core/crc32/crc32_armv8.cpp",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#include \"Checksum.h\"\n\n#ifdef MMKV_USE_ARMV8_CRC32\n\n#    ifdef CRC32\n// nothing to do\n#    elif MMKV_EMBED_ZLIB\n\nstatic inline uint32_t _crc32Wrap(uint32_t crc, const uint8_t *buf, size_t len) {\n    return static_cast<uint32_t>(zlib::crc32(crc, buf, static_cast<uInt>(len)));\n}\n\nCRC32_Func_t CRC32 = _crc32Wrap;\n\n#    else\n#        include <zlib.h>\n\nstatic inline uint32_t _crc32Wrap(uint32_t crc, const uint8_t *buf, size_t len) {\n    return static_cast<uint32_t>(::crc32(crc, buf, static_cast<uInt>(len)));\n}\n\nCRC32_Func_t CRC32 = _crc32Wrap;\n\n#    endif\n\n// targeting armv8 with crc instruction extension\n#if defined(__GNUC__) && !defined(__clang__)\n#    define TARGET_ARM_CRC __attribute__((target(\"+crc\")))\n#    define __builtin_arm_crc32b(a, b) __builtin_aarch64_crc32b(a, b)\n#    define __builtin_arm_crc32h(a, b) __builtin_aarch64_crc32h(a, b)\n#    define __builtin_arm_crc32w(a, b) __builtin_aarch64_crc32w(a, b)\n#    define __builtin_arm_crc32d(a, b) __builtin_aarch64_crc32x(a, b)\n#else\n#    define TARGET_ARM_CRC __attribute__((target(\"crc\")))\n#endif\n\nTARGET_ARM_CRC static inline uint32_t __crc32b(uint32_t a, uint8_t b) {\n    return __builtin_arm_crc32b(a, b);\n}\n\nTARGET_ARM_CRC static inline uint32_t __crc32h(uint32_t a, uint16_t b) {\n    return __builtin_arm_crc32h(a, b);\n}\n\nTARGET_ARM_CRC static inline uint32_t __crc32w(uint32_t a, uint32_t b) {\n    return __builtin_arm_crc32w(a, b);\n}\n\nTARGET_ARM_CRC static inline uint32_t __crc32d(uint32_t a, uint64_t b) {\n    return __builtin_arm_crc32d(a, b);\n}\n\nTARGET_ARM_CRC static inline uint32_t armv8_crc32_small(uint32_t crc, const uint8_t *buf, size_t len) {\n    if (len >= sizeof(uint32_t)) {\n        crc = __crc32w(crc, *(const uint32_t *) buf);\n        buf += sizeof(uint32_t);\n        len -= sizeof(uint32_t);\n    }\n    if (len >= sizeof(uint16_t)) {\n        crc = __crc32h(crc, *(const uint16_t *) buf);\n        buf += sizeof(uint16_t);\n        len -= sizeof(uint16_t);\n    }\n    if (len >= sizeof(uint8_t)) {\n        crc = __crc32b(crc, *(const uint8_t *) buf);\n    }\n\n    return crc;\n}\n\nnamespace mmkv {\n\nTARGET_ARM_CRC uint32_t armv8_crc32(uint32_t crc, const uint8_t *buf, size_t len) {\n\n    crc = crc ^ 0xffffffffUL;\n\n    // roundup to 8 byte pointer\n    auto offset = std::min(len, (uintptr_t) buf & 7);\n    if (offset) {\n        crc = armv8_crc32_small(crc, buf, offset);\n        buf += offset;\n        len -= offset;\n    }\n    if (!len) {\n        return crc ^ 0xffffffffUL;\n    }\n\n    // unroll to 8 * 8 byte per loop\n    auto ptr64 = (const uint64_t *) buf;\n    for (constexpr auto step = 8 * sizeof(uint64_t); len >= step; len -= step) {\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n        crc = __crc32d(crc, *ptr64++);\n    }\n\n    for (constexpr auto step = sizeof(uint64_t); len >= step; len -= step) {\n        crc = __crc32d(crc, *ptr64++);\n    }\n\n    if (len) {\n        crc = armv8_crc32_small(crc, (const uint8_t *) ptr64, len);\n    }\n\n    return crc ^ 0xffffffffUL;\n}\n\n} // namespace mmkv\n\n#endif // MMKV_USE_ARMV8_CRC32\n"
  },
  {
    "path": "Core/crc32/zlib/crc32.cpp",
    "content": "/* crc32.c -- compute the CRC-32 of a data stream\n * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n *\n * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster\n * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing\n * tables for updating the shift register in one step with three exclusive-ors\n * instead of four steps with four exclusive-ors.  This results in about a\n * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.\n */\n\n/* @(#) $Id$ */\n\n#include \"../../MMKVPredef.h\"\n\n#if MMKV_EMBED_ZLIB\n\n#include \"zutil.h\"      /* for STDC and FAR definitions */\n\n#define Z_NULL 0   /* for initializing zalloc, zfree, opaque */\n#  define TBLS 1\n\n#include \"crc32.h\"\n\nnamespace zlib {\n\n/* ========================================================================= */\n#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)\n#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1\n\n/* ========================================================================= */\nunsigned long local crc32_z(unsigned long crc, const unsigned char FAR *buf, z_size_t len)\n{\n    if (buf == Z_NULL) return 0UL;\n\n    crc = crc ^ 0xffffffffUL;\n    while (len >= 8) {\n        DO8;\n        len -= 8;\n    }\n    if (len) do {\n        DO1;\n    } while (--len);\n    return crc ^ 0xffffffffUL;\n}\n\n/* ========================================================================= */\n\nunsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, z_size_t len) {\n    return crc32_z(crc, buf, len);\n}\n\n} // namespace zlib\n\n#endif // MMKV_EMBED_ZLIB"
  },
  {
    "path": "Core/crc32/zlib/crc32.h",
    "content": "/* crc32.h -- tables for rapid CRC calculation\n * Generated automatically by crc32.c\n */\n\nlocal const z_crc_t FAR crc_table[TBLS][256] = {\n    {0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, 0x706af48fUL,\n     0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL,\n     0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL,\n     0xf3b97148UL, 0x84be41deUL, 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL,\n     0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,\n     0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL,\n     0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, 0x35b5a8faUL, 0x42b2986cUL,\n     0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL,\n     0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL,\n     0xcfba9599UL, 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,\n     0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, 0x01db7106UL,\n     0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL,\n     0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL,\n     0x91646c97UL, 0xe6635c01UL, 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL,\n     0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,\n     0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL,\n     0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, 0x4adfa541UL, 0x3dd895d7UL,\n     0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL,\n     0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL,\n     0xbe0b1010UL, 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,\n     0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, 0x2eb40d81UL,\n     0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL,\n     0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL,\n     0x0d6d6a3eUL, 0x7a6a5aa8UL, 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL,\n     0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,\n     0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL,\n     0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, 0xd6d6a3e8UL, 0xa1d1937eUL,\n     0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL,\n     0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL,\n     0x316e8eefUL, 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,\n     0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, 0xb2bd0b28UL,\n     0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL,\n     0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL,\n     0x72076785UL, 0x05005713UL, 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL,\n     0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,\n     0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL,\n     0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, 0x8f659effUL, 0xf862ae69UL,\n     0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL,\n     0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL,\n     0x40df0b66UL, 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,\n     0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, 0xcdd70693UL,\n     0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL,\n     0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL}};\n"
  },
  {
    "path": "Core/crc32/zlib/zconf.h",
    "content": "/* zconf.h -- configuration of the zlib compression library\n * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#ifndef ZCONF_H\n#define ZCONF_H\n\n#define Z_SOLO\n#define Z_NEED_DICT 2\n\n#if defined(__MSDOS__) && !defined(MSDOS)\n#define MSDOS\n#endif\n#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)\n#define OS2\n#endif\n#if defined(_WINDOWS) && !defined(WINDOWS)\n#define WINDOWS\n#endif\n#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)\n#ifndef WIN32\n#define WIN32\n#endif\n#endif\n#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)\n#if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)\n#ifndef SYS16BIT\n#define SYS16BIT\n#endif\n#endif\n#endif\n\n/*\n * Compile with -DMAXSEG_64K if the alloc function cannot allocate more\n * than 64k bytes at a time (needed on systems with 16-bit int).\n */\n#ifdef SYS16BIT\n#define MAXSEG_64K\n#endif\n#ifdef MSDOS\n#define UNALIGNED_OK\n#endif\n\n#ifdef __STDC_VERSION__\n#ifndef STDC\n#define STDC\n#endif\n#if __STDC_VERSION__ >= 199901L\n#ifndef STDC99\n#define STDC99\n#endif\n#endif\n#endif\n#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))\n#define STDC\n#endif\n#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))\n#define STDC\n#endif\n#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))\n#define STDC\n#endif\n#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))\n#define STDC\n#endif\n\n#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */\n#define STDC\n#endif\n\n#ifndef STDC\n#ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */\n#define const /* note: need a more gentle solution here */\n#endif\n#endif\n\n#if defined(ZLIB_CONST) && !defined(z_const)\n#define z_const const\n#else\n#define z_const\n#endif\n\n#ifdef Z_SOLO\ntypedef unsigned long z_size_t;\n#else\n#define z_longlong long long\n#if defined(NO_SIZE_T)\ntypedef unsigned NO_SIZE_T z_size_t;\n#elif defined(STDC)\n#include <stddef.h>\ntypedef size_t z_size_t;\n#else\ntypedef unsigned long z_size_t;\n#endif\n#undef z_longlong\n#endif\n\n/* Maximum value for memLevel in deflateInit2 */\n#ifndef MAX_MEM_LEVEL\n#ifdef MAXSEG_64K\n#define MAX_MEM_LEVEL 8\n#else\n#define MAX_MEM_LEVEL 9\n#endif\n#endif\n\n/* Maximum value for windowBits in deflateInit2 and inflateInit2.\n * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files\n * created by gzip. (Files created by minigzip can still be extracted by\n * gzip.)\n */\n#ifndef MAX_WBITS\n#define MAX_WBITS 15 /* 32K LZ77 window */\n#endif\n\n/* The memory requirements for deflate are (in bytes):\n            (1 << (windowBits+2)) +  (1 << (memLevel+9))\n that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)\n plus a few kilobytes for small objects. For example, if you want to reduce\n the default memory requirements from 256K to 128K, compile with\n     make CFLAGS=\"-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7\"\n Of course this will generally degrade compression (there's no free lunch).\n\n   The memory requirements for inflate are (in bytes) 1 << windowBits\n that is, 32K for windowBits=15 (default value) plus about 7 kilobytes\n for small objects.\n*/\n\n/* Type declarations */\n\n#ifndef OF /* function prototypes */\n#ifdef STDC\n#define OF(args) args\n#else\n#define OF(args) ()\n#endif\n#endif\n\n#ifndef Z_ARG /* function prototypes for stdarg */\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#define Z_ARG(args) args\n#else\n#define Z_ARG(args) ()\n#endif\n#endif\n\n/* The following definitions for FAR are needed only for MSDOS mixed\n * model programming (small or medium model with some far allocations).\n * This was tested only with MSC; for other MSDOS compilers you may have\n * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,\n * just define FAR to be empty.\n */\n#ifdef SYS16BIT\n#if defined(M_I86SM) || defined(M_I86MM)\n/* MSC small or medium model */\n#define SMALL_MEDIUM\n#ifdef _MSC_VER\n#define FAR _far\n#else\n#define FAR far\n#endif\n#endif\n#if (defined(__SMALL__) || defined(__MEDIUM__))\n/* Turbo C small or medium model */\n#define SMALL_MEDIUM\n#ifdef __BORLANDC__\n#define FAR _far\n#else\n#define FAR far\n#endif\n#endif\n#endif\n\n#if defined(WINDOWS) || defined(WIN32)\n/* If building or using zlib as a DLL, define ZLIB_DLL.\n    * This is not mandatory, but it offers a little performance increase.\n    */\n#ifdef ZLIB_DLL\n#if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))\n#ifdef ZLIB_INTERNAL\n#define ZEXTERN extern __declspec(dllexport)\n#else\n#define ZEXTERN extern __declspec(dllimport)\n#endif\n#endif\n#endif /* ZLIB_DLL */\n       /* If building or using zlib with the WINAPI/WINAPIV calling convention,\n    * define ZLIB_WINAPI.\n    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.\n    */\n#ifdef ZLIB_WINAPI\n#ifdef FAR\n#undef FAR\n#endif\n#include <windows.h>\n/* No need for _export, use ZLIB.DEF instead. */\n/* For complete Windows compatibility, use WINAPI, not __stdcall. */\n#define ZEXPORT WINAPI\n#ifdef WIN32\n#define ZEXPORTVA WINAPIV\n#else\n#define ZEXPORTVA FAR CDECL\n#endif\n#endif\n#endif\n\n#if defined(__BEOS__)\n#ifdef ZLIB_DLL\n#ifdef ZLIB_INTERNAL\n#define ZEXPORT __declspec(dllexport)\n#define ZEXPORTVA __declspec(dllexport)\n#else\n#define ZEXPORT __declspec(dllimport)\n#define ZEXPORTVA __declspec(dllimport)\n#endif\n#endif\n#endif\n\n#ifndef ZEXTERN\n#define ZEXTERN extern\n#endif\n#ifndef ZEXPORT\n#define ZEXPORT\n#endif\n#ifndef ZEXPORTVA\n#define ZEXPORTVA\n#endif\n\n#ifndef FAR\n#define FAR\n#endif\n\n#if !defined(__MACTYPES__)\ntypedef unsigned char Byte; /* 8 bits */\n#endif\ntypedef unsigned int uInt;   /* 16 bits or more */\ntypedef unsigned long uLong; /* 32 bits or more */\n\n#ifdef SMALL_MEDIUM\n/* Borland C/C++ and some old MSC versions ignore FAR inside typedef */\n#define Bytef Byte FAR\n#else\ntypedef Byte FAR Bytef;\n#endif\ntypedef char FAR charf;\ntypedef int FAR intf;\ntypedef uInt FAR uIntf;\ntypedef uLong FAR uLongf;\n\n#ifdef STDC\ntypedef void const *voidpc;\ntypedef void FAR *voidpf;\ntypedef void *voidp;\n#else\ntypedef Byte const *voidpc;\ntypedef Byte FAR *voidpf;\ntypedef Byte *voidp;\n#endif\n\n#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)\n#include <limits.h>\n#if (UINT_MAX == 0xffffffffUL)\n#define Z_U4 unsigned\n#elif (ULONG_MAX == 0xffffffffUL)\n#define Z_U4 unsigned long\n#elif (USHRT_MAX == 0xffffffffUL)\n#define Z_U4 unsigned short\n#endif\n#endif\n\n#ifdef Z_U4\ntypedef Z_U4 z_crc_t;\n#else\ntypedef unsigned long z_crc_t;\n#endif\n\n#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */\n#define Z_HAVE_UNISTD_H\n#endif\n\n#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */\n#define Z_HAVE_STDARG_H\n#endif\n\n#ifdef STDC\n#ifndef Z_SOLO\n#include <sys/types.h> /* for off_t */\n#endif\n#endif\n\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#ifndef Z_SOLO\n#include <stdarg.h> /* for va_list */\n#endif\n#endif\n\n#ifdef _WIN32\n#ifndef Z_SOLO\n#include <stddef.h> /* for wchar_t */\n#endif\n#endif\n\n/* a little trick to accommodate both \"#define _LARGEFILE64_SOURCE\" and\n * \"#define _LARGEFILE64_SOURCE 1\" as requesting 64-bit operations, (even\n * though the former does not conform to the LFS document), but considering\n * both \"#undef _LARGEFILE64_SOURCE\" and \"#define _LARGEFILE64_SOURCE 0\" as\n * equivalently requesting no 64-bit operations\n */\n#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1\n#undef _LARGEFILE64_SOURCE\n#endif\n\n#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)\n#define Z_HAVE_UNISTD_H\n#endif\n#ifndef Z_SOLO\n#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)\n#include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */\n#ifdef VMS\n#include <unixio.h> /* for off_t */\n#endif\n#ifndef z_off_t\n#define z_off_t off_t\n#endif\n#endif\n#endif\n\n#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE - 0\n#define Z_LFS64\n#endif\n\n#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)\n#define Z_LARGE64\n#endif\n\n#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS - 0 == 64 && defined(Z_LFS64)\n#define Z_WANT64\n#endif\n\n#if !defined(SEEK_SET) && !defined(Z_SOLO)\n#define SEEK_SET 0 /* Seek from beginning of file.  */\n#define SEEK_CUR 1 /* Seek from current position.  */\n#define SEEK_END 2 /* Set file pointer to EOF plus \"offset\" */\n#endif\n\n#ifndef z_off_t\n#define z_off_t long\n#endif\n\n#if !defined(_WIN32) && defined(Z_LARGE64)\n#define z_off64_t off64_t\n#else\n#if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)\n#define z_off64_t __int64\n#else\n#define z_off64_t z_off_t\n#endif\n#endif\n\n/* MVS linker does not support external names larger than 8 bytes */\n#if defined(__MVS__)\n#pragma map(deflateInit_, \"DEIN\")\n#pragma map(deflateInit2_, \"DEIN2\")\n#pragma map(deflateEnd, \"DEEND\")\n#pragma map(deflateBound, \"DEBND\")\n#pragma map(inflateInit_, \"ININ\")\n#pragma map(inflateInit2_, \"ININ2\")\n#pragma map(inflateEnd, \"INEND\")\n#pragma map(inflateSync, \"INSY\")\n#pragma map(inflateSetDictionary, \"INSEDI\")\n#pragma map(compressBound, \"CMBND\")\n#pragma map(inflate_table, \"INTABL\")\n#pragma map(inflate_fast, \"INFA\")\n#pragma map(inflate_copyright, \"INCOPY\")\n#endif\n\n#endif /* ZCONF_H */\n"
  },
  {
    "path": "Core/crc32/zlib/zutil.h",
    "content": "/* zutil.h -- internal interface and configuration of the compression library\n * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\n/* @(#) $Id$ */\n\n#ifndef ZUTIL_H\n#define ZUTIL_H\n\n#include \"zconf.h\"\n\n#ifndef local\n#define local static\n#endif\n/* since \"static\" is used to mean two completely different things in C, we\n   define \"local\" for the non-static meaning of \"static\", for readability\n   (compile with -Dlocal if your debugger can't find static symbols) */\n\n#endif /* ZUTIL_H */\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MMBuffer.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMBuffer.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MMKV.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMKV.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MMKVHandler.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMKVHandler.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MMKVLog.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMKVLog.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MMKVPredef.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMKVPredef.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MemoryFile.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MemoryFile.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/MiniPBCoder.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MiniPBCoder.h\"\n"
  },
  {
    "path": "Core/fakeinclude/MMKVCore/ScopedLock.hpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../ScopedLock.hpp\""
  },
  {
    "path": "Core/fakeinclude/MMKVCore/ThreadLock.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../ThreadLock.h\""
  },
  {
    "path": "Dockerfile",
    "content": "FROM gcc:11.5.0 AS builder\n\nRUN apt-get update && apt-get install cmake -y\n\nWORKDIR /MMKV\n\nCOPY . .\n\n## 设置环境变量\n#ENV CGO_ENABLED='1'\n#ENV CGO_CFLAGS='-static -O2 -g'\n#ENV CGO_LDFLAGS='-static -O2 -g'\n\nWORKDIR /MMKV/POSIX/golang\nRUN mkdir -p build && cd build && rm -rf ./* && \\\n    cmake .. -DCMAKE_INSTALL_PREFIX=. \\\n    -DCMAKE_BUILD_TYPE=Release && \\\n    make -j8 install\n\nRUN uname -a > /MMKV/POSIX/golang/build/tencent.com/build_info.txt 2>&1 && \\\n    gcc -v >> /MMKV/POSIX/golang/build/tencent.com/build_info.txt 2>&1 && \\\n    cmake --version >> /MMKV/POSIX/golang/build/tencent.com/build_info.txt 2>&1\n\nFROM scratch AS export-stage\nCOPY --from=builder /MMKV/POSIX/golang/build/tencent.com /tencent.com\n"
  },
  {
    "path": "LICENSE.TXT",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "MMKV.podspec",
    "content": "Pod::Spec.new do |s|\n\n  s.name         = \"MMKV\"\n  s.version      = \"2.4.0\"\n  s.summary      = \"MMKV is a cross-platform key-value storage framework developed by WeChat.\"\n\n  s.description  = <<-DESC\n                      The MMKV, for Objective-C.\n                      MMKV is an efficient, complete, easy-to-use mobile key-value storage framework used in the WeChat application.\n                      It can be a replacement for NSUserDefaults & SQLite.\n                   DESC\n\n  s.homepage     = \"https://github.com/Tencent/MMKV\"\n  s.license      = { :type => \"BSD 3-Clause\", :file => \"LICENSE.TXT\"}\n  s.author       = { \"guoling\" => \"guoling@tencent.com\" }\n\n  s.ios.deployment_target = \"13.0\"\n  s.osx.deployment_target = \"10.15\"\n  s.tvos.deployment_target = \"13.0\"\n  s.visionos.deployment_target = \"1.0\"\n\n  s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :tag => \"v#{s.version}\" }\n#s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :branch => \"dev_namespace\" }\n  s.source_files =  \"iOS/MMKV/MMKV\", \"iOS/MMKV/MMKV/*.{h,mm,hpp}\"\n  s.public_header_files = \"iOS/MMKV/MMKV/MMKV.h\", \"iOS/MMKV/MMKV/MMKVHandler.h\"\n  s.resource_bundles = {\n    \"MMKV_Privacy\" => [\"iOS/MMKV/MMKV/Resources/PrivacyInfo.xcprivacy\"]\n  }\n\n  s.framework    = \"CoreFoundation\"\n  s.libraries    = \"z\", \"c++\"\n  s.requires_arc = false\n  s.pod_target_xcconfig = {\n    \"CLANG_CXX_LANGUAGE_STANDARD\" => \"gnu++20\",\n    \"CLANG_CXX_LIBRARY\" => \"libc++\",\n    \"CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF\" => \"NO\",\n    \"DEFINES_MODULE\" => \"YES\",\n  }\n\n  s.dependency 'MMKVCore', '~> 2.4.0'\n\nend\n\n"
  },
  {
    "path": "MMKV.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:iOS/MMKVDemo/MMKVDemo.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:iOS/MMKV/MMKV.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "MMKV.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "MMKVAppExtension.podspec",
    "content": "Pod::Spec.new do |s|\n\n  s.name         = \"MMKVAppExtension\"\n  s.version      = \"2.4.0\"\n  s.summary      = \"MMKV is a cross-platform key-value storage framework developed by WeChat.\"\n  s.module_name  = \"MMKVAppExtension\"\n\n  s.description  = <<-DESC\n                      The MMKV for iOS App Extensions.\n                      MMKV is an efficient, complete, easy-to-use mobile key-value storage framework used in the WeChat application.\n                      It can be a replacement for NSUserDefaults & SQLite.\n                   DESC\n\n  s.homepage     = \"https://github.com/Tencent/MMKV\"\n  s.license      = { :type => \"BSD 3-Clause\", :file => \"LICENSE.TXT\"}\n  s.author       = { \"guoling\" => \"guoling@tencent.com\" }\n\n  s.ios.deployment_target = \"13.0\"\n\n  s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :tag => \"v#{s.version}\" }\n  s.source_files =  \"iOS/MMKV/MMKV\", \"iOS/MMKV/MMKV/*.{h,mm,hpp}\"\n  s.public_header_files = \"iOS/MMKV/MMKV/MMKV.h\", \"iOS/MMKV/MMKV/MMKVHandler.h\"\n  s.resource_bundles = {\n    \"MMKVAppExtension_Privacy\" => [\"iOS/MMKV/MMKV/Resources/PrivacyInfo.xcprivacy\"]\n  }\n\n  s.framework    = \"CoreFoundation\"\n  s.libraries    = \"z\", \"c++\"\n  s.requires_arc = false\n  s.pod_target_xcconfig = {\n    \"CLANG_CXX_LANGUAGE_STANDARD\" => \"gnu++20\",\n    \"CLANG_CXX_LIBRARY\" => \"libc++\",\n    \"CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF\" => \"NO\",\n    \"GCC_PREPROCESSOR_DEFINITIONS\" => \"MMKV_IOS_EXTENSION\",\n  }\n\n  s.dependency 'MMKVCore', '~> 2.4.0'\n\nend\n\n"
  },
  {
    "path": "MMKVCore.podspec",
    "content": "Pod::Spec.new do |s|\n\n  s.name         = \"MMKVCore\"\n  s.version      = \"2.4.0\"\n  s.summary      = \"MMKVCore for MMKV. MMKV is a cross-platform key-value storage framework developed by WeChat.\"\n\n  s.description  = <<-DESC\n                      Don't use this library directly. Use MMKV instead.\n                      MMKV is an efficient, complete, easy-to-use mobile key-value storage framework used in the WeChat application.\n                      It can be a replacement for NSUserDefaults & SQLite.\n                   DESC\n\n  s.homepage     = \"https://github.com/Tencent/MMKV\"\n  s.license      = { :type => \"BSD 3-Clause\", :file => \"LICENSE.TXT\"}\n  s.author       = { \"guoling\" => \"guoling@tencent.com\" }\n\n  s.ios.deployment_target = \"13.0\"\n  s.osx.deployment_target = \"10.15\"\n  s.tvos.deployment_target = \"13.0\"\n  s.watchos.deployment_target = \"6.0\"\n  s.visionos.deployment_target = \"1.0\"\n\n  s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :tag => \"v#{s.version}\" }\n#s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :branch => \"dev_namespace\" }\n\n  s.source_files = \"Core\", \"Core/*.{h,cpp,hpp}\", \"Core/aes/*\", \"Core/aes/openssl/*\", \"Core/crc32/*.h\"\n  s.public_header_files = \"Core/MMBuffer.h\", \"Core/MMKV.h\", \"Core/MMKVHandler.h\", \"Core/MMKVLog.h\", \"Core/MMKVPredef.h\", \"Core/MiniPBCoder.h\", \"Core/PBUtility.h\", \"Core/ScopedLock.hpp\", \"Core/ThreadLock.h\", \"Core/aes/openssl/openssl_md5.h\", \"Core/aes/openssl/openssl_opensslconf.h\"\n  s.compiler_flags = '-x objective-c++'\n\n  s.requires_arc = ['Core/MemoryFile.cpp', 'Core/ThreadLock.cpp', 'Core/InterProcessLock.cpp', 'Core/MMKVLog.cpp', 'Core/PBUtility.cpp', 'Core/MemoryFile_OSX.cpp', 'aes/openssl/openssl_cfb128.cpp', 'aes/openssl/openssl_aes_core.cpp', 'aes/openssl/openssl_md5_one.cpp', 'aes/openssl/openssl_md5_dgst.cpp', 'aes/AESCrypt.cpp']\n\n  s.framework    = \"CoreFoundation\"\n  s.ios.frameworks = \"UIKit\"\n  s.libraries    = \"z\", \"c++\"\n  s.pod_target_xcconfig = {\n    \"CLANG_CXX_LANGUAGE_STANDARD\" => \"gnu++20\",\n    \"CLANG_CXX_LIBRARY\" => \"libc++\",\n    \"CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF\" => \"NO\",\n    \"DEFINES_MODULE\" => \"YES\",\n    'RELEASE' => {\n      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) NDEBUG=1'\n    },\n    'EXCLUDED_ARCHS[sdk=watchos*]' => 'armv7k',\n  }\n\nend\n\n"
  },
  {
    "path": "MMKVWatchExtension.podspec",
    "content": "Pod::Spec.new do |s|\n\n  s.name         = \"MMKVWatchExtension\"\n  s.version      = \"2.4.0\"\n  s.summary      = \"MMKV is a cross-platform key-value storage framework developed by WeChat.\"\n  s.module_name  = \"MMKVWatchExtension\"\n\n  s.description  = <<-DESC\n                      The MMKV for WatchOS App Extensions.\n                      MMKV is an efficient, complete, easy-to-use mobile key-value storage framework used in the WeChat application.\n                      It can be a replacement for NSUserDefaults & SQLite.\n                   DESC\n\n  s.homepage     = \"https://github.com/Tencent/MMKV\"\n  s.license      = { :type => \"BSD 3-Clause\", :file => \"LICENSE.TXT\"}\n  s.author       = { \"guoling\" => \"guoling@tencent.com\" }\n\n  s.watchos.deployment_target = \"6.0\"\n\n  s.source       = { :git => \"https://github.com/Tencent/MMKV.git\", :tag => \"v#{s.version}\" }\n  s.source_files =  \"iOS/MMKV/MMKV\", \"iOS/MMKV/MMKV/*.{h,mm,hpp}\"\n  s.public_header_files = \"iOS/MMKV/MMKV/MMKV.h\", \"iOS/MMKV/MMKV/MMKVHandler.h\"\n  s.resource_bundles = {\n    \"MMKVWatchExtension_Privacy\" => [\"iOS/MMKV/MMKV/Resources/PrivacyInfo.xcprivacy\"]\n  }\n\n  s.framework    = \"CoreFoundation\"\n  s.libraries    = \"z\", \"c++\"\n  s.requires_arc = false\n  s.pod_target_xcconfig = {\n    \"CLANG_CXX_LANGUAGE_STANDARD\" => \"gnu++20\",\n    \"CLANG_CXX_LIBRARY\" => \"libc++\",\n    \"CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF\" => \"NO\",\n    \"GCC_PREPROCESSOR_DEFINITIONS\" => \"MMKV_IOS_EXTENSION\",\n    'EXCLUDED_ARCHS[sdk=watchos*]' => 'armv7k',\n  }\n\n  s.dependency 'MMKVCore', '~> 2.4.0'\n\nend\n\n"
  },
  {
    "path": "Makefile",
    "content": "format_code:\n\tpython Script/formatCode.py\n\ngolang:\n\tif [ -z \"$(PLATFORM)\" ]; then \\\n\t\tdocker buildx build --output ./output . ;\\\n\telse \\\n\t\tdocker buildx build --platform=$(PLATFORM) --output ./output . ;\\\n\tfi\n\n"
  },
  {
    "path": "OpenHarmony/.gitignore",
    "content": "/node_modules\n/oh_modules\n/local.properties\n/.idea\n**/build\n/.hvigor\n.cxx\n/.clangd\n/.clang-format\n/.clang-tidy\n**/.test\n/.appanalyzer\n/codesign\nMMKV/include\noh-package-lock.json5\n"
  },
  {
    "path": "OpenHarmony/AppScope/app.json5",
    "content": "{\n  \"app\": {\n    \"bundleName\": \"com.tencent.mmkvdemo\",\n    \"vendor\": \"example\",\n    \"versionCode\": 1000000,\n    \"versionName\": \"1.0.0\",\n    \"icon\": \"$media:app_icon\",\n    \"label\": \"$string:app_name\"\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/AppScope/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"app_name\",\n      \"value\": \"MMKVDemo\"\n    }\n  ]\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/.gitignore",
    "content": "/node_modules\n/oh_modules\n/.preview\n/build\n/.cxx\n/.test"
  },
  {
    "path": "OpenHarmony/MMKV/BuildProfile.ets",
    "content": "/**\n * Use these variables when you tailor your ArkTS code. They must be of the const type.\n */\nexport const HAR_VERSION = '2.3.0';\nexport const BUILD_MODE_NAME = 'release';\nexport const DEBUG = false;\nexport const TARGET_NAME = 'default';\n\n/**\n * BuildProfile Class is used only for compatibility purposes.\n */\nexport default class BuildProfile { \n\tstatic readonly HAR_VERSION = HAR_VERSION;\n\tstatic readonly BUILD_MODE_NAME = BUILD_MODE_NAME;\n\tstatic readonly DEBUG = DEBUG;\n\tstatic readonly TARGET_NAME = TARGET_NAME;\n}"
  },
  {
    "path": "OpenHarmony/MMKV/CHANGELOG.md",
    "content": "# MMKV for HarmonyOS NEXT Change Log\r\n## v2.4.0 / 2026-03-18\r\n* **Feature:** Refactored the callback system into a unified `MMKVHandler` interface. Added `onMMKVContentLoadSuccessfully` callback.\r\n* **Feature:** Added `MMKVConfig` for all-in-one instance configuration.\r\n* **Feature:** Added `defaultMMKVWithConfig()` for creating the default instance with full configuration.\r\n* **Fix:** Robust check on encryption mode.\r\n* Merge ashmem size with `expectedCapacity`.\r\n\r\n## v2.3.0 / 2025-12-03\r\n\r\nThis release introduces **AES-256 encryption** for enhanced security.\r\n\r\n* **Feature:** Added **AES-256 encryption** functionality. To upgrade an existing encrypted MMKV instance to AES-256, first load it using the old key. Then, call the `reKey()` method with the new key and set the `aes256` parameter to `true`, e.g., `reKey(newKey, true)`. After this, you should use the new key for all future loads of this instance.\r\n* **Fix:** Resolved a crash that occurred when loading an empty file in `ReadOnly` mode.\r\n* **Fix:** Added protection against the `weakly_canonical()` exception caused by an invalid file path.\r\n* **Fix:** Fixed an issue where the file size could change during multi-process loading.\r\n* **Fix:** Corrected a bug where a single key could be overridden incorrectly when upgrading from a v1.1.x version.\r\n\r\n## v2.2.4 / 2025-09-25\r\n* Improve the performance of MMBuffer a little bit in some cases.\r\n\r\n## v2.2.3 / 2025-08-20\r\n* Keep up with Core library v2.2.3.\r\n\r\n## v2.2.2 / 2025-05-08\r\nThis is a hot fix version mainly **for Android/Linux platforms**. It’s highly recommended for v2.2.0~v2.2.1 users.\r\n* Improve file lock consistency for Mayfly FD MMKV instances.\r\n\r\n## v2.2.1 / 2025-04-25\r\n* Add `importFrom()`.\r\n\r\n## v2.2.0 / 2025-04-24\r\nWe introduce the **Mayfly FD** (short-lived file descriptor) enhancement, reducing MMKV's fd footprint by half and more. For a single-process mode MMKV instance, the fd footprint is reduced to zero (except Android/OHOS, details below). For a multi-process mode MMKV instance, the fd footprint is reduced by half, for we still need a long-lived fd to inter-process lock the shared memory.\r\n* Add **Mayfly FD** (short-lived file descriptor) enhancement. Reduce the fd footprint by half. For a single-process mode MMKV instance, we still need a long-lived fd to support the **legacy name upgrading**. In the far future, when all legacy names have been upgraded, we might drop the long-lived fd like other platforms.\r\n* Improve multi-process access efficiency by about 20%.\r\n* Add `checkExist()` to check if a MMKV instance exists on disk.\r\n* Drop deprecated armv7 AES hardware acceleration.\r\n* Drop `checkProcessMode()`, it’s never been used.\r\n* Improve obfuscation configuration with relative path.\r\n\r\n## v2.1.0 / 2025-02-18\r\n* **Breaking change**: Migrate legacy MMKV in a custom directory to normal MMKV. Historically Android/OHOS mistakenly use mmapKey as mmapID, which will be problematic with the `NameSpace` feature. Starting from v2.1.0, MMKV will try to migrate them back to normal when possible.  \r\n  It's highly recommended that you **upgrade to v2.0.2 first** with **forward support** of normal MMKV in a custom directory.\r\n* Supports using MMKV directly in C++ code.\r\n* Improve inter-process locking by using `F_OFD_SETLK` instead of `F_SETLK`.\r\n* Improve directory creation on `ReadOnly` mode.\r\n* Add *experimental* protection from bad disk records of MMKV files.\r\n* Fix FileLock not being unlocked on destruction.\r\n\r\n## v2.0.2 / 2024-12-27\r\n* Obfuscation fully supported.\r\n* Use atomic file rename on OHOS.\r\n* Add forward support for the correct filename with a custom root path.\r\n\r\n## v2.0.1 / 2024-11-12\r\n* Fix a bug that MMKV might become dead-locked for other threads after `decodeStringSet()` / `decodeNumberSet` / `decodeBoolSet` or decoding `TypedArray`.\r\n\r\n## v2.0.0 / 2024-10-21\r\n* Support obfuscation. For the time being, you will have to manually copy the content of MMKV's [consumer-rules.txt](https://github.com/Tencent/MMKV/blob/master/OpenHarmony/MMKV/consumer-rules.txt) into your App's obfuscation-rules.txt.\r\n* Support read-only mode.\r\n* Add add log/error/content-change callback for Flutter & ArtTS\r\n\r\n## v1.3.9 / 2024-07-26\r\n* Fix a data corruption bug on an encrypted MMKV with only one key value stored.\r\n* Make encryption more resilient from brute force cracking.\r\n\r\n## v1.3.7 / 2024-07-12\r\n* Add support of Flutter on HarmonyOS NEXT.\r\n\r\n## v1.3.6 / 2024-07-05\r\n* Fix a bug that a `String` value might get truncated on encoding.\r\n* MMKV returns `undefined` when a key does not exist, previously a default value of the type (`false` for `boolean`, `0` for `number`, etc) is returned.\r\n* Add the feature to encode/decode a `float` value.\r\n* Add the feature to encode/decode a `TypedArray` value.\r\n* Support encoding a part of an `ArrayBuffer`.\r\n\r\n## v1.3.5 / 2024-04-24\r\nFix a bug in `MMKV.initialize()` that fails to handle the optional parameter _logLevel_.\r\n\r\n## v1.3.4 / 2024-04-17\r\n\r\nThe first official HarmonyOS NEXT package of MMKV. Most things actually work!\r\n"
  },
  {
    "path": "OpenHarmony/MMKV/Index.ets",
    "content": "export { MMKV } from \"./src/main/ets/utils/MMKV\";\nexport { MMKVLogLevel } from \"./src/main/ets/utils/MMKVLogLevel\";\nexport { NativeBuffer } from \"./src/main/ets/utils/NativeBuffer\";\nexport { MMKVHandler, MMKVRecoverStrategic } from \"./src/main/ets/utils/MMKVHandler\";\nexport { MMKVConfig } from \"./src/main/ets/utils/MMKVConfig\";\n"
  },
  {
    "path": "OpenHarmony/MMKV/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "OpenHarmony/MMKV/README.md",
    "content": "[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)\r\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)\r\n[![Release Version](https://img.shields.io/badge/release-2.4.0-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)\r\n[![Platform](https://img.shields.io/badge/Platform-%20HarmonyOS%20NEXT-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)\r\n\r\nMMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's now available on **HarmonyOS NEXT**.\r\n\r\n# MMKV for HarmonyOS NEXT\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of native platform to achieve best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `flush` calls needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.\r\n  * **About 600K in binary size**: MMKV adds about 600K per architecture on App size, and much less when zipped (HAR/HAP).\r\n\r\n\r\n## Getting Started\r\n\r\n### Prerequisites\r\n\r\n* Apps using MMKV can target: HarmonyOS Next (3.0.0.13) or later (API 12).\r\n* ARM64 & x86_64 architecture.\r\n* DevEco Studio NEXT Developer Beta1 (5.0.3.100) or later.\r\n\r\n### Installation via OHPM:\r\nThis is the fastest and recommended way to add MMKV to your project.\r\n```bash\r\nohpm install @tencent/mmkv\r\n```\r\nOr, you can add it to your project manually.\r\n* Add the following lines to `oh-package.json5` on your app module.\r\n\r\n  ```json\r\n  \"dependencies\": {\r\n      \"@tencent/mmkv\": \"~2.4.0\",\r\n  }\r\n  ```\r\n* Then run\r\n \r\n  ```bash\r\n  ohpm install\r\n  ```\r\n\r\nFor additional installation methods, checkout the [wiki](https://github.com/Tencent/MMKV/wiki/ohos_setup#installation).\r\n\r\n### Setup\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.  \r\nSetup MMKV on App startup, say your `EntryAbility.onCreate()` function, add these lines:\r\n\r\n```js\r\nimport { MMKV } from '@tencent/mmkv';\r\n\r\nexport default class EntryAbility extends UIAbility {\r\n  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {\r\n    let appCtx = this.context.getApplicationContext();\r\n    let mmkvRootDir = MMKV.initialize(appCtx);\r\n    console.info('mmkv rootDir: ', mmkvRootDir);\r\n    ……\r\n  }\r\n```\r\n\r\n### CRUD Operations\r\n\r\n* MMKV has a global instance, that can be used directly:\r\n\r\n    ```js\r\n    import { MMKV } from '@tencent/mmkv';\r\n        \r\n    let mmkv = MMKV.defaultMMKV();\r\n    mmkv.encodeBool('bool', true);\r\n    console.info('bool = ', mmkv.decodeBool('bool'));\r\n    \r\n    mmkv.encodeInt32('int32', Math.pow(2, 31) - 1);\r\n    console.info('max int32 = ', mmkv.decodeInt32('int32'));\r\n    \r\n    mmkv.encodeInt64('int', BigInt(2**63) - BigInt(1));\r\n    console.info('max int64 = ', mmkv.decodeInt64('int'));\r\n    \r\n    let str: string = 'Hello OpenHarmony from MMKV';\r\n    mmkv.encodeString('string', str);\r\n    console.info('string = ', mmkv.decodeString('string'));\r\n\r\n    let arrayBuffer: ArrayBuffer = StringToArrayBuffer('Hello OpenHarmony from MMKV with bytes');\r\n    mmkv.encodeBytes('bytes', arrayBuffer);\r\n    let bytes = mmkv.decodeBytes('bytes');\r\n    console.info('bytes = ', ArrayBufferToString(bytes));\r\n\r\n    let arr = new Uint8Array([0, 255, 1, 255]);\r\n    mmkv.encodeTypedArray('uint8-array', arr);\r\n    let newUI8Arr = kv.decodeUint8Array('uint8-array');\r\n    console.info('uint8-array = ', newUI8Arr);\r\n    ```\r\n\r\n    As you can see, MMKV is quite easy to use.\r\n    \r\n* **Deleting & Querying**:\r\n\r\n    ```js\r\n    mmkv.removeValueForKey('bool');\r\n    console.info('contains \"bool\"', mmkv.containsKey('bool'));\r\n\r\n    mmkv.removeValuesForKeys(['int32', 'int']);\r\n    console.info('all keys: ', mmkv.allKeys().join());\r\n    ```\r\n\r\n* If different modules/logic need **isolated storage**, you can also create your own MMKV instance separately:\r\n\r\n    ```js\r\n    var mmkv = MMKV.mmkvWithID('test');\r\n    mmkv.encodeBool('bool', true);\r\n    console.info('bool = ', mmkv.decodeBool('bool'));\r\n    ```\r\n\r\n* If **multi-process accessing** is needed，you can set `MMKV.MULTI_PROCESS_MODE` on MMKV initialization:\r\n\r\n    ```dart\r\n    var mmkv = MMKV.mmkvWithID('test-multi-process', MMKV.MULTI_PROCESS_MODE);\r\n    mmkv.encodeBool('bool', true);\r\n    console.info('bool = ', mmkv.decodeBool('bool'));\r\n    ```\r\n\r\n### Supported Types\r\n* Primitive Types:\r\n  - `boolean, number, bigint, string`\r\n\r\n* Classes & Collections:\r\n  - `boolean[], number[], string[], ArrayBuffer, TypedArray`\r\n\r\n### Log\r\n\r\n* By default, MMKV prints log to hilog, which is not convenient for diagnosing online issues. \r\nYou can setup MMKV **log redirecting** on App startup on the **native** interface of MMKV. \r\nCheckout how to do it on [C++](https://github.com/Tencent/MMKV/wiki/posix_tutorial#logs).\r\nDue to the current limitation of NAPI runtime, we **can't efficiently** redirect log to the JavaScript side.\r\n\r\n* You can turn off MMKV's logging once and for all on initialization (which we strongly disrecommend).  \r\n\r\n    ```js\r\n    import { MMKV, MMKVLogLevel } from '@tencent/mmkv';\r\n\r\n    MMKV.initialize(appCtx, MMKVLogLevel.None);\r\n    ```\r\n\r\n### Encryption\r\n* By default MMKV stores all key-values in plain text on file, relying on Android's/iOS's sandbox to make sure the file is encrypted. Should you worry about information leaking, you can choose to encrypt MMKV.\r\n\r\n    ```js\r\n    let encryptKey = 'MyEncryptKey';\r\n    let mmkv = MMKV.mmkvWithID('test-encryption', MMKV.SINGLE_PROCESS_MODE, encryptKey);\r\n    ```\r\n\r\n* You can change the encryption key later as you like. You can also change an existing MMKV instance from encrypted to unencrypted, or vice versa.\r\n\r\n    ```js\r\n    // an unencrypted MMKV instance\r\n    let mmkv = MMKV.mmkvWithID('test-encryption');\r\n\r\n    // change from unencrypted to encrypted with AES-128 key length\r\n    mmkv.reKey('Key_seq_1');\r\n\r\n    // change encryption key with AES-258 key length\r\n    mmkv.reKey('Key_Seq_Very_Looooooooong', true);\r\n\r\n    // change from encrypted to unencrypted\r\n    kmmkv.reKey();\r\n    ```\r\n \r\n### Customize location\r\n* By default, MMKV stores file inside `$(FilesDir)/mmkv/`. You can customize MMKV's **root directory** on App startup:\r\n\r\n    ```js\r\n    let appCtx = this.context.getApplicationContext();\r\n    let rootDir = appCtx.filesDir + '/mmkv_2';\r\n    let cacheDir = appCtx.cacheDir;\r\n    MMKV.initializeWithPath(rootDir, cacheDir);\r\n    ```\r\n\r\n* You can even customize any MMKV instance's location:\r\n\r\n    ```js\r\n    let appCtx = this.context.getApplicationContext();\r\n    let rootDir = appCtx.filesDir + '/mmkv_3';\r\n    var mmkv = MMKV.mmkvWithID('testCustomDir', MMKV.SINGLE_PROCESS_MODE, null, rootDir);\r\n    ```\r\n  **Note:** It's recommended to store MMKV files **inside** your App's sandbox path. **DO NOT** store them on external storage(aka SD card).\r\n\r\n### Additional docs\r\nFor additional documents, checkout the [wiki](https://github.com/Tencent/MMKV/wiki/ohos_setup).\r\n\r\n## License\r\nMMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT).\r\n\r\n## Change Log\r\nCheck out the [CHANGELOG.md](https://github.com/Tencent/MMKV/blob/master/OpenHarmony/MMKV/CHANGELOG.md) for details of change history.\r\n\r\n## Contributing\r\n\r\nIf you are interested in contributing, check out the [CONTRIBUTING.md](https://github.com/Tencent/MMKV/blob/master/CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution).\r\n\r\nTo give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](https://github.com/Tencent/MMKV/blob/master/CODE_OF_CONDUCT.md).\r\n\r\n## FAQ & Feedback\r\nCheck out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).\r\n"
  },
  {
    "path": "OpenHarmony/MMKV/build-profile.json5",
    "content": "{\n  \"apiType\": \"stageMode\",\n  \"buildOption\": {\n    \"externalNativeOptions\": {\n      \"path\": \"./src/main/cpp/CMakeLists.txt\",\n      \"arguments\": \"-DOHOS_STL=c++_static\",\n      \"cppFlags\": \"-DMMKV_STL_SHARED=0 -fvisibility=hidden -funwind-tables -fasynchronous-unwind-tables -O2 -g\",\n      \"abiFilters\": [\"arm64-v8a\", \"x86_64\"],\n    }\n  },\n  \"buildOptionSet\": [\n    {\n      \"name\": \"release\",\n      \"arkOptions\": {\n        \"obfuscation\": {\n          \"ruleOptions\": {\n            \"enable\": false,\n            \"files\": [\n              \"./obfuscation-rules.txt\"\n            ]\n          },\n          \"consumerFiles\": [\n            \"./consumer-rules.txt\"\n          ]\n        },\n      },\n      \"nativeLib\": {\n        \"debugSymbol\": {\n          \"strip\": false,\n          \"exclude\": []\n        }\n      }\n    },\n  ],\n  \"targets\": [\n    {\n      \"name\": \"default\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/MMKV/consumer-rules.txt",
    "content": "# Define project specific obfuscation rules here.\n# You can include the obfuscation configuration files in the current module's build-profile.json5.\n#\n# For more details, see\n#   https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md\n\n# Obfuscation options:\n# -disable-obfuscation: disable all obfuscations\n# -enable-property-obfuscation: obfuscate the property names\n# -enable-toplevel-obfuscation: obfuscate the names in the global scope\n# -compact: remove unnecessary blank spaces and all line feeds\n# -remove-log: remove all console.* statements\n# -print-namecache: print the name cache that contains the mapping from the old names to new names\n# -apply-namecache: reuse the given cache file\n\n# Keep options:\n# -keep-property-name: specifies property names that you want to keep\n# -keep-global-name: specifies names that you want to keep in the global scope\n\n-keep-global-name\nMMKV\nMMKVHandler\nMMKVRecoverStrategic\nMMKVLogLevel\nNativeBuffer\ngetObjKeys\nNameSpace\nMMKVConfig\n\n-keep-property-name\n*_MODE\ninitializeWithPath\ndefaultMMKV\ninitData\nwantLogRedirect\nmmkvLog\nonMMKV*\nwantContentChangeNotification\nonContentChangedByOuterProcess\nLevel*\npointer\nsize\nbackedUpMMKVWithID\ndecodeFloat32Array\ndecodeFloat64Array\ndecodeInt16Array\ndecodeInt8Array\ndecodeUint16Array\ndecodeUint32Array\ndecodeUint64Array\ndecodeUint8Array\ndecodeUint8ClampedArray\ndefaultMMKV\nencodeBytesPart\nencodeTypedArray\nimportFromPreferences\nmmkvWithAshmemID\nrootDir\nmode\naes256\ncryptKey\nrootPath\nexpectedCapacity\nenableKeyExpire\nexpiredInSeconds\nenableCompareBeforeSet\nrecover\nitemSizeLimit\n\n-keep-dts\n/Users/lingol/Developer/mmkv/OpenHarmony/MMKV/src/main/cpp/types/libmmkv/\n\n"
  },
  {
    "path": "OpenHarmony/MMKV/example/Index.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { MMKV, NativeBuffer } from '@tencent/mmkv';\nimport util from '@ohos.util';\nimport dataPreferences from '@ohos.data.preferences';\nimport { GlobalContainer } from './Util'\n\n@Entry\n@Component\nstruct Index {\n  @State message: string = 'Hello, world!';\n\n  build() {\n    Row() {\n      Column({ space: 20 }) {\n        Button('Functionality Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.functionalTest('test', MMKV.MULTI_PROCESS_MODE, 'encrypt_key');\n          })\n        Button('Encryption Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testReKey();\n          })\n        Button('Import Preferences Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testImportPreferences();\n          })\n        Button('Backup & Restore Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testBackup();\n            this.testRestore();\n          })\n        Button('Auto Expiration Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testAutoExpire();\n          })\n        Button('Compare-Before-Set Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testCompareBeforeSet()\n          })\n        Button('Ashmem Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testAshmem()\n          })\n        Text('mmkv version: ' + MMKV.version)\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n        Text('pagesize: ' + MMKV.pageSize.toString())\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n        Text('rootDir: ' + MMKV.rootDir)\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n      }\n      .width('100%')\n    }\n    .height('100%')\n  }\n\n  functionalTest(mmapID: string, mode?: number, cryptKey?: string, rootPath?: string, expectedCapacity?: bigint) {\n    let kv = MMKV.mmkvWithID(mmapID, mode, cryptKey, rootPath, expectedCapacity);\n    hilog.info(0, 'mmkvdemo', 'mmapID = %{public}s, cryptKey = %{public}s', kv.mmapID, kv.cryptKey);\n\n    kv.encodeBool('bool', true);\n    hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n\n    let pow31 = Math.pow(2, 31);\n    kv.encodeInt32('int32', pow31 - 1);\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n\n    kv.encodeInt32('int32', -pow31);\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n    let pow32 = Math.pow(2, 32);\n    kv.encodeUInt32('uint32', pow32 - 1);\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n\n    kv.encodeUInt32('uint32', 0);\n    hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n    let pow63 = BigInt(2**63);\n    kv.encodeInt64('int64', pow63 - BigInt(1));\n    hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n\n    kv.encodeInt64('int64', -pow63);\n    hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n    let pow64 = BigInt(2**64);\n    kv.encodeUInt64('uint64', pow64 - BigInt(1));\n    hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n\n    kv.encodeUInt64('uint64', BigInt(0));\n    hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n    kv.encodeDouble('double', Number.MAX_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n\n    kv.encodeDouble('double', Number.MIN_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n    kv.encodeString('string', 'Hello world to OpenHarmony!');\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n    let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n    kv.encodeBytes('bytes', arrayBuffer);\n    let bytes = kv.decodeBytes('bytes');\n    hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n    hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n      kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n    let strArr: string[] = ['abc', 'defg', 'hijk'];\n    kv.encodeStringSet('string-set', strArr);\n    let newStrArr = kv.decodeStringSet('string-set');\n    hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n    kv.encodeStringSet('empty-string-set', []);\n    let emptyStrArr = kv.decodeStringSet('empty-string-set');\n    hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n    let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n    hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n    hilog.info(0, 'mmkvdemo', 'contains bytes = %{public}s, contains bytes_not_exit = %{public}s',\n      kv.containsKey('bytes'), kv.containsKey('bytes_not_exit'));\n\n    kv.removeValueForKey('bytes');\n    hilog.info(0, 'mmkvdemo', 'after remove, contains bytes = %{public}s', kv.containsKey('bytes'));\n\n    hilog.info(0, 'mmkvdemo', 'total count = %{public}i, total size = %{public}i, actual size = %{public}i',\n      kv.count(), kv.totalSize(), kv.actualSize());\n\n    kv.lock();\n    kv.trim();\n    kv.sync(true);\n    kv.unlock();\n    hilog.info(0, 'mmkvdemo', 'tryLock = %{public}s', kv.tryLock());\n\n    kv.clearMemoryCache();\n    hilog.info(0, 'mmkvdemo', 'allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.removeValuesForKeys(['bool', 'int32']);\n    hilog.info(0, 'mmkvdemo', 'remove \"bool\" & \"int32\", allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.clearAll();\n    hilog.info(0, 'mmkvdemo', 'clearAll(), allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.close();\n    hilog.info(0, 'mmkvdemo', 'isFileValue %{public}s', MMKV.isFileValid(mmapID, rootPath));\n    hilog.info(0, 'mmkvdemo', 'remove storage %{public}s', MMKV.removeStorage(mmapID, rootPath));\n  }\n\n  testMMKV(mmapID: string, decodeOnly: boolean, cryptKey?: string, rootPath?: string): MMKV {\n    let kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, rootPath);\n\n    this.testOneMMKV(kv, decodeOnly);\n    hilog.info(0, 'mmkvdemo', 'isFileValue %{public}s', MMKV.isFileValid(kv.mmapID, rootPath));\n\n    return kv;\n  }\n\n  testOneMMKV(kv: MMKV, decodeOnly: boolean) {\n    hilog.info(0, 'mmkvdemo', 'mmapID = %{public}s, cryptKey = %{public}s', kv.mmapID, kv.cryptKey);\n\n    if (!decodeOnly) {\n      kv.encodeBool('bool', true);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n\n    let pow31 = Math.pow(2, 31);\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', pow31 - 1);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', -pow31);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n    let pow32 = Math.pow(2, 32);\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', pow32 - 1);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', 0);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n    let pow63 = BigInt(2**63);\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', pow63 - BigInt(1));\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', -pow63);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n    let pow64 = BigInt(2**64);\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', pow64 - BigInt(1));\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', BigInt(0));\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MAX_VALUE);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MIN_VALUE);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeString('string', 'Hello world to OpenHarmony!');\n    }\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n    if (!decodeOnly) {\n      let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n      kv.encodeBytes('bytes', arrayBuffer);\n    }\n    let bytes = kv.decodeBytes('bytes');\n    hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n    hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n      kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n    let sizeNeeded = kv.getValueSize('bytes', true);\n    let nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);\n    if (nativeBuffer) {\n      let size = kv.writeValueToNativeBuffer('bytes', nativeBuffer);\n      hilog.info(0, 'mmkvdemo', 'NativeBuffer: size Needed = %{public}d,  written size = %{public}d', sizeNeeded, size);\n      MMKV.destroyNativeBuffer(nativeBuffer);\n    }\n\n    if (!decodeOnly) {\n      let strArr: string[] = ['abc', 'defg', 'hijk'];\n      kv.encodeStringSet('string-set', strArr);\n    }\n    let newStrArr = kv.decodeStringSet('string-set');\n    hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n    if (!decodeOnly) {\n      kv.encodeStringSet('empty-string-set', []);\n    }\n    let emptyStrArr = kv.decodeStringSet('empty-string-set');\n    hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n    let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n    hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n    hilog.info(0, 'mmkvdemo', 'contains bool = %{public}s, contains bool_not_exit = %{public}s',\n      kv.containsKey('bool'), kv.containsKey('bool_not_exit'));\n\n    kv.removeValueForKey('bool');\n    hilog.info(0, 'mmkvdemo', 'after remove, contains bool = %{public}s', kv.containsKey('bool'));\n\n    hilog.info(0, 'mmkvdemo', 'total count = %{public}i, total size = %{public}i, actual size = %{public}i',\n      kv.count(), kv.totalSize(), kv.actualSize());\n\n    // kv.lock();\n    // kv.trim();\n    // kv.sync(true);\n    // kv.unlock();\n    // hilog.info(0, 'mmkvdemo', 'tryLock = %{public}s', kv.tryLock());\n\n    kv.clearMemoryCache();\n    hilog.info(0, 'mmkvdemo', 'allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.removeValuesForKeys(['int32', 'uint32']);\n    hilog.info(0, 'mmkvdemo', 'remove \"int32\" & \"uint32\", allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    // kv.clearAll();\n    // hilog.info(0, 'mmkvdemo', 'clearAll(), allKeys = %{public}s', StringArrayToString(kv.allKeys()));\n\n    // kv.close();\n  }\n\n  testReKey() {\n    let mmapID: string = 'test/AES_reKey1';\n    let kv = this.testMMKV(mmapID, false);\n\n    kv.reKey('Key_seq_1');\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true, 'Key_seq_1');\n\n    kv.reKey('Key_seq_2');\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true, 'Key_seq_2');\n\n    kv.reKey();\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true);\n  }\n\n  testImportPreferences() {\n    let context = GlobalContainer.getObject('context') as Context\n    let preferences = dataPreferences.getPreferencesSync(context, { name: 'default' });\n    preferences.putSync('bool', true);\n    let pow31 = Math.pow(2, 31);\n    preferences.putSync('int32_max', pow31 - 1);\n    preferences.putSync('int32_min', -pow31);\n    let pow32 = Math.pow(2, 32);\n    preferences.putSync('uint32', pow32 - 1);\n    preferences.putSync('double_max', Number.MAX_VALUE);\n    preferences.putSync('double_min', Number.MIN_VALUE);\n    preferences.putSync('string', 'hello, world preferenes');\n    preferences.putSync('string_array', ['hello', 'world', 'preferenes']);\n    preferences.putSync('empty_string_array', []);\n    preferences.putSync('number_array', [pow31 - 1, -pow31, pow32, Number.MAX_VALUE, Number.MIN_VALUE]);\n    preferences.putSync('bool_array', [true, false, true, false]);\n    let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n    preferences.putSync('uint8_array', new Uint8Array(arrayBuffer));\n    preferences.flush();\n\n    let kv = MMKV.mmkvWithID('imported');\n    let count = kv.importFromPreferences(preferences);\n    hilog.info(0, 'mmkvdemo', 'importFromPreferences count %{public}i', count);\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeDouble('int32_max'));\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeDouble('int32_min'));\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeDouble('uint32'));\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double_max'));\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double_min'));\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n    hilog.info(0, 'mmkvdemo', 'decode string array = %{public}s', ArrayToString(kv.decodeStringSet('string_array')));\n    hilog.info(0, 'mmkvdemo', 'decode empty string array = %{public}s', ArrayToString(kv.decodeStringSet('empty_string_array')));\n    hilog.info(0, 'mmkvdemo', 'decode number array = %{public}s', ArrayToString(kv.decodeNumberSet('number_array')));\n    hilog.info(0, 'mmkvdemo', 'decode bool array = %{public}s', ArrayToString(kv.decodeBoolSet('bool_array')));\n    hilog.info(0, 'mmkvdemo', 'decode uint8 array = %{public}s', ArrayBufferToString(kv.decodeBytes('uint8_array')));\n  }\n\n  testBackup() {\n    let context = GlobalContainer.getObject('context') as Context;\n    let backupRootDir = context.filesDir + '/mmkv_backup_3';\n    let otherDir = context.filesDir + '/mmkv_3';\n    let mmapID = 'test/AES';\n    let cryptKey = 'Tencent MMKV';\n\n    {\n      let kv = this.testMMKV(mmapID, false, cryptKey, otherDir);\n      kv.removeValueForKey('test_restore');\n      kv.close();\n    }\n\n    let ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, otherDir);\n    hilog.info(0, 'mmkvdemo', 'backup one [%{public}s] ret = %{public}s', mmapID, ret);\n    if (ret) {\n      let mmkv = MMKV.backedUpMMKVWithID(mmapID, backupRootDir, MMKV.SINGLE_PROCESS_MODE, cryptKey);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, otherDir);\n      hilog.info(0, 'mmkvdemo', 'check on origin file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n\n    /*{\n      MMKV mmkv = MMKV.mmkvWithID('imported');\n      mmkv.close();\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      mmkv.close();\n    }*/\n    backupRootDir = context.filesDir + '/mmkv_backup';\n    let count = MMKV.backupAllToDirectory(backupRootDir);\n    hilog.info(0, 'mmkvdemo', 'backup all count %{public}d', count);\n    if (count > 0) {\n      let mmkv = MMKV.backedUpMMKVWithID('imported', backupRootDir);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.backedUpMMKVWithID('test/AES_reKey1', backupRootDir);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n  }\n\n  testRestore() {\n    hilog.info(0, 'mmkvdemo', 'test restore begin');\n\n    let context = GlobalContainer.getObject('context') as Context;\n    let backupRootDir = context.filesDir + '/mmkv_backup_3';\n    let otherDir = context.filesDir + '/mmkv_3';\n    let mmapID = 'test/AES';\n    let cryptKey = 'Tencent MMKV';\n\n    let mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, otherDir);\n    mmkv.encodeBool('test_restore', true);\n    hilog.info(0, 'mmkvdemo', 'before restore [%{public}s] allKeys: %{public}s',\n      mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    let ret = MMKV.restoreOneFromDirectory(mmapID, backupRootDir, otherDir);\n    hilog.info(0, 'mmkvdemo', 'restore one [%{public}s] ret = %{public}s', mmapID, ret);\n    if (ret) {\n      hilog.info(0, 'mmkvdemo', 'after restore [%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n\n    /*{\n      mmkv = MMKV.mmkvWithID('imported');\n      mmkv.close();\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      mmkv.close();\n    }*/\n    backupRootDir = context.filesDir + '/mmkv_backup';\n    let count = MMKV.restoreAllFromDirectory(backupRootDir);\n    hilog.info(0, 'mmkvdemo', 'restore all count %{public}d', count);\n    if (count > 0) {\n      mmkv = MMKV.mmkvWithID('imported');\n      hilog.info(0, 'mmkvdemo', 'check on restore file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      hilog.info(0, 'mmkvdemo', 'check on restore file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n  }\n\n  private testAutoExpireOne(kv: MMKV, decodeOnly: boolean, expiration: number): void {\n    if (!decodeOnly) {\n      kv.encodeBool('bool', true, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n\n    let pow31 = Math.pow(2, 31);\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', pow31 - 1, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', -pow31, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n    let pow32 = Math.pow(2, 32);\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', pow32 - 1, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', 0, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n    let pow63 = BigInt(2**63);\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', pow63 - BigInt(1), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', -pow63, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n    let pow64 = BigInt(2**64);\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', pow64 - BigInt(1), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', BigInt(0), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MAX_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MIN_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeString('string', 'Hello world to OpenHarmony!', expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n    if (!decodeOnly) {\n      let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n      kv.encodeBytes('bytes', arrayBuffer, expiration);\n    }\n    let bytes = kv.decodeBytes('bytes');\n    hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n    hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n      kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n    if (!decodeOnly) {\n      let strArr: string[] = ['abc', 'defg', 'hijk'];\n      kv.encodeStringSet('string-set', strArr, expiration);\n    }\n    let newStrArr = kv.decodeStringSet('string-set');\n    hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n    if (!decodeOnly) {\n      kv.encodeStringSet('empty-string-set', [], expiration);\n    }\n    let emptyStrArr = kv.decodeStringSet('empty-string-set');\n    hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n    let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n    hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n    hilog.info(0, 'mmkvdemo', 'contains bool = %{public}s, contains bool_not_exit = %{public}s',\n      kv.containsKey('bool'), kv.containsKey('bool_not_exit'));\n\n    kv.removeValueForKey('bool');\n    hilog.info(0, 'mmkvdemo', 'after remove, contains bool = %{public}s', kv.containsKey('bool'));\n\n    hilog.info(0, 'mmkvdemo', 'total NonExpire count = %{public}i, total size = %{public}i, actual size = %{public}i',\n      kv.count(true), kv.totalSize(), kv.actualSize());\n\n    kv.clearMemoryCache();\n    hilog.info(0, 'mmkvdemo', 'all NonExpire Keys = %{public}s', ArrayToString(kv.allKeys(true)));\n\n    kv.removeValuesForKeys(['int32', 'uint32']);\n    hilog.info(0, 'mmkvdemo', 'remove \"int32\" & \"uint32\", allKeys = %{public}s', ArrayToString(kv.allKeys(true)));\n  }\n\n  private async testAutoExpire(): Promise<void> {\n    let mmkv = MMKV.mmkvWithID('test_auto_expire');\n    mmkv.clearAll();\n    mmkv.disableAutoKeyExpire();\n\n    mmkv.enableAutoKeyExpire(1);\n    mmkv.encodeBool('auto_expire_key_1', true);\n    mmkv.encodeBool('never_expire_key_1', true, MMKV.ExpireNever);\n\n    this.testAutoExpireOne(mmkv, false, 1);\n    await new Promise<void>(resolve => setTimeout(resolve, 1000 * 2));\n    this.testAutoExpireOne(mmkv, true, 1);\n\n    if (mmkv.containsKey('auto_expire_key_1')) {\n      hilog.error(0, 'mmkvdemo', 'auto key expiration auto_expire_key_1');\n    } else {\n      hilog.info(0, 'mmkvdemo', 'auto key expiration auto_expire_key_1');\n    }\n    if (mmkv.containsKey('never_expire_key_1')) {\n      hilog.info(0, 'mmkvdemo', 'auto key expiration never_expire_key_1');\n    } else {\n      hilog.error(0, 'mmkvdemo', 'auto key expiration never_expire_key_1');\n    }\n  }\n\n  private testCompareBeforeSet(): void {\n    let mmkv = MMKV.mmkvWithID('testCompareBeforeSet');\n    mmkv.enableCompareBeforeSet();\n\n    mmkv.encodeString('key', 'extra');\n\n    {\n      let key = 'int';\n      let v = 12345;\n      mmkv.encodeInt32(key, v);\n      let actualSize = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}d', mmkv.decodeInt32(key, -1));\n      mmkv.encodeInt32(key, v);\n      let actualSize2 = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize2);\n      if (actualSize2 != actualSize) {\n        hilog.error(0, 'mmkvdemo', 'testCompareBeforeSet fail');\n      }\n\n      mmkv.encodeInt32(key, v * 23);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', mmkv.actualSize());\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}d', mmkv.decodeInt32(key, -1));\n    }\n\n    {\n      let key = 'string';\n      let v = 'w012A🏊🏻good';\n      mmkv.encodeString(key, v);\n      let actualSize = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}s', mmkv.decodeString(key, ''));\n      mmkv.encodeString(key, v);\n      let actualSize2 = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize2);\n      if (actualSize2 != actualSize) {\n        hilog.error(0, 'mmkvdemo', 'testCompareBeforeSet fail');\n      }\n\n      mmkv.encodeString(key, 'temp data 👩🏻‍🏫');\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', mmkv.actualSize());\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}s', mmkv.decodeString(key, ''));\n    }\n  }\n\n  private testAshmem() {\n    let mmapID = 'testAshmem';\n    let cryptKey = 'Tencent MMKV';\n\n    let kv = MMKV.mmkvWithAshmemID(mmapID, MMKV.pageSize, MMKV.SINGLE_PROCESS_MODE, cryptKey);\n    this.testOneMMKV(kv, false);\n\n    let newKV = MMKV.mmkvWithAshmemFD(mmapID, kv.ashmemFD, kv.ashmemMetaFD, cryptKey);\n    this.testOneMMKV(newKV, true);\n  }\n}\n\nfunction StringToArrayBuffer(str: string | undefined): ArrayBuffer {\n  if (str == undefined) {\n    return new ArrayBuffer(0);\n  }\n  let enc = new util.TextEncoder(); // always utf-8\n  let i8Arr = enc.encodeInto(str);\n  return i8Arr.buffer;\n}\n\nfunction ArrayBufferToString(arr: ArrayBuffer | undefined): string {\n  if (arr == undefined) {\n    return 'undefined';\n  }\n  let out8Arr = new Uint8Array(arr);\n  let dnc = util.TextDecoder.create('utf-8');\n  return dnc.decodeWithStream(out8Arr);\n}\n\nfunction ArrayToString(arr: string[] | number[] | boolean[] | undefined): string {\n  if (arr == undefined) {\n    return 'undefined';\n  }\n  return '[' + arr.join(', ') + ']';\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/example/Util.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class GlobalContainer {\n  private static container = new Map<string, Object>();\n\n  static getObject(value: string) {\n    return GlobalContainer.container.get(value);\n  }\n\n  static setObject(key: string, objectClass: Object): void {\n    GlobalContainer.container.set(key, objectClass);\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/hvigorfile.ts",
    "content": "import { harTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: harTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/obfuscation-rules.txt",
    "content": "# Define project specific obfuscation rules here.\n# You can include the obfuscation configuration files in the current module's build-profile.json5.\n#\n# For more details, see\n#   https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md\n\n# Obfuscation options:\n# -disable-obfuscation: disable all obfuscations\n# -enable-property-obfuscation: obfuscate the property names\n# -enable-toplevel-obfuscation: obfuscate the names in the global scope\n# -compact: remove unnecessary blank spaces and all line feeds\n# -remove-log: remove all console.* statements\n# -print-namecache: print the name cache that contains the mapping from the old names to new names\n# -apply-namecache: reuse the given cache file\n\n# Keep options:\n# -keep-property-name: specifies property names that you want to keep\n# -keep-global-name: specifies names that you want to keep in the global scope\n"
  },
  {
    "path": "OpenHarmony/MMKV/oh-package.json5",
    "content": "{\n  \"name\": \"@tencent/mmkv\",\n  \"version\": \"2.4.0\",\n  \"description\": \"The official OpenHarmony package of MMKV. An efficient, small mobile key-value storage framework developed by WeChat.\",\n  \"main\": \"Index.ets\",\n  \"author\": \"guoling\",\n  \"license\": \"MMKV is published under the BSD 3-Clause license. For details about 3rd components check out https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"libmmkv.so\": \"file:./src/main/cpp/types/libmmkv\"\n  },\n  \"compatibleSdkVersion\": \"12\",\n  \"compatibleSdkType\": \"HarmonyOS\",\n  \"repository\": \"https://github.com/Tencent/mmkv\",\n  \"homepage\": \"https://github.com/Tencent/mmkv\",\n  \"keywords\": [\n    \"mmkv\",\n    \"key-value\",\n    \"storage\",\n    \"preferences\"\n  ],\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/cpp/CMakeLists.txt",
    "content": "# the minimum version of CMake.\ncmake_minimum_required(VERSION 3.4.1)\nproject(mmkv)\n\nset(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})\n# github/MMKV\nset(REPO_ROOT_PATH ${NATIVERENDER_ROOT_PATH}/../../../../..)\n# github/MMKV/OpenHarmony/MMKV\nset(MODULE_ROOT_PATH ${NATIVERENDER_ROOT_PATH}/../../..)\n\nadd_subdirectory(${REPO_ROOT_PATH}/Core Core)\n\nif(DEFINED PACKAGE_FIND_FILE)\n    include(${PACKAGE_FIND_FILE})\nendif()\n\ninclude_directories(${NATIVERENDER_ROOT_PATH}\n                    ${NATIVERENDER_ROOT_PATH}/include)\n\nadd_library(mmkv SHARED\n native_bridge.cpp\n flutter-bridge.cpp)\n\nset_target_properties(mmkv PROPERTIES\n        CXX_STANDARD 20\n        CXX_EXTENSIONS OFF\n        POSITION_INDEPENDENT_CODE ON\n        )\n\ntarget_link_libraries(mmkv PUBLIC\n    libace_napi.z.so\n    libhilog_ndk.z.so\n    core)\n\n#file(REMOVE_RECURSE ${MODULE_ROOT_PATH}/include)\n\nmessage(STATUS \"copying from ${REPO_ROOT_PATH}/Core/include to ${MODULE_ROOT_PATH}\")\nfile(COPY ${REPO_ROOT_PATH}/Core/include DESTINATION ${MODULE_ROOT_PATH}\n    PATTERN \"MMKVLog.h\" EXCLUDE\n    PATTERN \"MemoryFile.h\" EXCLUDE\n)\n\n#message(FATAL_ERROR \"Halt.\")\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/cpp/flutter-bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKVPredef.h>\n\n#ifndef MMKV_DISABLE_FLUTTER\n\n#    include <MMKV/MMKV.h>\n#    include <MMKV/MMKVLog.h>\n#    include <cstdint>\n#    include <string>\n\nusing namespace mmkv;\nusing namespace std;\n\nnamespace mmkv {\n// extern int g_android_api;\nextern string g_android_tmpDir;\n}\n\n#    define MMKV_EXPORT extern \"C\" __attribute__((visibility(\"default\"))) __attribute__((used))\n\nusing LogCallback_t = void (*)(uint32_t level, const char *file, int32_t line, const char *funcname, const char *message);\nusing ErrorCallback_t = int (*)(const char *mmapID, int32_t errorType);\nusing ContenctChangeCallback_t = void (*)(const char *mmapID);\n\nclass FlutterMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    LogCallback_t logCallback = nullptr;\n    ErrorCallback_t errorCallback = nullptr;\n    ContenctChangeCallback_t contentChangeCallback = nullptr;\n    ContenctChangeCallback_t contentLoadedCallback = nullptr;\n\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) override {\n        if (logCallback) {\n            logCallback(level, file, line, function, message.c_str());\n        }\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVCRCCheckFail);\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVFileLength);\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (contentChangeCallback) {\n            contentChangeCallback(mmapID.c_str());\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if (contentLoadedCallback) {\n            contentLoadedCallback(mmapID.c_str());\n        }\n    }\n};\n\nstatic FlutterMMKVHandler g_flutterHandler;\n\nMMKV_EXPORT void *mmkvInitialize_v2(const char *rootDir, const char *cacheDir, int32_t sdkInt, int32_t logLevel, LogCallback_t callback) {\n    if (!rootDir) {\n        return nullptr;\n    }\n    if (cacheDir) {\n        g_android_tmpDir = string(cacheDir);\n    }\n\n    // g_android_api = sdkInt;\n#ifdef MMKV_STL_SHARED\n    MMKVInfo(\"current API level = %d, libc++_shared=%d\", sdkInt, MMKV_STL_SHARED);\n#else\n    MMKVInfo(\"current API level = %d, libc++_shared=?\", sdkInt);\n#endif\n\n    if (callback) {\n        g_flutterHandler.logCallback = callback;\n        MMKV::initializeMMKV(rootDir, (MMKVLogLevel) logLevel, &g_flutterHandler);\n    } else {\n        MMKV::initializeMMKV(rootDir, (MMKVLogLevel) logLevel);\n    }\n    return (void *) MMKV::getRootDir().c_str();\n}\n\nMMKV_EXPORT void mmkvInitialize_v1(const char *rootDir, const char *cacheDir, int32_t sdkInt, int32_t logLevel) {\n    mmkvInitialize_v2(rootDir, cacheDir, sdkInt, logLevel, nullptr);\n}\n\nMMKV_EXPORT void mmkvInitialize(const char *rootDir, int32_t logLevel) {\n    mmkvInitialize_v2(rootDir, nullptr, 0, logLevel, nullptr);\n}\n\nMMKV_EXPORT void *getMMKVWithID(const char *mmapID, int32_t mode, const char *cryptKey, const char *rootPath,\n                                size_t expectedCapacity, bool fromNameSpace, bool aes256, int32_t enableKeyExpire,\n                                int32_t expiredInSeconds, bool enableCompareBeforeSet, int32_t recover,\n                                uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n    if (!mmapID) {\n        return kv;\n    }\n    string str = mmapID;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    bool done = false;\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            if (rootPath) {\n                string path = rootPath;\n                if (fromNameSpace) {\n                    auto ns = MMKV::nameSpace(path);\n                    config.rootPath = &ns.getRootDir();\n                    kv = ns.mmkvWithID(str, config);\n                } else {\n                    config.rootPath = &path;\n                    kv = MMKV::mmkvWithID(str, config);\n                }\n            } else {\n                kv = MMKV::mmkvWithID(str, config);\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath) {\n            string path = rootPath;\n            if (fromNameSpace) {\n                auto ns = MMKV::nameSpace(path);\n                config.rootPath = &ns.getRootDir();\n                kv = ns.mmkvWithID(str, config);\n            } else {\n                config.rootPath = &path;\n                kv = MMKV::mmkvWithID(str, config);\n            }\n        } else {\n            kv = MMKV::mmkvWithID(str, config);\n        }\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT void *getDefaultMMKV(int32_t mode, const char *cryptKey, bool aes256, size_t expectedCapacity,\n                                 int32_t enableKeyExpire, int32_t expiredInSeconds, bool enableCompareBeforeSet,\n                                 int32_t recover, uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            kv = MMKV::defaultMMKV(config);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::defaultMMKV(config);\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT const char *mmapID(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->mmapID().c_str();\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool encodeBool(void *handle, const char *oKey, bool value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBool_v2(void *handle, const char *oKey, bool value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool decodeBool(void *handle, const char *oKey, bool defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getBool(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt32(void *handle, const char *oKey, int32_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt32_v2(void *handle, const char *oKey, int32_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int32_t decodeInt32(void *handle, const char *oKey, int32_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt64(void *handle, const char *oKey, int64_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt64_v2(void *handle, const char *oKey, int64_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int64_t decodeInt64(void *handle, const char *oKey, int64_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeDouble(void *handle, const char *oKey, double value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeDouble_v2(void *handle, const char *oKey, double value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT double decodeDouble(void *handle, const char *oKey, double defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getDouble(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeBytes(void *handle, const char *oKey, void *oValue, uint64_t length) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBytes_v2(void *handle, const char *oKey, void *oValue, uint64_t length, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key, expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *decodeBytes(void *handle, const char *oKey, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        mmkv::MMBuffer value;\n        auto hasValue = kv->getBytes(key, value);\n        if (hasValue) {\n            if (value.length() > 0) {\n                if (value.isStoredOnStack()) {\n                    auto result = malloc(value.length());\n                    if (result) {\n                        memcpy(result, value.getPtr(), value.length());\n                        *lengthPtr = value.length();\n                    }\n                    return result;\n                }\n                void *result = value.getPtr();\n                *lengthPtr = value.length();\n                value.detach();\n                return result;\n            }\n            *lengthPtr = 0;\n            // this ptr is intended for checking existence of the value\n            // don't free this ptr\n            return value.getPtr();\n        }\n    }\n    return nullptr;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT bool reKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            return kv->reKey(key, aes256);\n        } else {\n            return kv->reKey(string(), aes256);\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *cryptKey(void *handle, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && lengthPtr) {\n        auto cryptKey = kv->cryptKey();\n        if (cryptKey.length() > 0) {\n            auto ptr = malloc(cryptKey.length());\n            if (ptr) {\n                memcpy(ptr, cryptKey.data(), cryptKey.length());\n                *lengthPtr = cryptKey.length();\n                return ptr;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT void checkReSetCryptKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            kv->checkReSetCryptKey(&key, aes256);\n        } else {\n            kv->checkReSetCryptKey(nullptr, aes256);\n        }\n    }\n}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT uint32_t valueSize(void *handle, char *oKey, bool actualSize) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        auto ret = kv->getValueSize(key, actualSize);\n        return static_cast<uint32_t>(ret);\n    }\n    return 0;\n}\n\nMMKV_EXPORT int32_t writeValueToNB(void *handle, char *oKey, void *pointer, uint32_t size) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->writeValueToBuffer(key, pointer, size);\n    }\n    return -1;\n}\n\nMMKV_EXPORT uint64_t allKeys(void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        auto keys = kv->allKeys(filterExpire);\n        if (!keys.empty()) {\n            auto keyArray = (char **) malloc(keys.size() * sizeof(void *));\n            auto sizeArray = (uint32_t *) malloc(keys.size() * sizeof(uint32_t *));\n            if (!keyArray || !sizeArray) {\n                free(keyArray);\n                free(sizeArray);\n                return 0;\n            }\n            *keyArrayPtr = keyArray;\n            *sizeArrayPtr = sizeArray;\n\n            for (size_t index = 0; index < keys.size(); index++) {\n                auto &key = keys[index];\n                sizeArray[index] = static_cast<uint32_t>(key.length());\n                keyArray[index] = (char *) malloc(key.length());\n                if (keyArray[index]) {\n                    memcpy(keyArray[index], key.data(), key.length());\n                }\n            }\n        }\n        return keys.size();\n    }\n    return 0;\n}\n\nMMKV_EXPORT bool containsKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->containsKey(key);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t count(void *handle, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->count(filterExpire);\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t totalSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->totalSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t actualSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->actualSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT void removeValueForKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        kv->removeValueForKey(key);\n    }\n}\n\nMMKV_EXPORT void removeValuesForKeys(void *handle, char **keyArray, uint32_t *sizeArray, uint64_t count) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && keyArray && sizeArray && count > 0) {\n        vector<string> arrKeys;\n        arrKeys.reserve(count);\n        for (uint64_t index = 0; index < count; index++) {\n            if (sizeArray[index] > 0 && keyArray[index]) {\n                arrKeys.emplace_back(keyArray[index], sizeArray[index]);\n            }\n        }\n        if (!arrKeys.empty()) {\n            kv->removeValuesForKeys(arrKeys);\n        }\n    }\n}\n\nMMKV_EXPORT void clearAll(void *handle, bool keepSpace) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearAll(keepSpace);\n    }\n}\n\nMMKV_EXPORT void mmkvSync(void *handle, bool sync) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->sync((SyncFlag) sync);\n    }\n}\n\nMMKV_EXPORT void clearMemoryCache(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n}\n\nMMKV_EXPORT int32_t pageSize() {\n    return static_cast<int32_t>(DEFAULT_MMAP_SIZE);\n}\n\nMMKV_EXPORT const char *version() {\n    return MMKV_VERSION;\n}\n\nMMKV_EXPORT void trim(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->trim();\n    }\n}\n\nMMKV_EXPORT void mmkvClose(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->close();\n    }\n}\n\nMMKV_EXPORT void mmkvMemcpy(void *dst, const void *src, uint64_t size) {\n    memcpy(dst, src, size);\n}\n\nMMKV_EXPORT bool backupOne(const char *mmapID, const char *dstDir, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupOneToDirectory(mmapID, dstDir, &root);\n        }\n    }\n    return MMKV::backupOneToDirectory(mmapID, dstDir);\n}\n\nMMKV_EXPORT bool restoreOne(const char *mmapID, const char *srcDir, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreOneFromDirectory(mmapID, srcDir, &root);\n        }\n    }\n    return MMKV::restoreOneFromDirectory(mmapID, srcDir);\n}\n\nMMKV_EXPORT uint64_t backupAll(const char *dstDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupAllToDirectory(dstDir, &root);\n        }\n    }*/\n    return MMKV::backupAllToDirectory(dstDir);\n}\n\nMMKV_EXPORT uint64_t restoreAll(const char *srcDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreAllFromDirectory(srcDir, &root);\n        }\n    }*/\n    return MMKV::restoreAllFromDirectory(srcDir);\n}\n\nMMKV_EXPORT bool enableAutoExpire(void *handle, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableAutoKeyExpire(expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableAutoExpire(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableAutoKeyExpire();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool enableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isFileValid(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::isFileValid(mmapID, &root);\n        }\n    }\n    return MMKV::isFileValid(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool removeStorage(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::removeStorage(mmapID, &root);\n        }\n    }\n    return MMKV::removeStorage(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool isMultiProcess(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isMultiProcess();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isReadOnly(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isReadOnly();\n    }\n    return false;\n}\n\nMMKV_EXPORT void registerErrorHandler(ErrorCallback_t callback) {\n    g_flutterHandler.errorCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.contentChangeCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentChangeNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentChangeCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentLoadedNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentLoadedCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentChangeCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void checkContentChanged(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->checkContentChanged();\n    }\n}\n\nMMKV_EXPORT bool getNameSpace(const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (!root.empty()) {\n            MMKV::nameSpace(root);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool checkExist(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::checkExist(mmapID, &root);\n        }\n    }\n    return MMKV::checkExist(mmapID, nullptr);\n}\n\nMMKV_EXPORT uint64_t importFrom(void *handle, void *srcHandle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    MMKV *kvSrc = static_cast<MMKV *>(srcHandle);\n    if (kv && kvSrc) {\n        return kv->importFrom(kvSrc);\n    }\n    return 0;\n}\n\n#endif // MMKV_DISABLE_FLUTTER\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/cpp/native_bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMBuffer.h>\n#include <MMKV/MMKV.h>\n#include <MMKV/MMKVLog.h>\n#include <MMKV/MemoryFile.h>\n#include <MMKV/MiniPBCoder.h>\n#include \"napi/native_api.h\"\n#include <cstdint>\n#include <string>\n\nusing namespace std;\nusing namespace mmkv;\n\n// assuming env is defined\n#define NAPI_CALL_RET(call, return_value)                                                                              \\\n    do {                                                                                                               \\\n        napi_status status = (call);                                                                                   \\\n        if (status != napi_ok) {                                                                                       \\\n            const napi_extended_error_info *error_info = nullptr;                                                      \\\n            napi_get_last_error_info(env, &error_info);                                                                \\\n            MMKVInfo(\"NAPI Error: code %d, msg %s\", error_info->error_code, error_info->error_message);                \\\n            bool is_pending;                                                                                           \\\n            napi_is_exception_pending(env, &is_pending);                                                               \\\n            if (!is_pending) {                                                                                         \\\n                auto message = error_info->error_message ? error_info->error_message : \"null\";                         \\\n                napi_throw_error(env, nullptr, message);                                                               \\\n                return return_value;                                                                                   \\\n            }                                                                                                          \\\n        }                                                                                                              \\\n    } while (0)\n\n#define NAPI_CALL(call) NAPI_CALL_RET(call, nullptr)\n\nbool IsNValueUndefined(napi_env env, napi_value value) {\n    napi_valuetype type;\n    if (napi_typeof(env, value, &type) == napi_ok && type == napi_undefined) {\n        return true;\n    }\n    return false;\n}\n\nstatic string NValueToString(napi_env env, napi_value value, bool maybeUndefined = false) {\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return \"\";\n    }\n\n    size_t size;\n    NAPI_CALL_RET(napi_get_value_string_utf8(env, value, nullptr, 0, &size), \"\");\n    string result(size, '\\0');\n    NAPI_CALL_RET(napi_get_value_string_utf8(env, value, (char *) result.data(), size + 1, nullptr), \"\");\n    return result;\n}\n\nstatic napi_value StringToNValue(napi_env env, const string &value) {\n    napi_value result;\n    napi_create_string_utf8(env, value.data(), value.size(), &result);\n    return result;\n}\n\nstatic vector<string> NValueToStringArray(napi_env env, napi_value value, bool maybeUndefined = false) {\n    vector<string> keys;\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return keys;\n    }\n\n    uint32_t length = 0;\n    if (napi_get_array_length(env, value, &length) != napi_ok || length == 0) {\n        return keys;\n    }\n    keys.reserve(length);\n\n    for (uint32_t index = 0; index < length; index++) {\n        napi_value jsKey = nullptr;\n        if (napi_get_element(env, value, index, &jsKey) != napi_ok) {\n            continue;\n        }\n        keys.push_back(NValueToString(env, jsKey));\n    }\n    return keys;\n}\n\nstatic napi_value StringArrayToNValue(napi_env env, const vector<string> &value) {\n    napi_value jsArr = nullptr;\n    napi_create_array_with_length(env, value.size(), &jsArr);\n    for (size_t index = 0; index < value.size(); index++) {\n        auto jsKey = StringToNValue(env, value[index]);\n        napi_set_element(env, jsArr, index, jsKey);\n    }\n    return jsArr;\n}\n\nstatic napi_value DoubleToNValue(napi_env env, double value);\nstatic double NValueToDouble(napi_env env, napi_value value);\n\nstatic vector<double> NValueToDoubleArray(napi_env env, napi_value value, bool maybeUndefined = false) {\n    vector<double> vec;\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return vec;\n    }\n\n    uint32_t length = 0;\n    if (napi_get_array_length(env, value, &length) != napi_ok || length == 0) {\n        return vec;\n    }\n    vec.reserve(length);\n\n    for (uint32_t index = 0; index < length; index++) {\n        napi_value jsKey = nullptr;\n        if (napi_get_element(env, value, index, &jsKey) != napi_ok) {\n            continue;\n        }\n        vec.push_back(NValueToDouble(env, jsKey));\n    }\n    return vec;\n}\n\nstatic napi_value DoubleArrayToNValue(napi_env env, const vector<double> &value) {\n    napi_value jsArr = nullptr;\n    napi_create_array_with_length(env, value.size(), &jsArr);\n    for (size_t index = 0; index < value.size(); index++) {\n        auto jsKey = DoubleToNValue(env, value[index]);\n        napi_set_element(env, jsArr, index, jsKey);\n    }\n    return jsArr;\n}\n\nstatic napi_value BoolToNValue(napi_env env, bool value);\nstatic bool NValueToBool(napi_env env, napi_value value, bool maybeUndefined = false);\n\nstatic vector<bool> NValueToBoolArray(napi_env env, napi_value value, bool maybeUndefined = false) {\n    vector<bool> keys;\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return keys;\n    }\n\n    uint32_t length = 0;\n    if (napi_get_array_length(env, value, &length) != napi_ok || length == 0) {\n        return keys;\n    }\n    keys.reserve(length);\n\n    for (uint32_t index = 0; index < length; index++) {\n        napi_value jsKey = nullptr;\n        if (napi_get_element(env, value, index, &jsKey) != napi_ok) {\n            continue;\n        }\n        keys.push_back(NValueToBool(env, jsKey));\n    }\n    return keys;\n}\n\nstatic napi_value BoolArrayToNValue(napi_env env, const vector<bool> &value) {\n    napi_value jsArr = nullptr;\n    napi_create_array_with_length(env, value.size(), &jsArr);\n    for (size_t index = 0; index < value.size(); index++) {\n        auto jsKey = BoolToNValue(env, value[index]);\n        napi_set_element(env, jsArr, index, jsKey);\n    }\n    return jsArr;\n}\n\nstatic void my_finalizer(napi_env env, void *finalize_data, void *finalize_hint) {\n    // MMKVInfo(\"free %p\", finalize_data);\n    free(finalize_data);\n}\n\nstatic MMBuffer NValueToMMBuffer(napi_env env, napi_value value, bool maybeUndefined = false) {\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return MMBuffer();\n    }\n\n    void *data = nullptr;\n    size_t length = 0;\n    if (napi_get_arraybuffer_info(env, value, &data, &length) == napi_ok) {\n        return MMBuffer(data, length, mmkv::MMBufferNoCopy);\n    }\n    return MMBuffer();\n}\n\nstatic napi_value MMBufferToNValue(napi_env env, const MMBuffer &value) {\n    napi_value result = nullptr;\n    void *data = nullptr;\n    if (napi_create_arraybuffer(env, value.length(), &data, &result) == napi_ok) {\n        memcpy(data, value.getPtr(), value.length());\n    }\n    return result;\n}\n\nstatic napi_value MMBufferToNValue(napi_env env, MMBuffer &&value) {\n    if (!value.isStoredOnStack()) {\n        napi_value result = nullptr;\n        auto ret = napi_create_external_arraybuffer(env, value.getPtr(), value.length(), my_finalizer, nullptr, &result);\n        if (ret == napi_ok) {\n            // MMKVInfo(\"using napi_create_external_arraybuffer %p\", value.getPtr());\n            value.detach();\n            return result;\n        }\n    }\n    return MMBufferToNValue(env, (const MMBuffer &) value);\n}\n\n// static napi_value MMBufferToTypeArray(napi_env env, const MMBuffer &value, napi_typedarray_type type) {\n//     napi_value result = nullptr;\n//     void *data = nullptr;\n//     if (napi_create_typedarray(env, type, value.length(), &data, &result) == napi_ok) {\n//         memcpy(data, value.getPtr(), value.length());\n//     }\n//     return result;\n// }\n//\n// static napi_value MMBufferToTypeArray(napi_env env, MMBuffer &&value) {\n//     if (!value.isStoredOnStack()) {\n//         napi_value result = nullptr;\n//         auto ret =\n//             napi_create_external_arraybuffer(env, value.getPtr(), value.length(), my_finalizer, nullptr, &result);\n//         if (ret == napi_ok) {\n//             // MMKVInfo(\"using napi_create_external_arraybuffer %p\", value.getPtr());\n//             value.detach();\n//             return result;\n//         }\n//     }\n//     return MMBufferToNValue(env, (const MMBuffer &)value);\n// }\n\nstatic napi_value NAPIUndefined(napi_env env) {\n    napi_value result = nullptr;\n    napi_get_undefined(env, &result);\n    return result;\n}\n\nstatic napi_value NAPINull(napi_env env) {\n    napi_value result;\n    napi_get_null(env, &result);\n    return result;\n}\n\nstatic napi_value BoolToNValue(napi_env env, bool value) {\n    napi_value result;\n    napi_value resultBool;\n    napi_create_double(env, value, &result);\n    napi_coerce_to_bool(env, result, &resultBool);\n    return resultBool;\n}\n\nstatic bool NValueToBool(napi_env env, napi_value value, bool maybeUndefined) {\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return false;\n    }\n    bool result;\n    if (napi_get_value_bool(env, value, &result) == napi_ok) {\n        return result;\n    }\n    return false;\n}\n\nstatic napi_value Int32ToNValue(napi_env env, int32_t value) {\n    napi_value result;\n    napi_create_int32(env, value, &result);\n    return result;\n}\n\nstatic int32_t NValueToInt32(napi_env env, napi_value value) {\n    int32_t result;\n    napi_get_value_int32(env, value, &result);\n    return result;\n}\n\nstatic int32_t NValueToInt32(napi_env env, napi_value value, int32_t defaultValue) {\n    if (IsNValueUndefined(env, value)) {\n        return defaultValue;\n    }\n    return NValueToInt32(env, value);\n}\n\nstatic napi_value UInt32ToNValue(napi_env env, uint32_t value) {\n    napi_value result;\n    napi_create_uint32(env, value, &result);\n    return result;\n}\n\nstatic uint32_t NValueToUInt32(napi_env env, napi_value value) {\n    uint32_t result;\n    napi_get_value_uint32(env, value, &result);\n    return result;\n}\n\nstatic uint32_t NValueToUInt32(napi_env env, napi_value value, uint32_t defaultValue) {\n    if (IsNValueUndefined(env, value)) {\n        return defaultValue;\n    }\n    return NValueToUInt32(env, value);\n}\n\nstatic napi_value DoubleToNValue(napi_env env, double value) {\n    napi_value result;\n    napi_create_double(env, value, &result);\n    return result;\n}\n\nstatic double NValueToDouble(napi_env env, napi_value value) {\n    double result;\n    napi_get_value_double(env, value, &result);\n    return result;\n}\n\nstatic napi_value Int64ToNValue(napi_env env, int64_t value) {\n    napi_value result;\n    napi_create_bigint_int64(env, value, &result);\n    return result;\n}\n\nstatic int64_t NValueToInt64(napi_env env, napi_value value) {\n    int64_t result;\n    bool lossless;\n    napi_get_value_bigint_int64(env, value, &result, &lossless);\n    return result;\n}\n\nstatic napi_value UInt64ToNValue(napi_env env, uint64_t value) {\n    napi_value result;\n    napi_create_bigint_uint64(env, value, &result);\n    return result;\n}\n\nstatic uint64_t NValueToUInt64(napi_env env, napi_value value, bool maybeUndefined = false) {\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return 0;\n    }\n    uint64_t result;\n    bool lossless;\n    napi_get_value_bigint_uint64(env, value, &result, &lossless);\n    return result;\n}\n\nstruct CallbackInfo {\n    napi_ref handlerRef = nullptr;\n\n    napi_ref callbacks[7] = {};\n    napi_ref &wantLogRedirect = callbacks[0];\n    napi_ref &mmkvLog = callbacks[1];\n    napi_ref &onMMKVCRCCheckFail = callbacks[2];\n    napi_ref &onMMKVFileLengthError = callbacks[3];\n    napi_ref &wantContentChangeNotification = callbacks[4];\n    napi_ref &onContentChangedByOuterProcess = callbacks[5];\n    napi_ref &onMMKVContentLoadSuccessfully = callbacks[6];\n};\n\nstatic const char *g_arrCallbackNames[] = {\n    \"wantLogRedirect\",\n    \"mmkvLog\",\n    \"onMMKVCRCCheckFail\",\n    \"onMMKVFileLengthError\",\n    \"wantContentChangeNotification\",\n    \"onContentChangedByOuterProcess\",\n    \"onMMKVContentLoadSuccessfully\"\n};\n\nstatic CallbackInfo g_callbackInfo;\n\nstatic std::tuple<bool, bool, bool> initCallbacks(napi_env env, napi_value callbackArg) {\n    if (IsNValueUndefined(env, callbackArg)) {\n        return {false, false, false};\n    }\n    napi_valuetype valueType;\n    napi_typeof(env, callbackArg, &valueType);\n    if (valueType != napi_object) {\n        napi_throw_type_error(env, nullptr, \"Expected an object as the callback argument\");\n        return {false, false, false};\n    }\n\n    bool hasCallback = false;\n    for (size_t index = 0; index < (sizeof(g_arrCallbackNames) / sizeof(g_arrCallbackNames[0])); index++) {\n        auto &name = g_arrCallbackNames[index];\n        napi_value callback;\n        if (napi_get_named_property(env, callbackArg, name, &callback) != napi_ok) {\n            continue;\n        }\n        napi_valuetype callbackType;\n        napi_typeof(env, callback, &callbackType);\n        if (callbackType != napi_function) {\n            napi_throw_type_error(env, nullptr, \"Expected a function as the callback\");\n            continue;\n        }\n        hasCallback = true;\n\n        napi_create_reference(env, callback, 1, &g_callbackInfo.callbacks[index]);\n    }\n    if (hasCallback) {\n        napi_create_reference(env, callbackArg, 1, &g_callbackInfo.handlerRef);\n    }\n\n    bool wantLogRedirect = false;\n    if (g_callbackInfo.wantLogRedirect) {\n        napi_value callback;\n        napi_get_reference_value(env, g_callbackInfo.wantLogRedirect, &callback);\n\n        napi_value result;\n        napi_call_function(env, callbackArg, callback, 0, nullptr, &result);\n        wantLogRedirect = NValueToBool(env, result);\n    }\n\n    bool wantContentChangeNotification = false;\n    if (g_callbackInfo.wantContentChangeNotification) {\n        napi_value callback;\n        napi_get_reference_value(env, g_callbackInfo.wantContentChangeNotification, &callback);\n\n        napi_value result;\n        napi_call_function(env, callbackArg, callback, 0, nullptr, &result);\n        wantContentChangeNotification = NValueToBool(env, result);\n    }\n\n    return {hasCallback, wantLogRedirect, wantContentChangeNotification};\n}\n\nstatic napi_env g_env = nullptr;\n\n// C++ adapter class that bridges mmkv::MMKVHandler to NAPI\nclass NAPIMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message) override {\n        if (!g_env || !g_callbackInfo.mmkvLog) {\n            return;\n        }\n        napi_value handler;\n        napi_get_reference_value(g_env, g_callbackInfo.handlerRef, &handler);\n        napi_value callback;\n        napi_get_reference_value(g_env, g_callbackInfo.mmkvLog, &callback);\n\n        napi_value args[] = {\n            Int32ToNValue(g_env, level),\n            StringToNValue(g_env, file),\n            Int32ToNValue(g_env, line),\n            StringToNValue(g_env, function),\n            StringToNValue(g_env, message)\n        };\n\n        napi_value result;\n        napi_call_function(g_env, handler, callback, sizeof(args) / sizeof(args[0]), args, &result);\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (!g_env || !g_callbackInfo.onMMKVCRCCheckFail) {\n            return OnErrorDiscard;\n        }\n        napi_value handler;\n        napi_get_reference_value(g_env, g_callbackInfo.handlerRef, &handler);\n        napi_value callback;\n        napi_get_reference_value(g_env, g_callbackInfo.onMMKVCRCCheckFail, &callback);\n\n        napi_value args[] = { StringToNValue(g_env, mmapID) };\n\n        napi_value result;\n        if (napi_call_function(g_env, handler, callback, sizeof(args) / sizeof(args[0]), args, &result) == napi_ok) {\n            if (!IsNValueUndefined(g_env, result)) {\n                return ( MMKVRecoverStrategic ) NValueToInt32(g_env, result);\n            }\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (!g_env || !g_callbackInfo.onMMKVFileLengthError) {\n            return OnErrorDiscard;\n        }\n        napi_value handler;\n        napi_get_reference_value(g_env, g_callbackInfo.handlerRef, &handler);\n        napi_value callback;\n        napi_get_reference_value(g_env, g_callbackInfo.onMMKVFileLengthError, &callback);\n\n        napi_value args[] = { StringToNValue(g_env, mmapID) };\n\n        napi_value result;\n        if (napi_call_function(g_env, handler, callback, sizeof(args) / sizeof(args[0]), args, &result) == napi_ok) {\n            if (!IsNValueUndefined(g_env, result)) {\n                return ( MMKVRecoverStrategic ) NValueToInt32(g_env, result);\n            }\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (!g_env || !g_callbackInfo.onContentChangedByOuterProcess) {\n            return;\n        }\n        napi_value handler;\n        napi_get_reference_value(g_env, g_callbackInfo.handlerRef, &handler);\n        napi_value callback;\n        napi_get_reference_value(g_env, g_callbackInfo.onContentChangedByOuterProcess, &callback);\n\n        napi_value args[] = { StringToNValue(g_env, mmapID) };\n\n        napi_value result;\n        napi_call_function(g_env, handler, callback, sizeof(args) / sizeof(args[0]), args, &result);\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if (!g_env || !g_callbackInfo.onMMKVContentLoadSuccessfully) {\n            return;\n        }\n        napi_value handler;\n        napi_get_reference_value(g_env, g_callbackInfo.handlerRef, &handler);\n        napi_value callback;\n        napi_get_reference_value(g_env, g_callbackInfo.onMMKVContentLoadSuccessfully, &callback);\n\n        napi_value args[] = { StringToNValue(g_env, mmapID) };\n\n        napi_value result;\n        napi_call_function(g_env, handler, callback, sizeof(args) / sizeof(args[0]), args, &result);\n    }\n};\n\nstatic NAPIMMKVHandler g_napiHandler;\n\nstatic napi_value initialize(napi_env env, napi_callback_info info) {\n    g_env = env;\n\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args , nullptr, nullptr));\n\n    auto rootDir = NValueToString(env, args[0]);\n    auto cacheDir = NValueToString(env, args[1]);\n\n    int32_t logLevel;\n    NAPI_CALL(napi_get_value_int32(env, args[2], &logLevel));\n\n    auto [hasCallback, wantLogRedirect, wantContentChangeNotification] = initCallbacks(env, args[3]);\n    mmkv::MMKVHandler *handler = (hasCallback || wantLogRedirect) ? &g_napiHandler : nullptr;\n\n    MMKVInfo(\"rootDir: %s, cacheDir: %s, log level:%d, has callback: %d, want log redirect: %d\",\n        rootDir.c_str(), cacheDir.c_str(), logLevel, hasCallback, wantLogRedirect);\n\n    MMKV::initializeMMKV(rootDir, (MMKVLogLevel) logLevel, handler);\n    g_android_tmpDir = cacheDir;\n\n    if (hasCallback || wantLogRedirect || wantContentChangeNotification) {\n        MMKV::registerHandler(&g_napiHandler);\n    }\n\n    return StringToNValue(env, MMKV::getRootDir());\n}\n\nstatic napi_value version(napi_env env, napi_callback_info info) {\n    napi_value sum;\n    NAPI_CALL(napi_create_string_latin1(env, MMKV_VERSION, strlen(MMKV_VERSION), &sum));\n    return sum;\n}\n\nstatic napi_value pageSize(napi_env env, napi_callback_info info) {\n    napi_value value;\n    NAPI_CALL(napi_create_uint32(env, DEFAULT_MMAP_SIZE, &value));\n    return value;\n}\n\nstatic napi_value getDefaultMMKV(napi_env env, napi_callback_info info) {\n    size_t argc = 9;\n    napi_value args[9] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    int32_t mode;\n    NAPI_CALL(napi_get_value_int32(env, args[0], &mode));\n    auto crypt = NValueToString(env, args[1], true);\n    auto aes256 = NValueToBool(env, args[2], true);\n    auto expectedCapacity = NValueToUInt64(env, args[3], true);\n    auto enableKeyExpire = NValueToInt32(env, args[4], -1);\n    auto expiredInSeconds = NValueToInt32(env, args[5], 0);\n    auto enableCompareBeforeSet = NValueToBool(env, args[6], true);\n    auto recover = NValueToInt32(env, args[7], -1);\n    auto itemSizeLimit = NValueToUInt32(env, args[8], 0);\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.cryptKey = crypt.empty() ? nullptr : &crypt;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    MMKV *kv = MMKV::defaultMMKV(config);\n\n    return UInt64ToNValue(env,(uint64_t)kv);\n}\n\n// mmkvWithID(mmapID: string, mode: number, cryptKey?: string, rootPath?: string, expectedCapacity?: bigint): bigint\nstatic napi_value mmkvWithID(napi_env env, napi_callback_info info) {\n    size_t argc = 11;\n    napi_value args[11] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    MMKV *kv = nullptr;\n    auto mmapID = NValueToString(env, args[0]);\n    if (!mmapID.empty()) {\n        int32_t mode = NValueToInt32(env, args[1]);\n        auto cryptKey = NValueToString(env, args[2], true);\n        auto rootPath = NValueToString(env, args[3], true);\n        auto expectedCapacity = NValueToUInt64(env, args[4], true);\n        auto aes256 = NValueToBool(env, args[5], true);\n        auto enableKeyExpire = NValueToInt32(env, args[6], -1);\n        auto expiredInSeconds = NValueToInt32(env, args[7], 0);\n        auto enableCompareBeforeSet = NValueToBool(env, args[8], true);\n        auto recover = NValueToInt32(env, args[9], -1);\n        auto itemSizeLimit = NValueToUInt32(env, args[10], 0);\n\n        auto config = MMKVConfig();\n        config.mode = (MMKVMode) mode;\n        config.rootPath = rootPath.empty() ? nullptr : &rootPath;\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey.empty() ? nullptr : &cryptKey;\n        config.expectedCapacity = expectedCapacity;\n        if (enableKeyExpire >= 0) {\n            config.enableKeyExpire = (enableKeyExpire != 0);\n        }\n        config.expiredInSeconds = expiredInSeconds;\n        config.enableCompareBeforeSet = enableCompareBeforeSet;\n        if (recover >= 0) {\n            config.recover = static_cast<MMKVRecoverStrategic>(recover);\n        }\n        config.itemSizeLimit = itemSizeLimit;\n\n        // MMKVInfo(\"rootPath: %p, %s, %s\", rootPathPtr, rootPath.c_str(), rootPathPtr ? rootPathPtr->c_str() : \"\");\n        kv = MMKV::mmkvWithID(mmapID, config);\n    }\n\n    return UInt64ToNValue(env, (uint64_t) kv);\n}\n\nstatic napi_value mmkvWithAshmemFD(napi_env env, napi_callback_info info) {\n    size_t argc = 10;\n    napi_value args[10] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    MMKV *kv = nullptr;\n    auto mmapID = NValueToString(env, args[0]);\n    if (!mmapID.empty()) {\n        int32_t fd = NValueToInt32(env, args[1]);\n        int32_t metaFD = NValueToInt32(env, args[2]);\n        auto cryptKey = NValueToString(env, args[3], true);\n        auto aes256 = NValueToBool(env, args[4], true);\n        auto enableKeyExpire = NValueToInt32(env, args[5], -1);\n        auto expiredInSeconds = NValueToInt32(env, args[6], 0);\n        auto enableCompareBeforeSet = NValueToBool(env, args[7], true);\n        auto recover = NValueToInt32(env, args[8], -1);\n        auto itemSizeLimit = NValueToUInt32(env, args[9], 0);\n\n        auto config = MMKVConfig();\n        config.aes256 = aes256;\n        config.cryptKey = cryptKey.empty() ? nullptr : &cryptKey;\n        if (enableKeyExpire >= 0) {\n            config.enableKeyExpire = (enableKeyExpire != 0);\n        }\n        config.expiredInSeconds = expiredInSeconds;\n        config.enableCompareBeforeSet = enableCompareBeforeSet;\n        if (recover >= 0) {\n            config.recover = static_cast<MMKVRecoverStrategic>(recover);\n        }\n        config.itemSizeLimit = itemSizeLimit;\n\n        kv = MMKV::mmkvWithAshmemFD(mmapID, fd, metaFD, config);\n    }\n\n    return UInt64ToNValue(env, (uint64_t)kv);\n}\n\nstatic napi_value mmapID(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return StringToNValue(env, kv->mmapID());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value encodeBool(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToBool(env,args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env,false);\n}\n\nstatic napi_value decodeBool(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToBool(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getBool(key, defaultValue, &hasValue);\n        if(hasValue) {\n            return BoolToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeInt32(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToInt32(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeInt32(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToInt32(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getInt32(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return Int32ToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeUInt32(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToUInt32(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeUInt32(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToUInt32(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getUInt32(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return UInt32ToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeInt64(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToInt64(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeInt64(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToInt64(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getInt64(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return Int64ToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeUInt64(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToUInt64(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeUInt64(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToUInt64(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getUInt64(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return UInt64ToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeFloat(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        float value = NValueToDouble(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeFloat(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToDouble(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getFloat(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return DoubleToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeDouble(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToDouble(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeDouble(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto defaultValue = NValueToDouble(env, args[2]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        bool hasValue = false;\n        auto ret = kv->getDouble(key, defaultValue, &hasValue);\n        if (hasValue) {\n            return DoubleToNValue(env, ret);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeString(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToString(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeString(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        string result;\n        if (kv->getString(key, result)) {\n            return StringToNValue(env, result);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeStringSet(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToStringArray(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeStringSet(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        vector<string> result;\n        if (kv->getVector(key, result)) {\n            return StringArrayToNValue(env, result);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeNumberSet(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToDoubleArray(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeNumberSet(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        vector<double> result;\n        if (kv->getVector(key, result)) {\n            return DoubleArrayToNValue(env, result);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeBoolSet(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto value = NValueToBoolArray(env, args[2]);\n        if (IsNValueUndefined(env, args[3])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[3]);\n        auto ret = kv->set(value, key, expiration);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeBoolSet(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        vector<bool> result;\n        if (kv->getVector(key, result)) {\n            return BoolArrayToNValue(env, result);\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeBytes(napi_env env, napi_callback_info info) {\n    size_t argc = 6;\n    napi_value args[6] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto buffer = NValueToMMBuffer(env, args[2]);\n        auto offset = NValueToUInt32(env, args[3]);\n        auto length = NValueToUInt32(env, args[4]);\n        auto value = MMBuffer((uint8_t *)buffer.getPtr() + offset, length, mmkv::MMBufferNoCopy);\n        if (IsNValueUndefined(env, args[5])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[5]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value decodeBytes(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        MMBuffer result;\n        if (kv->getBytes(key, result)) {\n            return MMBufferToNValue(env, std::move(result));\n        }\n    }\n    return args[2];\n}\n\ntemplate <typename T>\nnapi_value encodeArray(napi_env env, napi_callback_info info) {\n    size_t argc = 6;\n    napi_value args[6] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto buffer = NValueToMMBuffer(env, args[2]);\n        auto offset = NValueToUInt32(env, args[3]);\n        auto length = NValueToUInt32(env, args[4]);\n        auto ptr = (uint8_t *)buffer.getPtr() + offset;\n        std::span<T> value((T *)ptr, length / sizeof(T));\n        if (IsNValueUndefined(env, args[5])) {\n            auto ret = kv->set(value, key);\n            return BoolToNValue(env, ret);\n        }\n        uint32_t expiration = NValueToUInt32(env, args[5]);\n        auto ret = kv->set(value, key, expiration);\n        return BoolToNValue(env, ret);\n    }\n    return BoolToNValue(env, false);\n}\n\ntemplate <typename T>\nnapi_value decodeArray(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        std::vector<T> vec;\n        if (kv->getVector(key, vec)) {\n            MMBuffer result(vec.data(), vec.size() * sizeof(T));\n            return MMBufferToNValue(env, std::move(result));\n        }\n    }\n    return args[2];\n}\n\nstatic napi_value encodeInt32Array(napi_env env, napi_callback_info info) {\n    return encodeArray<int32_t>(env, info);\n}\n\nstatic napi_value decodeInt32Array(napi_env env, napi_callback_info info) {\n    return decodeArray<int32_t>(env, info);\n}\n\nstatic napi_value encodeUInt32Array(napi_env env, napi_callback_info info) {\n    return encodeArray<uint32_t>(env, info);\n}\n\nstatic napi_value decodeUInt32Array(napi_env env, napi_callback_info info) {\n    return decodeArray<uint32_t>(env, info);\n}\n\nstatic napi_value encodeInt64Array(napi_env env, napi_callback_info info) {\n    return encodeArray<int64_t>(env, info);\n}\n\nstatic napi_value decodeInt64Array(napi_env env, napi_callback_info info) {\n    return decodeArray<int64_t>(env, info);\n}\n\nstatic napi_value encodeUInt64Array(napi_env env, napi_callback_info info) {\n    return encodeArray<uint64_t>(env, info);\n}\n\nstatic napi_value decodeUInt64Array(napi_env env, napi_callback_info info) {\n    return decodeArray<uint64_t>(env, info);\n}\n\nstatic napi_value containsKey(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n       return BoolToNValue(env, kv->containsKey(key));\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value removeValueForKey(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n       kv->removeValueForKey(key);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value removeValuesForKeys(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (!kv) {\n       return NAPIUndefined(env);\n    }\n\n    vector<string> keys = NValueToStringArray(env, args[1]);\n    if (keys.size() > 0) {\n       kv->removeValuesForKeys(keys);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value count(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto filterExpire = NValueToBool(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n       return UInt64ToNValue(env, kv->count(filterExpire));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value allKeys(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto filterExpire = NValueToBool(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        auto keys = kv->allKeys(filterExpire);\n        return StringArrayToNValue(env, keys);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value clearAll(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto keepSpace = NValueToBool(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearAll(keepSpace);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value sync(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto needSync = NValueToBool(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->sync(needSync ? MMKV_SYNC : MMKV_ASYNC);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value clearMemoryCache(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value totalSize(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return UInt64ToNValue(env, kv->totalSize());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value actualSize(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return UInt64ToNValue(env, kv->actualSize());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value lock(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->lock();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value unlock(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->unlock();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value tryLock(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->try_lock());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value getValueSize(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0) {\n        auto actualSize = NValueToBool(env, args[2]);\n        return UInt32ToNValue(env, kv->getValueSize(key, actualSize));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value trim(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->trim();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value importFrom(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto srcHandle = NValueToUInt64(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    MMKV *kvSrc = reinterpret_cast<MMKV *>(srcHandle);\n    if (kv && kvSrc) {\n       return UInt64ToNValue(env, kv->importFrom(kvSrc));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value mmkvClose(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->close();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value removeStorage(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto mmapID = NValueToString(env, args[0]);\n    if (!mmapID.empty()) {\n        auto rootPath = NValueToString(env, args[1], true);\n        if (rootPath.empty()) {\n            return BoolToNValue(env, MMKV::removeStorage(mmapID));\n        }\n        return BoolToNValue(env, MMKV::removeStorage(mmapID, &rootPath));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value isFileValid(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto mmapID = NValueToString(env, args[0]);\n    if (!mmapID.empty()) {\n        auto rootPath = NValueToString(env, args[1], true);\n        if (rootPath.empty()) {\n            return BoolToNValue(env, MMKV::isFileValid(mmapID));\n        }\n        return BoolToNValue(env, MMKV::isFileValid(mmapID, &rootPath));\n    }\n    return NAPIUndefined(env);\n}\n\n#ifndef MMKV_DISABLE_CRYPT\n\nstatic napi_value cryptKey(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return StringToNValue(env, kv->cryptKey());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value reKey(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto cryptKey = NValueToString(env, args[1], true);\n    auto aes256 = NValueToBool(env, args[2], true);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->reKey(cryptKey, aes256));\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value checkReSetCryptKey(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto cryptKey = NValueToString(env, args[1], true);\n    auto aes256 = NValueToBool(env, args[2], true);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        auto cryptKeyPtr = cryptKey.empty() ? nullptr : &cryptKey;\n        kv->checkReSetCryptKey(cryptKeyPtr, aes256);\n    }\n    return NAPIUndefined(env);\n}\n\n#endif\n\nstatic napi_value backupOneToDirectory(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto mmapID = NValueToString(env, args[0]);\n    auto dstDir = NValueToString(env, args[1]);\n    if (!mmapID.empty()) {\n        auto rootPath = NValueToString(env, args[2], true);\n        auto rootPathPtr = rootPath.empty() ? nullptr : &rootPath;\n        return BoolToNValue(env, MMKV::backupOneToDirectory(mmapID, dstDir, rootPathPtr));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value restoreOneFromDirectory(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto mmapID = NValueToString(env, args[0]);\n    auto srcDir = NValueToString(env, args[1]);\n    if (!mmapID.empty()) {\n        auto rootPath = NValueToString(env, args[2], true);\n        auto rootPathPtr = rootPath.empty() ? nullptr : &rootPath;\n        return BoolToNValue(env, MMKV::restoreOneFromDirectory(mmapID, srcDir, rootPathPtr));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value backupAllToDirectory(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto dstDir = NValueToString(env, args[0]);\n    if (!dstDir.empty()) {\n        return UInt64ToNValue(env, MMKV::backupAllToDirectory(dstDir));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value restoreAllFromDirectory(napi_env env, napi_callback_info info) {\n    size_t argc = 3;\n    napi_value args[3] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto srcDir = NValueToString(env, args[0]);\n    if (!srcDir.empty()) {\n        return UInt64ToNValue(env, MMKV::restoreAllFromDirectory(srcDir));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value enableAutoKeyExpire(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto expireDuration = NValueToUInt32(env, args[1]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->enableAutoKeyExpire(expireDuration));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value disableAutoKeyExpire(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->disableAutoKeyExpire());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value enableCompareBeforeSet(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->enableCompareBeforeSet();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value disableCompareBeforeSet(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->disableCompareBeforeSet();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value ashmemFD(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return Int32ToNValue(env, kv->ashmemFD());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value ashmemMetaFD(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return Int32ToNValue(env, kv->ashmemMetaFD());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value createNativeBuffer(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto size = NValueToUInt32(env, args[0]);\n    if (size != 0) {\n        auto ptr = malloc(size);\n        return UInt64ToNValue(env, (uintptr_t) ptr);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value destroyNativeBuffer(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto ptr = (void*) NValueToUInt64(env, args[0]);\n    auto size = NValueToUInt32(env, args[1]);\n    if (ptr != nullptr && size != 0) {\n        free(ptr);\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value writeValueToNativeBuffer(napi_env env, napi_callback_info info) {\n    size_t argc = 4;\n    napi_value args[4] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    auto key = NValueToString(env, args[1]);\n    auto ptr = (void*) NValueToUInt64(env, args[2]);\n    auto size = NValueToUInt32(env, args[3]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv && key.length() > 0 && ptr != nullptr && size != 0) {\n        return Int32ToNValue(env, kv->writeValueToBuffer(key, ptr, size));\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value isMultiProcess(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->isMultiProcess());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value isReadOnly(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        return BoolToNValue(env, kv->isReadOnly());\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value checkContentChanged(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto handle = NValueToUInt64(env, args[0]);\n    MMKV *kv = reinterpret_cast<MMKV *>(handle);\n    if (kv) {\n        kv->checkContentChanged();\n    }\n    return NAPIUndefined(env);\n}\n\nstatic napi_value getNameSpace(napi_env env, napi_callback_info info) {\n    size_t argc = 1;\n    napi_value args[1] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto rootPath = NValueToString(env, args[0]);\n    if (!rootPath.empty()) {\n        MMKV::nameSpace(rootPath);\n        return BoolToNValue(env, true);\n    }\n    return BoolToNValue(env, false);\n}\n\nstatic napi_value checkExist(napi_env env, napi_callback_info info) {\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n    NAPI_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));\n\n    auto mmapID = NValueToString(env, args[0]);\n    if (!mmapID.empty()) {\n        auto rootPath = NValueToString(env, args[1], true);\n        if (rootPath.empty()) {\n            return BoolToNValue(env, MMKV::checkExist(mmapID));\n        }\n        return BoolToNValue(env, MMKV::checkExist(mmapID, &rootPath));\n    }\n    return NAPIUndefined(env);\n}\n\nEXTERN_C_START\nstatic napi_value Init(napi_env env, napi_value exports) {\n    napi_property_descriptor desc[] = {\n        { \"initialize\", nullptr, initialize, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"version\", nullptr, version, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"pageSize\", nullptr, pageSize, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"getDefaultMMKV\", nullptr, getDefaultMMKV, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"mmkvWithID\", nullptr, mmkvWithID, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"mmapID\", nullptr, mmapID, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeBool\", nullptr, encodeBool, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeBool\", nullptr, decodeBool, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeInt32\", nullptr, encodeInt32, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeInt32\", nullptr, decodeInt32, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeUInt32\", nullptr, encodeUInt32, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeUInt32\", nullptr, decodeUInt32, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeInt64\", nullptr, encodeInt64, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeInt64\", nullptr, decodeInt64, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeUInt64\", nullptr, encodeUInt64, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeUInt64\", nullptr, decodeUInt64, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeFloat\", nullptr, encodeFloat, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeFloat\", nullptr, decodeFloat, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeDouble\", nullptr, encodeDouble, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeDouble\", nullptr, decodeDouble, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeString\", nullptr, encodeString, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeString\", nullptr, decodeString, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeStringSet\", nullptr, encodeStringSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeStringSet\", nullptr, decodeStringSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeNumberSet\", nullptr, encodeNumberSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeNumberSet\", nullptr, decodeNumberSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeBoolSet\", nullptr, encodeBoolSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeBoolSet\", nullptr, decodeBoolSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeBytes\", nullptr, encodeBytes, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeBytes\", nullptr, decodeBytes, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeInt32Array\", nullptr, encodeInt32Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeInt32Array\", nullptr, decodeInt32Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeUInt32Array\", nullptr, encodeUInt32Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeUInt32Array\", nullptr, decodeUInt32Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeInt64Array\", nullptr, encodeInt64Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeInt64Array\", nullptr, decodeInt64Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"encodeUInt64Array\", nullptr, encodeUInt64Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"decodeUInt64Array\", nullptr, decodeUInt64Array, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"containsKey\", nullptr, containsKey, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"count\", nullptr, count, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"allKeys\", nullptr, allKeys, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"removeValueForKey\", nullptr, removeValueForKey, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"removeValuesForKeys\", nullptr, removeValuesForKeys, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"clearAll\", nullptr, clearAll, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"clearMemoryCache\", nullptr, clearMemoryCache, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"actualSize\", nullptr, actualSize, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"totalSize\", nullptr, totalSize, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"sync\", nullptr, sync, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"lock\", nullptr, lock, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"unlock\", nullptr, unlock, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"tryLock\", nullptr, tryLock, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"getValueSize\", nullptr, getValueSize, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"close\", nullptr, mmkvClose, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"trim\", nullptr, trim, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"removeStorage\", nullptr, removeStorage, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"isFileValid\", nullptr, isFileValid, nullptr, nullptr, nullptr, napi_default, nullptr },\n#ifndef MMKV_DISABLE_CRYPT\n        { \"cryptKey\", nullptr, cryptKey, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"reKey\", nullptr, reKey, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"checkReSetCryptKey\", nullptr, checkReSetCryptKey, nullptr, nullptr, nullptr, napi_default, nullptr },\n#endif\n        { \"backupOneToDirectory\", nullptr, backupOneToDirectory, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"restoreOneFromDirectory\", nullptr, restoreOneFromDirectory, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"backupAllToDirectory\", nullptr, backupAllToDirectory, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"restoreAllFromDirectory\", nullptr, restoreAllFromDirectory, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"enableAutoKeyExpire\", nullptr, enableAutoKeyExpire, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"disableAutoKeyExpire\", nullptr, disableAutoKeyExpire, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"enableCompareBeforeSet\", nullptr, enableCompareBeforeSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"disableCompareBeforeSet\", nullptr, disableCompareBeforeSet, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"mmkvWithAshmemFD\", nullptr, mmkvWithAshmemFD, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"ashmemFD\", nullptr, ashmemFD, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"ashmemMetaFD\", nullptr, ashmemMetaFD, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"createNativeBuffer\", nullptr, createNativeBuffer, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"destroyNativeBuffer\", nullptr, destroyNativeBuffer, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"writeValueToNativeBuffer\", nullptr, writeValueToNativeBuffer, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"isMultiProcess\", nullptr, isMultiProcess, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"isReadOnly\", nullptr, isReadOnly, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"checkContentChanged\", nullptr, checkContentChanged, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"getNameSpace\", nullptr, getNameSpace, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"checkExist\", nullptr, checkExist, nullptr, nullptr, nullptr, napi_default, nullptr },\n        { \"importFrom\", nullptr, importFrom, nullptr, nullptr, nullptr, napi_default, nullptr },\n    };\n    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);\n    return exports;\n}\nEXTERN_C_END\n\nstatic napi_module mmkvModule = {\n    .nm_version = 1,\n    .nm_flags = 0,\n    .nm_filename = nullptr,\n    .nm_register_func = Init,\n    .nm_modname = \"mmkv\",\n    .nm_priv = ((void*)0),\n    .reserved = { 0 },\n};\n\nextern \"C\" __attribute__((constructor)) void RegisterMMKVModule(void) {\n    napi_module_register(&mmkvModule);\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/cpp/types/libmmkv/index.d.ts",
    "content": "export const initialize: (rootDir: string, cacheDir: string, logLevel: number, handler?: MMKVHandler) => string;\nexport const version: () => string;\nexport const pageSize: () => number;\nexport const getDefaultMMKV: (mode: number, cryptKey?: string, aes256?: boolean, expectedCapacity?: bigint,\n  enableKeyExpire?: number, expiredInSeconds?: number, enableCompareBeforeSet?: boolean, recover?: number,\n  itemSizeLimit?: number) => bigint;\nexport const mmkvWithID: (mmapID: string, mode: number, cryptKey?: string, rootPath?: string, expectedCapacity?: bigint,\n  aes256?: boolean, enableKeyExpire?: number, expiredInSeconds?: number, enableCompareBeforeSet?: boolean,\n  recover?: number, itemSizeLimit?: number) => bigint;\nexport const mmapID: (handle: bigint) => string;\nexport const encodeBool: (handle: bigint, key: string, value: boolean, expiration?: number) => boolean;\nexport const decodeBool: (handle: bigint, key: string, defaultValue?: boolean) => boolean;\nexport const encodeInt32: (handle: bigint, key: string, value: number, expiration?: number) => boolean;\nexport const decodeInt32: (handle: bigint, key: string, defaultValue?: number) => number;\nexport const encodeUInt32: (handle: bigint, key: string, value: number, expiration?: number) => boolean;\nexport const decodeUInt32: (handle: bigint, key: string, defaultValue?: number) => number;\nexport const encodeInt64: (handle: bigint, key: string, value: bigint, expiration?: number) => boolean;\nexport const decodeInt64: (handle: bigint, key: string, defaultValue?: bigint) => bigint;\nexport const encodeUInt64: (handle: bigint, key: string, value: bigint, expiration?: number) => boolean;\nexport const decodeUInt64: (handle: bigint, key: string, defaultValue?: bigint) => bigint;\nexport const encodeFloat: (handle: bigint, key: string, value: number, expiration?: number) => boolean;\nexport const decodeFloat: (handle: bigint, key: string, defaultValue?: number) => number;\nexport const encodeDouble: (handle: bigint, key: string, value: number, expiration?: number) => boolean;\nexport const decodeDouble: (handle: bigint, key: string, defaultValue?: number) => number;\nexport const encodeString: (handle: bigint, key: string, value: string, expiration?: number) => boolean;\nexport const decodeString: (handle: bigint, key: string, defaultValue?: string) => string;\nexport const encodeStringSet: (handle: bigint, key: string, value: string[], expiration?: number) => boolean;\nexport const decodeStringSet: (handle: bigint, key: string, defaultValue?: string[]) => string[];\nexport const encodeNumberSet: (handle: bigint, key: string, value: number[], expiration?: number) => boolean;\nexport const decodeNumberSet: (handle: bigint, key: string, defaultValue?: number[]) => number[];\nexport const encodeBoolSet: (handle: bigint, key: string, value: boolean[], expiration?: number) => boolean;\nexport const decodeBoolSet: (handle: bigint, key: string, defaultValue?: boolean[]) => boolean[];\nexport const encodeBytes: (handle: bigint, key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number) => boolean;\nexport const decodeBytes: (handle: bigint, key: string, defaultValue?: ArrayBuffer) => ArrayBuffer;\nexport const encodeInt32Array: (handle: bigint, key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number) => boolean;\nexport const decodeInt32Array: (handle: bigint, key: string, defaultValue?: ArrayBuffer) => ArrayBuffer;\nexport const encodeUInt32Array: (handle: bigint, key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number) => boolean;\nexport const decodeUInt32Array: (handle: bigint, key: string, defaultValue?: ArrayBuffer) => ArrayBuffer;\nexport const encodeInt64Array: (handle: bigint, key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number) => boolean;\nexport const decodeInt64Array: (handle: bigint, key: string, defaultValue?: ArrayBuffer) => ArrayBuffer;\nexport const encodeUInt64Array: (handle: bigint, key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number) => boolean;\nexport const decodeUInt64Array: (handle: bigint, key: string, defaultValue?: ArrayBuffer) => ArrayBuffer;\nexport const containsKey: (handle: bigint, key: string) => boolean;\nexport const count: (handle: bigint, filterExpire: boolean) => bigint;\nexport const allKeys: (handle: bigint, filterExpire: boolean) => string[];\nexport const removeValueForKey: (handle: bigint, key: string) => void;\nexport const removeValuesForKeys: (handle: bigint, keys: string[]) => void;\nexport const clearAll: (handle: bigint, keepSpace: boolean) => void;\nexport const totalSize: (handle: bigint) => bigint;\nexport const actualSize: (handle: bigint) => bigint;\nexport const sync: (handle: bigint, sync: boolean) => void;\nexport const clearMemoryCache: (handle: bigint) => void;\nexport const lock: (handle: bigint) => void;\nexport const unlock: (handle: bigint) => void;\nexport const tryLock: (handle: bigint) => boolean;\nexport const getValueSize: (handle: bigint, key: string, actualSize?: boolean) => number;\nexport const trim: (handle: bigint) => void;\nexport const close: (handle: bigint) => void;\nexport const removeStorage: (mmapID: string, rootPath?: string) => boolean;\nexport const isFileValid: (mmapID: string, rootPath?: string) => boolean;\nexport const cryptKey: (handle: bigint) => string;\nexport const reKey: (handle: bigint, newKey?: string, aes256?: boolean) => boolean;\nexport const checkReSetCryptKey: (handle: bigint, newKey?: string, aes256?: boolean) => void;\nexport const backupOneToDirectory: (mmapID: string, dstDir: string, rootPath?: string) => boolean;\nexport const restoreOneFromDirectory: (mmapID: string, srcDir: string, rootPath?: string) => boolean;\nexport const backupAllToDirectory: (dstDir: string) => bigint;\nexport const restoreAllFromDirectory: (srcDir: string) => bigint;\nexport const enableAutoKeyExpire: (handle: bigint, expireDurationInSecond: number) => boolean;\nexport const disableAutoKeyExpire: (handle: bigint) => boolean;\nexport const enableCompareBeforeSet: (handle: bigint) => void;\nexport const disableCompareBeforeSet: (handle: bigint) => void;\nexport const mmkvWithAshmemFD: (mmapID: string, fd: number, metaFD: number, cryptKey?: string, aes256?: boolean,\n  enableKeyExpire?: number, expiredInSeconds?: number, enableCompareBeforeSet?: boolean, recover?: number,\n  itemSizeLimit?: number) => bigint;\nexport const ashmemFD: (handle: bigint) => number;\nexport const ashmemMetaFD: (handle: bigint) => number;\nexport const createNativeBuffer: (size: number) => bigint;\nexport const destroyNativeBuffer: (ptr: bigint, size: number) => void;\nexport const writeValueToNativeBuffer: (handle: bigint, key: string, ptr: bigint, size: number) => number;\nexport const isMultiProcess: (handle: bigint) => boolean;\nexport const isReadOnly: (handle: bigint) => boolean;\nexport const checkContentChanged:(handle: bigint) => void;\nexport const getNameSpace:(rootPath: string) => boolean;\nexport const checkExist: (mmapID: string, rootPath?: string) => boolean;\nexport const importFrom: (handle: bigint, srcHandle: bigint) => bigint;\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/cpp/types/libmmkv/oh-package.json5",
    "content": "{\n  \"name\": \"libmmkv.so\",\n  \"types\": \"./index.d.ts\",\n  \"version\": \"\",\n  \"description\": \"Please describe the basic information.\"\n}"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/MMKV.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport native from 'libmmkv.so';\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { MMKVLogLevel } from './MMKVLogLevel';\nimport { getObjKeys } from './Util';\nimport { NativeBuffer } from './NativeBuffer';\nimport { MMKVHandler } from './MMKVHandler';\nimport common from '@ohos.app.ability.common';\nimport dataPreferences from '@ohos.data.preferences';\nimport { MMKVConfig } from './MMKVConfig';\n\ndeclare namespace internal {\n    type ArrayValueType = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array;\n}\n\n/**\n * An highly efficient, reliable, multi-process key-value storage framework.\n * THE PERFECT drop-in replacement for Preferences.\n */\nexport class MMKV {\n    /**\n     * Single-process mode. The default mode on an MMKV instance.\n     */\n    public static readonly SINGLE_PROCESS_MODE: number = 1 << 0\n    /**\n     * Multi-process mode.\n     * To enable multi-process accessing of an MMKV instance, you must set this mode whenever you getting that instance.\n     */\n    public static readonly MULTI_PROCESS_MODE: number = 1 << 1\n    private static readonly ASHMEM_MODE: number = 1 << 3\n    private static readonly BACKUP_MODE: number = 1 << 4\n    public static readonly READ_ONLY_MODE: number = 1 << 5\n    private static g_rootDir: string\n\n    private static initData() {\n        // MMKV.checkedHandleSet = new Set<string>()\n    }\n\n    /**\n     * Initialize MMKV with default configuration.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     *\n     * @param context The ApplicationContext of the App, usually from UIAbility.\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel.LevelInfo}.\n     * @param handler Handle log redirecting & crc/file check failure.\n     * @returns The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static initialize(context: common.ApplicationContext, logLevel?: MMKVLogLevel, handler?: MMKVHandler): string {\n        let rootDir = context.filesDir + \"/mmkv\";\n        let cacheDir = context.cacheDir;\n        // hilog.info(0x0000, 'mmkv', '%{public}s, %{public}s, %{public}d', rootDir, cacheDir, logLevel);\n        return MMKV.initializeWithPath(rootDir, cacheDir, logLevel, handler);\n    }\n\n    /**\n     * Initialize MMKV with customize settings.\n     * You must call one of the initialize() methods on App startup process before using MMKV.\n     * @param rootDir The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     * @param cacheDir The cache folder of MMKV, defaults to $(CacheDir).\n     * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel.LevelInfo}.\n     * @param handler Handle log redirecting & crc/file check failure.\n     * @returns The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static initializeWithPath(rootDir: string, cacheDir: string, logLevel?: MMKVLogLevel, handler?: MMKVHandler): string {\n        MMKV.initData();\n        let level = logLevel ?? MMKVLogLevel.LevelInfo;\n        // hilog.info(0x0000, 'mmkv', '%{public}s, %{public}s, %{public}d', rootDir, cacheDir, level);\n        MMKV.g_rootDir = native.initialize(rootDir, cacheDir, level, handler);\n        return MMKV.g_rootDir;\n    }\n\n    /**\n     * @param rootDir the customize root directory of a NameSpace\n     * @returns a NameSpace with custom root dir\n     */\n    public static nameSpace(rootDir: string): NameSpace {\n        if (native.getNameSpace(rootDir)) {\n            return new NameSpace(rootDir);\n        }\n        throw new Error('Invalid rootDir: [' + rootDir + '].');\n    }\n\n    /**\n     * identical with the original MMKV with the global root dir\n     */\n    public static defaultNameSpace(): NameSpace {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        return new NameSpace(MMKV.g_rootDir);\n    }\n\n    /**\n     * @returns The version of MMKV.\n     */\n    public static get version(): string {\n        return native.version();\n    }\n\n    /**\n     * @returns The device's memory page size.\n     */\n    public static get pageSize(): number {\n        return native.pageSize();\n    }\n\n    /**\n     * @returns The root folder of MMKV, defaults to $(FilesDir)/mmkv.\n     */\n    public static get rootDir(): string {\n        return MMKV.g_rootDir;\n    }\n\n    private readonly nativeHandle: bigint;\n\n    /**\n     * Never use this constructor directly. It's visible only because ArkTS has no package-internal access control.\n     * @param handle The native handle of this instance.\n     */\n    constructor(handle: bigint) {\n        this.nativeHandle = handle;\n    }\n\n    /**\n     * Create the default MMKV instance in customize process mode, with an encryption key.\n     * @param mode The process mode of the MMKV instance, defaults to {@link MMKV.SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @returns The default MMKV instance.\n     */\n    public static defaultMMKV(mode?: number, cryptKey?: string, aes256?: boolean): MMKV {\n        return MMKV.defaultMMKVWithConfig({mode: mode, cryptKey: cryptKey, aes256: aes256});\n    }\n\n    /**\n     * Create the default MMKV instance with all-in-one configuration.\n     * @param config The all-in-one configuration.\n     * @returns The default MMKV instance.\n     */\n    public static defaultMMKVWithConfig(config: MMKVConfig): MMKV {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        let enableKeyExpire = (config.enableKeyExpire == undefined) ? -1 : (config.enableKeyExpire == true ? 1 : 0);\n        let mode = config.mode ?? MMKV.SINGLE_PROCESS_MODE;\n        let handle: bigint = native.getDefaultMMKV(mode, config.cryptKey, config.aes256, config.expectedCapacity,\n            enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet, config.recover,\n            config.itemSizeLimit);\n        if (!handle) {\n            throw new Error('Fail to create the default MMKV instance in NAPI.');\n        }\n        return new MMKV(handle);\n    }\n\n    /**\n     * Create an MMKV instance.\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode The process mode of the MMKV instance, defaults to {@link MMKV.SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     * @param expectedCapacity The file size you expected when opening or creating file.\n     * @returns The MMKV instance.\n     */\n    public static mmkvWithID(mmapID: string, mode?: number, cryptKey?: string, rootPath?: string,\n        expectedCapacity?: bigint, aes256?: boolean): MMKV {\n        return MMKV.mmkvWithConfig(mmapID, {mode: mode, cryptKey: cryptKey, rootPath: rootPath,\n            expectedCapacity: expectedCapacity, aes256: aes256});\n    }\n\n    /**\n     * Creat an MMKV instance with all-in-one configuration\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param config The all-in-one configuration.\n     * @returns The MMKV instance.\n     */\n    public static mmkvWithConfig(mmapID: string, config: MMKVConfig): MMKV {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        let enableKeyExpire = (config.enableKeyExpire == undefined) ? -1 : (config.enableKeyExpire == true ? 1 : 0);\n        let mode = config.mode ?? MMKV.SINGLE_PROCESS_MODE;\n        let handle: bigint = native.mmkvWithID(mmapID, mode, config.cryptKey, config.rootPath, config.expectedCapacity,\n            config.aes256, enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet, config.recover,\n            config.itemSizeLimit);\n        if (!handle) {\n            throw new Error('Fail to create an MMKV instance [' + mmapID + '] in NAPI.');\n        }\n        return new MMKV(handle);\n    }\n\n    /**\n     * @returns The unique ID of the MMKV instance.\n     */\n    public get mmapID(): string {\n        return native.mmapID(this.nativeHandle);\n    }\n\n    /**\n     * Set boolean value with customize expiration in seconds.\n     *\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeBool(key: string, value: boolean, expiration?: number): boolean {\n        return native.encodeBool(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get boolean value.\n     * @returns { boolean | undefined }\n     */\n    public decodeBool(key: string, defaultValue?: boolean): boolean | undefined {\n        return native.decodeBool(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set int32 value with customize expiration in seconds.\n     * @param value { number }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeInt32(key: string, value: number, expiration?: number): boolean {\n        return native.encodeInt32(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get int32 value.\n     * @returns { number | undefined }\n     */\n    public decodeInt32(key: string, defaultValue?: number): number | undefined {\n        return native.decodeInt32(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set uint32 value with customize expiration in seconds.\n     * @param value { number }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeUInt32(key: string, value: number, expiration?: number): boolean {\n        return native.encodeUInt32(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get uint32 value.\n     * @returns { number | undefined }\n     */\n    public decodeUInt32(key: string, defaultValue?: number): number | undefined {\n        return native.decodeUInt32(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set int64 value with customize expiration in seconds.\n     * @param value { bigint }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeInt64(key: string, value: bigint, expiration?: number): boolean {\n        return native.encodeInt64(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get int64 value.\n     * @returns { bigint | undefined }\n     */\n    public decodeInt64(key: string, defaultValue?: bigint): bigint | undefined {\n        return native.decodeInt64(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set uint64 value with customize expiration in seconds.\n     * @param value { bigint }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeUInt64(key: string, value: bigint, expiration?: number): boolean {\n        return native.encodeUInt64(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get uint64 value.\n     * @returns { bigint | undefined }\n     */\n    public decodeUInt64(key: string, defaultValue?: bigint): bigint | undefined {\n        return native.decodeUInt64(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set float value with customize expiration in seconds.\n     * @param value { number }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeFloat(key: string, value: number, expiration?: number): boolean {\n        return native.encodeFloat(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get float value.\n     * @returns { number | undefined }\n     */\n    public decodeFloat(key: string, defaultValue?: number): number | undefined {\n        return native.decodeFloat(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set double value with customize expiration in seconds.\n     * @param value { number }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeDouble(key: string, value: number, expiration?: number): boolean {\n        return native.encodeDouble(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get double value.\n     * @returns { number | undefined }\n     */\n    public decodeDouble(key: string, defaultValue?: number): number | undefined {\n        return native.decodeDouble(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set string value with customize expiration in seconds.\n     * @param value { string }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeString(key: string, value: string, expiration?: number): boolean {\n        return native.encodeString(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get string value.\n     * @returns { string | undefined }\n     */\n    public decodeString(key: string, defaultValue?: string): string | undefined {\n        return native.decodeString(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set string array value with customize expiration in seconds.\n     * @param value { string[] }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeStringSet(key: string, value: string[], expiration?: number): boolean {\n        return native.encodeStringSet(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get string array value.\n     * @returns { string[] | undefined }\n     */\n    public decodeStringSet(key: string, defaultValue?: string[]): string[] | undefined {\n        return native.decodeStringSet(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set number array value with customize expiration in seconds.\n     * @param value { number[] }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeNumberSet(key: string, value: number[], expiration?: number): boolean {\n        return native.encodeNumberSet(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get number array value.\n     * @returns { number[] | undefined }\n     */\n    public decodeNumberSet(key: string, defaultValue?: number[]) : number[] | undefined {\n        return native.decodeNumberSet(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set boolean array value with customize expiration in seconds.\n     * @param value { boolean[] }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeBoolSet(key: string, value: boolean[], expiration?: number): boolean {\n        return native.encodeBoolSet(this.nativeHandle, key, value, expiration);\n    }\n\n    /**\n     * Get boolean array value.\n     * @returns { boolean[] | undefined }\n     */\n    public decodeBoolSet(key: string, defaultValue?: boolean[]): boolean[] | undefined {\n        return native.decodeBoolSet(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Set ArrayBuffer value with customize expiration in seconds.\n     * @param value { ArrayBuffer }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeBytes(key: string, value: ArrayBuffer, expiration?: number): boolean {\n        return native.encodeBytes(this.nativeHandle, key, value, 0, value.byteLength, expiration);\n    }\n\n    /**\n     * Set a view of a ArrayBuffer value and customize expiration in seconds.\n     * @param value { ArrayBuffer }\n     * @param offset the offset from the start of the buffer\n     * @param length the total length of the view of the buffer\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeBytesPart(key: string, value: ArrayBuffer, offset: number, length: number, expiration?: number): boolean {\n        return native.encodeBytes(this.nativeHandle, key, value, offset, length, expiration);\n    }\n\n    /**\n     * Get ArrayBuffer value.\n     * @returns { ArrayBuffer | undefined }\n     */\n    public decodeBytes(key: string, defaultValue?: ArrayBuffer): ArrayBuffer | undefined {\n        return native.decodeBytes(this.nativeHandle, key, defaultValue);\n    }\n\n    /**\n     * Encode a typed array.\n     * @param value { Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array }\n     * @param expiration override the default duration, {@link MMKV.ExpireNever} (0) means never expire.\n     * @returns true if success, false if failure.\n     */\n    public encodeTypedArray(key: string, value: internal.ArrayValueType, expiration?: number): boolean {\n        if (value instanceof Int32Array) {\n            return native.encodeInt32Array(this.nativeHandle, key, value.buffer, value.byteOffset, value.byteLength, expiration);\n        } else if (value instanceof Uint32Array) {\n            return native.encodeUInt32Array(this.nativeHandle, key, value.buffer, value.byteOffset, value.byteLength, expiration);\n        } else if (value instanceof BigInt64Array) {\n            return native.encodeInt64Array(this.nativeHandle, key, value.buffer, value.byteOffset, value.byteLength, expiration);\n        } else if (value instanceof BigUint64Array) {\n            return native.encodeUInt64Array(this.nativeHandle, key, value.buffer, value.byteOffset, value.byteLength, expiration);\n        }\n        return native.encodeBytes(this.nativeHandle, key, value.buffer, value.byteOffset, value.byteLength, expiration);\n    }\n\n    /**\n     * Get Int8Array value.\n     * @returns { Int8Array | undefined }\n     */\n    public decodeInt8Array(key: string, defaultValue?: Int8Array): Int8Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Int8Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Uint8Array value.\n     * @returns { Uint8Array | undefined }\n     */\n    public decodeUint8Array(key: string, defaultValue?: Uint8Array): Uint8Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Uint8Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Uint8ClampedArray value.\n     * @returns { Uint8ClampedArray | undefined }\n     */\n    public decodeUint8ClampedArray(key: string, defaultValue?: Uint8ClampedArray): Uint8ClampedArray | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Uint8ClampedArray(buffer) : undefined;\n    }\n\n    /**\n     * Get Int16Array value.\n     * @returns { Int16Array | undefined }\n     */\n    public decodeInt16Array(key: string, defaultValue?: Int16Array): Int16Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Int16Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Uint16Array value.\n     * @returns { Uint16Array | undefined }\n     */\n    public decodeUint16Array(key: string, defaultValue?: Uint16Array): Uint16Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Uint16Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Int32Array value.\n     * @returns { Int32Array | undefined }\n     */\n    public decodeInt32Array(key: string, defaultValue?: Int32Array): Int32Array | undefined {\n        let buffer = native.decodeInt32Array(this.nativeHandle, key, defaultValue);\n        return buffer ? new Int32Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Uint32Array value.\n     * @returns { Uint32Array | undefined }\n     */\n    public decodeUint32Array(key: string, defaultValue?: Uint32Array): Uint32Array | undefined {\n        let buffer = native.decodeUInt32Array(this.nativeHandle, key, defaultValue);\n        return buffer ? new Uint32Array(buffer) : undefined;\n    }\n\n    /**\n     * Get BigInt64Array value.\n     * @returns { BigInt64Array | undefined }\n     */\n    public decodeInt64Array(key: string, defaultValue?: BigInt64Array): BigInt64Array | undefined {\n        let buffer = native.decodeInt64Array(this.nativeHandle, key, defaultValue);\n        return buffer ? new BigInt64Array(buffer) : undefined;\n    }\n\n    /**\n     * Get BigUint64Array value.\n     * @returns { BigUint64Array | undefined }\n     */\n    public decodeUint64Array(key: string, defaultValue?: BigUint64Array): BigUint64Array | undefined {\n        let buffer = native.decodeUInt64Array(this.nativeHandle, key, defaultValue);\n        return buffer ? new BigUint64Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Float32Array value.\n     * @returns { Float32Array | undefined }\n     */\n    public decodeFloat32Array(key: string, defaultValue?: Float32Array): Float32Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Float32Array(buffer) : undefined;\n    }\n\n    /**\n     * Get Float64Array value.\n     * @returns { Float64Array | undefined }\n     */\n    public decodeFloat64Array(key: string, defaultValue?: Float64Array): Float64Array | undefined {\n        let buffer = native.decodeBytes(this.nativeHandle, key, defaultValue ? defaultValue.buffer : undefined);\n        return buffer ? new Float64Array(buffer) : undefined;\n    }\n\n    /**\n     * Check whether or not MMKV contains the key.\n     *\n     * @param key The key of the value.\n     */\n    public containsKey(key: string): boolean {\n        return native.containsKey(this.nativeHandle, key);\n    }\n\n    /**\n     * Get all the key.\n     * @param filterExpire true to filter all non-expired keys. Note that this has costs.\n     * @return { string[] }\n     */\n    public allKeys(filterExpire?: boolean): string[] {\n        return native.allKeys(this.nativeHandle, filterExpire ?? false);\n    }\n\n    /**\n     * @param filterExpire  true to filter all non-expired keys. Note that this has costs.\n     * @return The total count of all the keys.\n     */\n    public count(filterExpire?: boolean): bigint {\n        return native.count(this.nativeHandle, filterExpire ?? false);\n    }\n\n    /**\n     * Remove the value for one key.\n     */\n    public removeValueForKey(key: string): void {\n        native.removeValueForKey(this.nativeHandle, key);\n    }\n\n    /**\n     * Batch remove some keys from the MMKV instance.\n     *\n     * @param arrKeys { string[] } The keys to be removed.\n     */\n    public removeValuesForKeys(keys: string[]): void {\n        native.removeValuesForKeys(this.nativeHandle, keys);\n    }\n\n    /**\n     * Clear all the key-values inside the MMKV instance.\n     * The data file will be trimmed down to `pageSize`, and some sync operations will be called\n     * @param keepSpace If you do not want to trim the file, pass true for better performance\n     */\n    public clearAll(keepSpace?: boolean): void {\n        return native.clearAll(this.nativeHandle, keepSpace ?? false);\n    }\n\n    /**\n     * Get the size of the underlying file. Align to the disk block size, typically 4K for an Android device.\n     */\n    public totalSize(): bigint {\n        return native.totalSize(this.nativeHandle);\n    }\n\n    /**\n     * Get the actual used size of the MMKV instance.\n     * This size might increase and decrease as MMKV doing insertion and full write back.\n     */\n    public actualSize(): bigint {\n        return native.actualSize(this.nativeHandle);\n    }\n\n    /**\n     * Save all mmap memory to file.\n     * You don't need to call this, really, I mean it.\n     * Unless you worry about the device running out of battery.\n     * @param sync true for synchronously sync, false for asynchronously sync\n     */\n    public sync(sync?: boolean): void {\n        return native.sync(this.nativeHandle, sync ?? false);\n    }\n\n    /**\n     * Exclusively inter-process lock the MMKV instance.\n     * It will block and wait until it successfully locks the file.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     */\n    public lock(): void {\n        return native.lock(this.nativeHandle);\n    }\n\n    /**\n     * Exclusively inter-process unlock the MMKV instance.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     */\n    public unlock(): void {\n        return native.unlock(this.nativeHandle);\n    }\n\n    /**\n     * Try exclusively inter-process lock the MMKV instance.\n     * It will not block if the file has already been locked by another process.\n     * It will make no effect if the MMKV instance is created with {@link #SINGLE_PROCESS_MODE}.\n     *\n     * @return true if successfully locked, otherwise return immediately with false.\n     */\n    public tryLock(): boolean {\n        return native.tryLock(this.nativeHandle);\n    }\n\n    /**\n     * The {@link MMKV.totalSize()} of an MMKV instance won't reduce after deleting key-values,\n     * call this method after lots of deleting if you care about disk usage.\n     * Note that {@link MMKV.clearAll()}  has a similar effect.\n     */\n    public trim(): void {\n        return native.trim(this.nativeHandle);\n    }\n\n    /**\n     * import all key-value items from src\n     * @returns count of items imported\n     */\n    public importFrom(src: MMKV): bigint {\n        return native.importFrom(this.nativeHandle, src.nativeHandle);\n    }\n\n    /**\n     * Call this method if the MMKV instance is no longer needed in the near future.\n     * Any subsequent call to any MMKV instances with the same ID is undefined behavior.\n     */\n    public close(): void {\n        return native.close(this.nativeHandle);\n    }\n\n    /**\n     * Clear memory cache of the MMKV instance.\n     * You can call it on memory warning.\n     * Any subsequent call to the MMKV instance will trigger all key-values loading from the file again.\n     */\n    public clearMemoryCache(): void {\n        return native.clearMemoryCache(this.nativeHandle);\n    }\n\n    /**\n     * Get the actual size consumption of the key's value.\n     * Note: might be a little bigger than value's length.\n     * @param key The key of the value.\n     * @param actualSize true to get the actual size of the key's value. String's length or byte[]'s length, etc.\n     */\n    public getValueSize(key: string, actualSize?: boolean): number {\n        return native.getValueSize(this.nativeHandle, key, actualSize);\n    }\n\n    /**\n     * remove the storage of the MMKV, including the data file & meta file (.crc)\n     * Note: the existing instance (if any) will be closed & destroyed\n     */\n    public static removeStorage(mmapID: string, rootPath?: string): boolean {\n        return native.removeStorage(mmapID, rootPath);\n    }\n\n    /**\n     * Check whether the MMKV file is valid or not on customize folder.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.\n     */\n    public static isFileValid(mmapID: string, rootPath?: string): boolean {\n        return native.isFileValid(mmapID, rootPath);\n    }\n\n    /**\n     * @return The encryption key (no more than 16 bytes).\n     */\n    public get cryptKey(): string | undefined {\n        return native.cryptKey(this.nativeHandle);\n    }\n\n    /**\n     * Transform plain text into encrypted text, or vice versa by passing a null encryption key.\n     * You can also change existing crypt key with a different cryptKey.\n     *\n     * @param cryptKey The new encryption key (no more than 16 bytes).\n     * @return true if success, otherwise false.\n     */\n    public reKey(newKey?: string, aes256?: boolean): boolean {\n        return native.reKey(this.nativeHandle, newKey, aes256);\n    }\n\n    /**\n     * Just reset the encryption key (will not encrypt or decrypt anything).\n     * Usually you should call this method after another process has {@link MMKV.reKey(String)} the multi-process MMKV instance.\n     *\n     * @param cryptKey The new encryption key (no more than 16 bytes).\n     */\n    public checkReSetCryptKey(newKey?: string, aes256?: boolean): void {\n        return native.checkReSetCryptKey(this.nativeHandle, newKey, aes256);\n    }\n\n    /**\n     * Atomically migrate all key-values from an existent Preferences to the MMKV instance.\n     * @param preferences The SharedPreferences to import from.\n     * @return The total count of key-values imported.\n     */\n    public importFromPreferences(preferences: dataPreferences.Preferences): bigint {\n        hilog.info(0x0000, 'mmkv', 'import from Preferences to %{public}s', this.mmapID);\n        let all = preferences.getAllSync();\n        let allKeys = getObjKeys(all);\n        let allKV = all as Record<string, Object>;\n        let count = BigInt(0);\n        for (let index = 0; index < allKeys.length; index++) {\n            const key = allKeys[index];\n            const value = allKV[key];\n            if (typeof value == 'string') {\n                this.encodeString(key, value as string);\n            } else if (typeof value == 'boolean') {\n                this.encodeBool(key, value as boolean);\n            } else if (typeof value == 'number') {\n                // there's no way to tell whether a number is integer or float/double\n                this.encodeDouble(key, value as number);\n            } else if (typeof value == 'object') {\n                if (Array.isArray(value)) {\n                    if (value.length == 0) {\n                        // assume it's Array<string>, the type doesn't really matter for an empty Array\n                        this.encodeStringSet(key, value as string[]);\n                    } else {\n                        // Array<number>、Array<string>、Array<boolean>、Uint8Array\n                        let first: Object = value[0];\n                        if (typeof first == 'string') {\n                            let arrStr = value as string[];\n                            this.encodeStringSet(key, arrStr);\n                        } else if (typeof first == 'boolean') {\n                            let arrBool = value as boolean[];\n                            this.encodeBoolSet(key, arrBool);\n                        } else if (typeof first == 'number') {\n                            let arrNumber = value as number[];\n                            this.encodeNumberSet(key, arrNumber);\n                        } else {\n                            hilog.warn(0x0000, 'mmkv', 'unsupported Array<%{public}s> type on key %{public}s',\n                                typeof first, key);\n                            continue;\n                        }\n                    }\n                } else if (value instanceof Uint8Array) {\n                    let arr = value as Uint8Array;\n                    this.encodeBytes(key, arr.buffer as ArrayBuffer);\n                } else {\n                    hilog.warn(0x0000, 'mmkv', 'unsupported Object type on key %{public}s', key);\n                    continue;\n                }\n            } else {\n                hilog.warn(0x0000, 'mmkv', 'unsupported %{public}s type on key %{public}s', typeof value, key);\n                continue;\n            }\n            count++;\n        }\n        return count;\n    }\n\n    /**\n     * Get an backed-up MMKV instance.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param rootPath The backup folder of the MMKV instance.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     */\n    public static backedUpMMKVWithID(mmapID: string, rootPath: string, mode?: number, cryptKey?: string): MMKV {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        let realMode = (mode ?? MMKV.SINGLE_PROCESS_MODE) | MMKV.BACKUP_MODE\n        // hilog.info(0x0000, 'mmkv', 'realMode %{public}d for mmapID %{public}s', realMode, mmapID);\n        return MMKV.mmkvWithID(mmapID, realMode, cryptKey, rootPath);\n    }\n\n    /**\n     * backup one MMKV instance to dstDir\n     * @param mmapID   the MMKV ID to backup\n     * @param dstDir   the backup destination directory\n     * @param rootPath the customize root path of the MMKV, if null then backup from the root dir of MMKV\n     */\n    public static backupOneToDirectory(mmapID: string, dstDir: string, rootPath?: string): boolean {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        return native.backupOneToDirectory(mmapID, dstDir, rootPath);\n    }\n\n    /**\n     * restore one MMKV instance from srcDir\n     * @param mmapID   the MMKV ID to restore\n     * @param srcDir   the restore source directory\n     * @param rootPath the customize root path of the MMKV, if null then restore to the root dir of MMKV\n     */\n    public static restoreOneFromDirectory(mmapID: string, srcDir: string, rootPath?: string): boolean {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        return native.restoreOneFromDirectory(mmapID, srcDir, rootPath);\n    }\n\n    /**\n     * backup all MMKV instance to dstDir\n     * @param dstDir the backup destination directory\n     * @return count of MMKV successfully backup\n     */\n    public static backupAllToDirectory(dstDir: string): bigint {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        return native.backupAllToDirectory(dstDir);\n    }\n\n    /**\n     * restore all MMKV instance from srcDir\n     * @param srcDir the restore source directory\n     * @return count of MMKV successfully restored\n     */\n    public static restoreAllFromDirectory(srcDir: string): bigint {\n        if (!MMKV.g_rootDir) {\n            throw new Error('You should Call MMKV.initialize() first.')\n        }\n        return native.restoreAllFromDirectory(srcDir);\n    }\n\n    public static readonly ExpireNever = 0;\n    public static readonly ExpireInMinute = 60;\n    public static readonly ExpireInHour = 60 * 60;\n    public static readonly ExpireInDay = 24 * 60 * 60;\n    public static readonly ExpireInMonth = 30 * 24 * 60 * 60;\n    public static readonly ExpireInYear = 365 * 30 * 24 * 60 * 60;\n\n    /**\n     * Enable auto key expiration. This is a upgrade operation, the file format will change.\n     * And the file won't be accessed correctly by older version (v1.2.16) of MMKV.\n     * NOTICE: enableCompareBeforeSet will be invalid when Expiration is on\n     * @param expireDurationInSecond the expire duration for all keys, {@link MMKV.ExpireNever} (0) means no default duration\n     * (aka each key will have it's own expire date)\n     */\n    public enableAutoKeyExpire(expireDurationInSecond: number): boolean {\n        return native.enableAutoKeyExpire(this.nativeHandle, expireDurationInSecond);\n    }\n\n    /**\n     * Disable auto key expiration. This is a downgrade operation.\n     */\n    public disableAutoKeyExpire(): boolean {\n        return native.disableAutoKeyExpire(this.nativeHandle);\n    }\n\n    /**\n     * Enable data compare before set, for better performance.\n     * If data for key seldom changes, use it.\n     * When encryption or expiration is on, compare-before-set will be invalid.\n     * For encryption, compare operation must decrypt data which is time consuming.\n     * For expiration, compare is useless because in most cases the expiration time changes every time.\n     */\n    public enableCompareBeforeSet(): void {\n        return native.enableCompareBeforeSet(this.nativeHandle);\n    }\n\n    /**\n     * Disable data compare before set\n     * disabled at default\n     */\n    public disableCompareBeforeSet(): void {\n        return native.disableCompareBeforeSet(this.nativeHandle);\n    }\n\n    /**\n     * Create an MMKV instance base on Anonymous Shared Memory, aka not synced to any disk files.\n     *\n     * @param context  The context of Android App, usually from Application.\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param size     The maximum size of the underlying Anonymous Shared Memory.\n     *                 Anonymous Shared Memory on Android can't grow dynamically, must set an appropriate size on creation.\n     * @param mode     The process mode of the MMKV instance, defaults to {@link #SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     */\n    public static mmkvWithAshmemID(mmapID: string, size: number, mode: number, cryptKey?: string): MMKV {\n        return MMKV.mmkvWithAshmemConfig(mmapID, {mode: mode, cryptKey: cryptKey, expectedCapacity: BigInt(size)});\n    }\n\n    /**\n     * Create an MMKV instance base on Anonymous Shared Memory, aka not synced to any disk files.\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param config The all-in-one configuration.\n     */\n    public static mmkvWithAshmemConfig(mmapID: string, config: MMKVConfig): MMKV {\n        config.mode = (config.mode ?? MMKV.SINGLE_PROCESS_MODE) | MMKV.ASHMEM_MODE;\n        return MMKV.mmkvWithConfig(mmapID, config);\n    }\n\n    /**\n     * Get an ashmem MMKV instance that has been initiated by another process.\n     * Normally you should just call {@link #mmkvWithAshmemID(String, int, int, String)} instead.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param fd       The file descriptor of the ashmem of the MMKV file, transferred from another process by binder.\n     * @param metaFD   The file descriptor of the ashmem of the MMKV crc file, transferred from another process by binder.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     */\n    public static mmkvWithAshmemFD(mmapID: string, fd: number, metaFD: number, cryptKey?: string): MMKV {\n        return MMKV.mmkvWithAshmemFDConfig(mmapID, fd, metaFD, {cryptKey: cryptKey});\n    }\n\n    /**\n     * Get an ashmem MMKV instance that has been initiated by another process.\n     * Normally you should just call {@link #mmkvWithAshmemID(String, MMKVConfig)} instead.\n     *\n     * @param mmapID   The unique ID of the MMKV instance.\n     * @param fd       The file descriptor of the ashmem of the MMKV file, transferred from another process by binder.\n     * @param metaFD   The file descriptor of the ashmem of the MMKV crc file, transferred from another process by binder.\n     * @param config    The all-in-one configuration.\n     */\n    public static mmkvWithAshmemFDConfig(mmapID: string, fd: number, metaFD: number, config: MMKVConfig): MMKV {\n        let enableKeyExpire = (config.enableKeyExpire == undefined) ? -1 : (config.enableKeyExpire == true ? 1 : 0);\n        let handle: bigint = native.mmkvWithAshmemFD(mmapID, fd, metaFD, config.cryptKey, config.aes256, enableKeyExpire,\n            config.expiredInSeconds, config.enableCompareBeforeSet, config.recover, config.itemSizeLimit);\n        if (!handle) {\n            throw new Error('Fail to create an MMKV instance [' + mmapID + '] in NAPI.');\n        }\n        return new MMKV(handle);\n    }\n\n    /**\n     * @return The file descriptor of the ashmem of the MMKV file.\n     */\n    public get ashmemFD(): number {\n        return native.ashmemFD(this.nativeHandle);\n    }\n\n    /**\n     * @return The file descriptor of the ashmem of the MMKV crc file.\n     */\n    public get ashmemMetaFD(): number {\n        return native.ashmemMetaFD(this.nativeHandle);\n    }\n\n    /**\n     * Create an native buffer, whose underlying memory can be directly transferred to another JNI method.\n     * Avoiding unnecessary JNI boxing and unboxing.\n     * An NativeBuffer must be manually {@link MMKV.destroyNativeBuffer()} to avoid memory leak.\n     *\n     * @param size The size of the underlying memory.\n     */\n    public static createNativeBuffer(size: number): NativeBuffer | null {\n        if (size <= 0) {\n            return null;\n        }\n        let pointer: bigint | undefined = native.createNativeBuffer(size);\n        if (!pointer || pointer <= BigInt(0)) {\n            return null;\n        }\n        return new NativeBuffer(pointer, size);\n    }\n\n    /**\n     * Destroy the native buffer. An NativeBuffer must be manually destroy to avoid memory leak.\n     */\n    public static destroyNativeBuffer(buffer: NativeBuffer): void {\n        native.destroyNativeBuffer(buffer.pointer, buffer.size);\n    }\n\n    /**\n     * Write the value of the key to the native buffer.\n     *\n     * @return The size written. Return -1 on any error.\n     */\n    public writeValueToNativeBuffer(key: string, buffer: NativeBuffer): number {\n        return native.writeValueToNativeBuffer(this.nativeHandle, key, buffer.pointer, buffer.size);\n    }\n\n    public isMultiProcess(): boolean {\n        return native.isMultiProcess(this.nativeHandle);\n    }\n\n    public isReadOnly(): boolean {\n        return native.isReadOnly(this.nativeHandle);\n    }\n\n    public checkContentChanged(): void {\n        native.checkContentChanged(this.nativeHandle);\n    }\n\n    public static checkExist(mmapID: string, rootPath?: string): boolean {\n        return native.checkExist(mmapID, rootPath);\n    }\n}\n\n/**\n * A wrapper for customize root path.\n */\n// Intentionally not exported to protect from being usd by constructor directly.\nclass NameSpace {\n    private _rootDir: string;\n\n    constructor(path: string) {\n        this._rootDir = path;\n    }\n\n    /**\n     * @returns The root folder of NameSpace.\n     */\n    public get rootDir(): string {\n        return this._rootDir;\n    }\n\n    /**\n     * Create an MMKV instance on customize folder.\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param mode The process mode of the MMKV instance, defaults to {@link MMKV.SINGLE_PROCESS_MODE}.\n     * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).\n     * @param expectedCapacity The file size you expected when opening or creating file.\n     * @returns The MMKV instance.\n     */\n    public mmkvWithID(mmapID: string, mode?: number, cryptKey?: string, expectedCapacity?: bigint, aes256?: boolean): MMKV {\n        return this.mmkvWithConfig(mmapID, {mode: mode, aes256: aes256, cryptKey: cryptKey,\n            expectedCapacity: expectedCapacity});\n    }\n\n    /**\n     * Creat an MMKV instance with all-in-one configuration\n     * @param mmapID The unique ID of the MMKV instance.\n     * @param config The all-in-one configuration.\n     * @returns The MMKV instance.\n     */\n    public mmkvWithConfig(mmapID: string, config: MMKVConfig): MMKV {\n        config.rootPath = this._rootDir;\n        let enableKeyExpire = (config.enableKeyExpire == undefined) ? -1 : (config.enableKeyExpire == true ? 1 : 0);\n        let mode = config.mode ?? MMKV.SINGLE_PROCESS_MODE;\n        let handle: bigint = native.mmkvWithID(mmapID, mode, config.cryptKey, config.rootPath, config.expectedCapacity,\n            config.aes256, enableKeyExpire, config.expiredInSeconds, config.enableCompareBeforeSet, config.recover,\n            config.itemSizeLimit);\n        if (!handle) {\n            throw new Error('Fail to create an MMKV instance [' + mmapID + '] in NAPI.');\n        }\n        return new MMKV(handle);\n    }\n\n    /**\n     * remove the storage of the MMKV, including the data file & meta file (.crc)\n     * Note: the existing instance (if any) will be closed & destroyed\n     */\n    public removeStorage(mmapID: string): boolean {\n        return native.removeStorage(mmapID, this._rootDir);\n    }\n\n    /**\n     * Check whether the MMKV file is valid or not on customize folder.\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public isFileValid(mmapID: string): boolean {\n        return native.isFileValid(mmapID, this._rootDir);\n    }\n\n    /**\n     * backup one MMKV instance the customize root path to dstDir\n     * @param mmapID   the MMKV ID to backup\n     * @param dstDir   the backup destination directory\n     */\n    public backupOneToDirectory(mmapID: string, dstDir: string): boolean {\n        return native.backupOneToDirectory(mmapID, dstDir, this._rootDir);\n    }\n\n    /**\n     * restore one MMKV instance from srcDir to the customize root path\n     * @param mmapID   the MMKV ID to restore\n     * @param srcDir   the restore source directory\n     */\n    public restoreOneFromDirectory(mmapID: string, srcDir: string): boolean {\n        return native.restoreOneFromDirectory(mmapID, srcDir, this._rootDir);\n    }\n\n    /**\n     * Check the existence of the MMKV file.\n     * @param mmapID   The unique ID of the MMKV instance.\n     */\n    public checkExist(mmapID: string): boolean {\n        return native.checkExist(mmapID, this._rootDir);\n    }\n}"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/MMKVConfig.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { MMKVRecoverStrategic } from \"./MMKVHandler\";\n\n/**\n * All-in-one configuration for creating a MMKV instance.\n */\nexport interface MMKVConfig {\n  mode?: number; // Defaults to SINGLE_PROCESS_MODE\n  aes256?: boolean; // using AES-256 key length\n  cryptKey?: string;\n  rootPath?: string;\n\n  expectedCapacity?: bigint; // the initial file size\n\n  enableKeyExpire?: boolean;\n  expiredInSeconds?: number; // ExpireNever = 0\n\n  enableCompareBeforeSet?: boolean;\n\n  recover?: MMKVRecoverStrategic; // if not set, use the old style callback\n\n  itemSizeLimit?: number; // the size limit of a key-value pair, reject insert if pass limit\n}"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/MMKVHandler.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { MMKVLogLevel } from './MMKVLogLevel';\n\n/**\n * The recover strategic of MMKV on errors. {@link MMKV#registerHandler}\n */\nexport enum MMKVRecoverStrategic {\n  /**\n   * The default strategic is to discard everything on errors.\n   */\n  OnErrorDiscard,\n\n  /**\n   * The recover strategic will try to recover as much data as possible.\n   */\n  OnErrorRecover,\n}\n\n/**\n * Callback handler for MMKV.\n * Callback is called on the operating thread of the MMKV instance.\n */\nexport abstract class MMKVHandler {\n  /**\n   * @return Return true if you want log redirecting.\n   */\n  public wantLogRedirect(): boolean {\n    return false;\n  }\n\n  /**\n   * Log Redirecting.\n   * @param level The level of this log.\n   * @param file The file name of this log.\n   * @param line The line of code of this log.\n   * @param function The function name of this log.\n   * @param message The content of this log.\n   */\n  public mmkvLog(level: MMKVLogLevel, file: string, line: number, func: string, message: string): void {\n    switch (level) {\n      case MMKVLogLevel.LevelDebug:\n        hilog.debug(0x0000, 'mmkv', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelInfo:\n        hilog.info(0x0000, 'mmkv', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelWarning:\n        hilog.warn(0x0000, 'mmkv', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelError:\n        hilog.error(0x0000, 'mmkv', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      default:\n        break;\n    }\n  }\n\n  /**\n   * By default MMKV will discard all data on crc32-check failure. {@link MMKVRecoverStrategic#OnErrorDiscard}\n   * @param mmapID The unique ID of the MMKV instance.\n   * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.\n   */\n  public onMMKVCRCCheckFail(mmapID: string): MMKVRecoverStrategic {\n    return MMKVRecoverStrategic.OnErrorDiscard;\n  }\n\n  /**\n   * By default MMKV will discard all data on file length mismatch. {@link MMKVRecoverStrategic#OnErrorDiscard}\n   * @param mmapID The unique ID of the MMKV instance.\n   * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.\n   */\n  public onMMKVFileLengthError(mmapID: string): MMKVRecoverStrategic {\n    return MMKVRecoverStrategic.OnErrorDiscard;\n  }\n\n  /**\n   * @return Return true to enable inter-process content change notification\n   */\n  public wantContentChangeNotification(): boolean {\n    return false;\n  }\n\n  /**\n   * Inter-process content change notification.\n   * Triggered by any method call, such as getXXX() or setXXX() or {@link MMKV#checkContentChangedByOuterProcess()}.\n   * @param mmapID The unique ID of the changed MMKV instance.\n   */\n  public onContentChangedByOuterProcess(mmapID: string): void {\n    return;\n  }\n\n  /**\n   * Called when an MMKV file is loaded successfully.\n   * @param mmapID The unique ID of the loaded MMKV instance.\n   */\n  public onMMKVContentLoadSuccessfully(mmapID: string): void {\n    return;\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/MMKVLogLevel.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @enum The levels of MMKV log.\n */\nexport enum MMKVLogLevel {\n  /**\n   * Debug level. Not available for release/production build.\n   */\n  LevelDebug,\n\n  /**\n   * Info level. The default level.\n   */\n  LevelInfo,\n\n  /**\n   * Warning level.\n   */\n  LevelWarning,\n\n  /**\n   * Error level.\n   */\n  LevelError,\n\n  /**\n   * Special level for disabling all logging.\n   * It's highly NOT suggested to turn off logging. Makes it hard to diagnose online/production bugs.\n   */\n  LevelNone\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/NativeBuffer.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A native memory wrapper, whose underlying memory can be passed to another NAPI method directly.\n * Avoiding unnecessary NAPI boxing and unboxing.\n * Must be destroy manually {@link MMKV.destroyNativeBuffer()}.\n */\nexport class NativeBuffer {\n  public pointer: bigint;\n  public size: number;\n\n  constructor(ptr: bigint, length: number) {\n    this.pointer = ptr;\n    this.size = length;\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/ets/utils/Util.ts",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport function getObjKeys(obj: Object): string[] {\n  let keys = Object.keys(obj);\n  return keys;\n}\n"
  },
  {
    "path": "OpenHarmony/MMKV/src/main/module.json5",
    "content": "{\n  \"module\": {\n    \"name\": \"MMKV\",\n    \"type\": \"har\",\n    \"description\": \"$string:shared_desc\",\n    \"deviceTypes\": [\n      \"phone\",\n      \"tablet\",\n      \"2in1\"\n    ],\n  }\n}"
  },
  {
    "path": "OpenHarmony/build-profile.json5",
    "content": "{\n  \"app\": {\n    \"signingConfigs\": [\n      {\n        \"name\": \"default\",\n        \"type\": \"HarmonyOS\",\n        \"material\": {\n          \"certpath\": \"/Users/lingol/.ohos/config/default_OpenHarmony_fx90P6mRx_D3JzUF_IGOq40WUJk3NNpLuuyDcrF9K6M=.cer\",\n          \"keyAlias\": \"debugKey\",\n          \"keyPassword\": \"0000001B54D50ACCB60BB70B8B13CAA92F7F6ECB681F2DD1A1EF23A1DC0393FD36914B4399A836C542E049\",\n          \"profile\": \"/Users/lingol/.ohos/config/default_OpenHarmony_fx90P6mRx_D3JzUF_IGOq40WUJk3NNpLuuyDcrF9K6M=.p7b\",\n          \"signAlg\": \"SHA256withECDSA\",\n          \"storeFile\": \"/Users/lingol/.ohos/config/default_OpenHarmony_fx90P6mRx_D3JzUF_IGOq40WUJk3NNpLuuyDcrF9K6M=.p12\",\n          \"storePassword\": \"0000001B0BA6A5ECCEBF7480ACAB289971CCA74088BBCEF063599D428D51ADF773AFBB8A663275B3779CBD\"\n        }\n      }\n    ],\n    \"products\": [\n      {\n        \"name\": \"default\",\n        \"signingConfig\": \"default\",\n        \"compatibleSdkVersion\": \"5.0.0(12)\",\n        \"runtimeOS\": \"HarmonyOS\",\n        \"targetSdkVersion\": \"6.0.0(20)\"\n      }\n    ],\n    \"buildModeSet\": [\n      {\n        \"name\": \"debug\",\n      },\n      {\n        \"name\": \"release\"\n      }\n    ]\n  },\n  \"modules\": [\n    {\n      \"name\": \"entry\",\n      \"srcPath\": \"./entry\",\n      \"targets\": [\n        {\n          \"name\": \"default\",\n          \"applyToProducts\": [\n            \"default\"\n          ]\n        }\n      ]\n    },\n    {\n      \"name\": \"MMKV\",\n      \"srcPath\": \"./MMKV\",\n      \"targets\": [\n        {\n          \"name\": \"default\",\n          \"applyToProducts\": [\n            \"default\"\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/.gitignore",
    "content": "/node_modules\n/oh_modules\n/.preview\n/build\n/.cxx\n/.test"
  },
  {
    "path": "OpenHarmony/entry/build-profile.json5",
    "content": "{\n  \"apiType\": \"stageMode\",\n  \"buildOption\": {\n    \"externalNativeOptions\": {\n      \"path\": \"./src/main/cpp/CMakeLists.txt\",\n      \"arguments\": \"\",\n      \"cppFlags\": \"\",\n    }\n  },\n  \"buildOptionSet\": [\n    {\n      \"name\": \"release\",\n      \"arkOptions\": {\n        \"obfuscation\": {\n          \"ruleOptions\": {\n            \"enable\": true,\n            \"files\": [\n              \"./obfuscation-rules.txt\"\n            ]\n          }\n        }\n      },\n      \"nativeLib\": {\n        \"debugSymbol\": {\n          \"strip\": true,\n          \"exclude\": []\n        }\n      }\n    },\n  ],\n  \"targets\": [\n    {\n      \"name\": \"default\"\n    },\n    {\n      \"name\": \"ohosTest\",\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/hvigorfile.ts",
    "content": "import { hapTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: hapTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}\n"
  },
  {
    "path": "OpenHarmony/entry/obfuscation-rules.txt",
    "content": "# Define project specific obfuscation rules here.\n# You can include the obfuscation configuration files in the current module's build-profile.json5.\n#\n# For more details, see\n#   https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md\n\n# Obfuscation options:\n# -disable-obfuscation: disable all obfuscations\n# -enable-property-obfuscation: obfuscate the property names\n# -enable-toplevel-obfuscation: obfuscate the names in the global scope\n# -compact: remove unnecessary blank spaces and all line feeds\n# -remove-log: remove all console.* statements\n# -print-namecache: print the name cache that contains the mapping from the old names to new names\n# -apply-namecache: reuse the given cache file\n\n# Keep options:\n# -keep-property-name: specifies property names that you want to keep\n# -keep-global-name: specifies names that you want to keep in the global scope\n-keep-global-name\nTestNativeMMKV\n\n-enable-property-obfuscation\n-enable-toplevel-obfuscation\n-enable-filename-obfuscation\n-enable-export-obfuscation"
  },
  {
    "path": "OpenHarmony/entry/oh-package.json5",
    "content": "{\n  \"name\": \"entry\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A demo for MMKV in OpenHarmony OS.\",\n  \"main\": \"\",\n  \"author\": \"guoling\",\n  \"license\": \"BSD 3-Clause\",\n  \"dependencies\": {\n    \"@tencent/mmkv\": \"file:../MMKV\",\n//  \"@tencent/mmkv\": \"~2.3.0\",\n    \"libentry.so\": \"file:./src/main/cpp/types/libentry\",\n  }\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/cpp/CMakeLists.txt",
    "content": "# the minimum version of CMake.\ncmake_minimum_required(VERSION 3.5.0)\nproject(OpenHarmony)\n\nset(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})\n\nset(MODULE_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../..)\n\n# mmkv/OpenHarmony/entry/oh_modules/@tencent/mmkv\nset(MMKV_MODULE_PATH ${MODULE_ROOT_PATH}/oh_modules/@tencent/mmkv)\n# mmkv/OpenHarmony/entry/oh_modules/@tencent/mmkv/include\nset(MMKV_INCLUDE_PATH ${MMKV_MODULE_PATH}/include)\n# mmkv/OpenHarmony/entry/oh_modules/@tencent/mmkv/libs/arm64-v8a\nset(MMKV_LIB_PATH ${MMKV_MODULE_PATH}/libs/${OHOS_ARCH})\n# mmkv/OpenHarmony/entry/oh_modules/@tencent/mmkv/build/default/intermediates/cmake/default/obj/arm64-v8a\nset(MMKV_EMBED_LIB_PATH ${MMKV_MODULE_PATH}/build/default/intermediates/cmake/default/obj/${OHOS_ARCH})\n\nif(DEFINED PACKAGE_FIND_FILE)\n    include(${PACKAGE_FIND_FILE})\nendif()\n\ninclude_directories(${NATIVERENDER_ROOT_PATH}\n                    ${NATIVERENDER_ROOT_PATH}/include)\n\nadd_library(entry SHARED napi_init.cpp)\ntarget_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so)\nset_target_properties(entry PROPERTIES CXX_STANDARD 17)\n\n# setup mmkv include path\ntarget_include_directories(entry PRIVATE ${MMKV_INCLUDE_PATH})\n# setup mmkv lib path\nadd_library(mmkv SHARED IMPORTED)\nif (EXISTS ${MMKV_LIB_PATH}/libmmkv.so)\n    set_target_properties(mmkv PROPERTIES IMPORTED_LOCATION ${MMKV_LIB_PATH}/libmmkv.so)\nelseif (EXISTS ${MMKV_EMBED_LIB_PATH}/libmmkv.so)\n    set_target_properties(mmkv PROPERTIES IMPORTED_LOCATION ${MMKV_EMBED_LIB_PATH}/libmmkv.so)\nelse()\n    message(AUTHOR_WARNING \"Can't find libmmkv.so in @tencent/mmkv module:${MMKV_MODULE_PATH}, check if \\\"ohpm install @tencent/mmkv\\\" fail!\")\n    set_target_properties(mmkv PROPERTIES IMPORTED_LOCATION ${MMKV_EMBED_LIB_PATH}/libmmkv.so)\nendif()\ntarget_link_libraries(entry PRIVATE mmkv)\n\n## Function to print all CMake variables\n#function(print_all_variables)\n#    message(STATUS \"Printing all CMake variables:\")\n#    get_cmake_property(_variableNames VARIABLES)\n#    foreach (_variableName ${_variableNames})\n#        message(STATUS \"${_variableName} = ${${_variableName}}\")\n#    endforeach()\n#endfunction()\n#\n## Call the function to print all variables\n#print_all_variables()\n#message(FATAL_ERROR \"Halt!\")\n\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/cpp/napi_init.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// demo of using MMKV C++ ABI directly\n\n#include \"napi/native_api.h\"\n#include <MMKV/MMKV.h>\n#include <hilog/log.h>\n#include <string>\n\nusing namespace std;\n\nbool IsNValueUndefined(napi_env env, napi_value value) {\n    napi_valuetype type;\n    if (napi_typeof(env, value, &type) == napi_ok && type == napi_undefined) {\n        return true;\n    }\n    return false;\n}\n\nstatic string NValueToString(napi_env env, napi_value value, bool maybeUndefined = false) {\n    if (maybeUndefined && IsNValueUndefined(env, value)) {\n        return \"\";\n    }\n\n    size_t size;\n    napi_get_value_string_utf8(env, value, nullptr, 0, &size);\n    string result(size, '\\0');\n    napi_get_value_string_utf8(env, value, (char *) result.data(), size + 1, nullptr);\n    return result;\n}\n\nstatic napi_value StringToNValue(napi_env env, const string &value) {\n    napi_value result;\n    napi_create_string_utf8(env, value.data(), value.size(), &result);\n    return result;\n}\n\nstatic napi_value BoolToNValue(napi_env env, bool value) {\n    napi_value result;\n    napi_value resultBool;\n    napi_create_double(env, value, &result);\n    napi_coerce_to_bool(env, result, &resultBool);\n    return resultBool;\n}\n\nstatic napi_value UInt64ToNValue(napi_env env, uint64_t value) {\n    napi_value result;\n    napi_create_bigint_uint64(env, value, &result);\n    return result;\n}\n\nvoid _MMKVLogWithLevel(const char *filename, const char *func, int line, const char *format, ...) {\n    string message;\n    char buffer[16];\n\n    va_list args;\n    va_start(args, format);\n    auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);\n    va_end(args);\n\n    if (length < 0) { // something wrong\n        message = {};\n    } else if (length < sizeof(buffer)) {\n        message = string(buffer, static_cast<unsigned long>(length));\n    } else {\n        message.resize(static_cast<unsigned long>(length), '\\0');\n        va_start(args, format);\n        std::vsnprintf(const_cast<char *>(message.data()), static_cast<size_t>(length) + 1, format, args);\n        va_end(args);\n    }\n\n    OH_LOG_Print(LOG_APP, LOG_INFO, 0, \"mmkvdemo\", \"<%{public}s:%{public}d::%{public}s> %{public}s\",\n        filename, line, func, message.c_str());\n}\n\n#define MMKVLog(format, ...) \\\n        _MMKVLogWithLevel(__FILE_NAME__, __func__, __LINE__, format, ##__VA_ARGS__)\n\nstring to_string(const std::string& str) {  return str; }\n\ntemplate <class T>\nstring to_string(const vector<T> &arr, const char* sp = \", \") {\n    string str;\n    for (const auto &element : arr) {\n        str += to_string(element);\n        str += sp;\n    }\n    if (!str.empty()) {\n        str.erase(str.length() - strlen(sp));\n    }\n    return str;\n}\n\nvoid functionalTest(MMKV *mmkv, bool decodeOnly) {\n    if (!decodeOnly) {\n        mmkv->set(true, \"bool\");\n    }\n    MMKVLog(\"bool = %d\\n\", mmkv->getBool(\"bool\"));\n\n    if (!decodeOnly) {\n        mmkv->set(1024, \"int32\");\n    }\n    MMKVLog(\"int32 = %d\\n\", mmkv->getInt32(\"int32\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint32_t>::max(), \"uint32\");\n    }\n    MMKVLog(\"uint32 = %u\\n\", mmkv->getUInt32(\"uint32\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<int64_t>::min(), \"int64\");\n    }\n    MMKVLog(\"int64 = %lld\\n\", mmkv->getInt64(\"int64\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint64_t>::max(), \"uint64\");\n    }\n    MMKVLog(\"uint64 = %llu\\n\", mmkv->getUInt64(\"uint64\"));\n\n    if (!decodeOnly) {\n        mmkv->set(3.14f, \"float\");\n    }\n    MMKVLog(\"float = %f\\n\", mmkv->getFloat(\"float\"));\n\n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<double>::max(), \"double\");\n    }\n    MMKVLog(\"double = %f\\n\", mmkv->getDouble(\"double\"));\n\n    if (!decodeOnly) {\n        mmkv->set(\"Hello, MMKV-示例 for POSIX\", \"raw_string\");\n        std::string str = \"Hello, MMKV-示例 for POSIX string\";\n        mmkv->set(str, \"string\");\n        mmkv->set(std::string_view(str).substr(7, 21), \"string_view\");\n    }\n    std::string result;\n    mmkv->getString(\"raw_string\", result);\n    MMKVLog(\"raw_string = %s\\n\", result.c_str());\n    mmkv->getString(\"string\", result);\n    MMKVLog(\"string = %s\\n\", result.c_str());\n    mmkv->getString(\"string_view\", result);\n    MMKVLog(\"string_view = %s\\n\", result.c_str());\n\n    // containerTest(mmkv, decodeOnly);\n\n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"count = %zu, totalSize = %zu\\n\", mmkv->count(), mmkv->totalSize());\n    MMKVLog(\"containsKey[string]: %d\\n\", mmkv->containsKey(\"string\"));\n\n    mmkv->removeValueForKey(\"bool\");\n    MMKVLog(\"bool: %d\\n\", mmkv->getBool(\"bool\"));\n    mmkv->removeValuesForKeys({\"int\", \"long\"});\n\n    mmkv->set(\"some string\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string before set null: %s\\n\", result.c_str());\n    mmkv->set((const char *) nullptr, \"null string\");\n    //mmkv->set(\"\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string after set null: %s, containsKey: %d\\n\", result.c_str(), mmkv->containsKey(\"null string\"));\n\n    //kv.sync();\n    //kv.async();\n    //kv.clearAll();\n    mmkv->clearMemoryCache();\n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"isFileValid[%s]: %d\\n\", mmkv->mmapID().c_str(), MMKV::isFileValid(mmkv->mmapID()));\n}\n\nstatic napi_value TestNativeMMKV(napi_env env, napi_callback_info info)\n{\n    size_t argc = 2;\n    napi_value args[2] = {nullptr};\n\n    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);\n\n    string root = NValueToString(env, args[0]);\n    string mmapID = NValueToString(env, args[1]);\n    auto ns = MMKV::nameSpace(root);\n    auto kv = ns.mmkvWithID(mmapID);\n    functionalTest(kv, false);\n\n    auto result = mmkv::DEFAULT_MMAP_SIZE;\n\n    return UInt64ToNValue(env, result);\n}\n\n\nEXTERN_C_START\nstatic napi_value Init(napi_env env, napi_value exports)\n{\n    napi_property_descriptor desc[] = {\n        { \"TestNativeMMKV\", nullptr, TestNativeMMKV, nullptr, nullptr, nullptr, napi_default, nullptr }\n    };\n    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);\n    return exports;\n}\nEXTERN_C_END\n\nstatic napi_module demoModule = {\n    .nm_version = 1,\n    .nm_flags = 0,\n    .nm_filename = nullptr,\n    .nm_register_func = Init,\n    .nm_modname = \"entry\",\n    .nm_priv = ((void*)0),\n    .reserved = { 0 },\n};\n\nextern \"C\" __attribute__((constructor)) void RegisterEntryModule(void)\n{\n    napi_module_register(&demoModule);\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/cpp/types/libentry/index.d.ts",
    "content": "export const TestNativeMMKV: (root: string, mmapID: string) => bigint;"
  },
  {
    "path": "OpenHarmony/entry/src/main/cpp/types/libentry/oh-package.json5",
    "content": "{\n  \"name\": \"libentry.so\",\n  \"types\": \"./index.d.ts\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\"\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/Util/Baseline.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MMKV } from '@tencent/mmkv';\nimport dataPreferences from '@ohos.data.preferences';\n\nexport class Baseline {\n  private static MMKV_ID = 'baseline';\n  private static CryptKey = undefined;\n  private m_arrStrings: string[] = [];\n  private m_arrKeys: string[] = [];\n  private m_arrIntKeys: string[] = [];\n  private m_loops = 1000;\n  private m_context: Context;\n  // private m_formatter: Intl.NumberFormat;\n\n  constructor(context: Context, loops: number) {\n    this.m_context = context;\n    this.m_loops = loops;\n\n    this.m_arrStrings = new Array(loops);\n    this.m_arrKeys = new Array(loops);\n    this.m_arrIntKeys = new Array(loops);\n    const filename = \"mmkv/Android/MMKV/mmkvdemo/src/main/java/com/tencent/mmkvdemo/Baseline.java_\";\n    for (let index = 0; index < loops; index++) {\n      this.m_arrStrings[index] = filename + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n      this.m_arrKeys[index] = \"str_\" + index;\n      this.m_arrIntKeys[index] = \"int_\" + index;\n    }\n    // this.m_formatter = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 });\n  }\n\n  private mmkvForTest() {\n    return MMKV.mmkvWithID(Baseline.MMKV_ID, MMKV.SINGLE_PROCESS_MODE, Baseline.CryptKey);\n    //return MMKV.mmkvWithAshmemID(m_context, MMKV_ID, 65536, MMKV.SINGLE_PROCESS_MODE, CryptKey);\n  }\n\n  private preferenceForTest() {\n    return dataPreferences.getPreferencesSync(this.m_context, { name: Baseline.MMKV_ID });\n  }\n\n  public mmkvBaselineTest() {\n    this.mmkvBatchWriteInt();\n    this.mmkvBatchReadInt();\n    this.mmkvBatchWriteString();\n    this.mmkvBatchReadString();\n\n    //this.mmkvBatchDeleteString();\n    // let mmkv = this.mmkvForTest();\n    //mmkv.trim();\n    // mmkv.clearMemoryCache();\n    // mmkv.totalSize();\n  }\n\n  private mmkvBatchWriteInt() {\n    let r = Math.random();\n    let startTime = Date.now();\n\n    let mmkv = this.mmkvForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      let tmp = Math.floor(r * (Number.MAX_SAFE_INTEGER + 1));\n      let key = this.m_arrIntKeys[index];\n      mmkv.encodeInt32(key, tmp);\n    }\n    let endTime = Date.now() - startTime;\n    console.log(\"MMKV write int: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private mmkvBatchReadInt() {\n    const startTime = Date.now();\n\n    const mmkv = this.mmkvForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      const key = this.m_arrIntKeys[index];\n      const tmp = mmkv.decodeInt32(key);\n    }\n    const endTime = Date.now() - startTime;\n    console.log(\"MMKV read int: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private mmkvBatchWriteString() {\n    const startTime = Date.now();\n\n    const mmkv = this.mmkvForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      const valueStr = this.m_arrStrings[index];\n      const strKey = this.m_arrKeys[index];\n      mmkv.encodeString(strKey, valueStr);\n    }\n    const endTime = Date.now() - startTime;\n    console.log(\"MMKV write String: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private mmkvBatchReadString() {\n    const startTime = Date.now();\n\n    const mmkv = this.mmkvForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      const strKey = this.m_arrKeys[index];\n      const tmpStr = mmkv.decodeString(strKey);\n    }\n    const endTime = Date.now() - startTime;\n    console.log(\"MMKV read String: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  public preferencesBaselineTest() {\n    this.preferencesBatchWriteInt();\n    this.preferencesBatchReadInt();\n    this.preferencesBatchWriteString();\n    this.preferencesBatchReadString();\n  }\n\n  private preferencesBatchWriteInt() {\n    let r = Math.random();\n    let startTime = Date.now();\n\n    let sp = this.preferenceForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      let tmp = Math.floor(r * (Number.MAX_SAFE_INTEGER + 1));\n      let key = this.m_arrIntKeys[index];\n      sp.putSync(key, tmp);\n    }\n    sp.flush();\n\n    let endTime = Date.now() - startTime;\n    console.log(\"Preferences write int: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private preferencesBatchReadInt() {\n    const startTime = Date.now();\n\n    const sp = this.preferenceForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      let key = this.m_arrIntKeys[index];\n      let tmp = sp.getSync(key, 0) as number;\n    }\n    sp.flush();\n\n    let endTime = Date.now() - startTime;\n    console.log(\"Preferences read int: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private preferencesBatchWriteString() {\n    const startTime = Date.now();\n\n    const sp = this.preferenceForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      const valueStr = this.m_arrStrings[index];\n      const strKey = this.m_arrKeys[index];\n      sp.putSync(strKey, valueStr);\n    }\n    sp.flush();\n\n    let endTime = Date.now() - startTime;\n    console.log(\"Preferences write String: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n\n  private preferencesBatchReadString() {\n    const startTime = Date.now();\n\n    const sp = this.preferenceForTest();\n    for (let index = 0; index < this.m_loops; index++) {\n      const strKey = this.m_arrKeys[index];\n      const tmpStr = sp.getSync(strKey, null) as string;\n    }\n    sp.flush();\n\n    let endTime = Date.now() - startTime;\n    console.log(\"Preferences read String: loop[\" + this.m_loops + \"]: \" + endTime.toFixed(2) + \" ms\");\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/Util/MyMMKVHandler.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MMKVLogLevel, MMKVHandler, MMKVRecoverStrategic } from '@tencent/mmkv';\nimport { hilog } from '@kit.PerformanceAnalysisKit';\n\nexport class MyMMKVHandler extends MMKVHandler {\n  /*private uniqueId: number;\n\n  constructor() {\n    super();\n    this.uniqueId = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);\n    hilog.info(0x0000, 'mmkvdemo', 'uniqueId: %{public}d', this.uniqueId);\n  }*/\n\n  public wantLogRedirect(): boolean {\n    let result = true;\n    // hilog.info(0x0000, 'mmkvdemo', 'uniqueId: %{public}d, wantLogRedirect: %{public}s', this.uniqueId, result);\n    return result;\n  }\n\n  public mmkvLog(level: MMKVLogLevel, file: string, line: number, func: string, message: string): void {\n    switch (level) {\n      case MMKVLogLevel.LevelDebug:\n        hilog.debug(0x0000, 'mmkv-redirect', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelInfo:\n        hilog.info(0x0000, 'mmkv-redirect', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelWarning:\n        hilog.warn(0x0000, 'mmkv-redirect', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      case MMKVLogLevel.LevelError:\n        hilog.error(0x0000, 'mmkv-redirect', '<%{public}s:%{public}d::%{public}s> %{public}s', file, line, func, message);\n        break;\n      default:\n        break;\n    }\n  }\n\n  public onMMKVCRCCheckFail(mmapID: string): MMKVRecoverStrategic {\n    hilog.warn(0x0000, 'mmkvdemo', 'onMMKVCRCCheckFail: %{public}s', mmapID);\n    return MMKVRecoverStrategic.OnErrorRecover;\n  }\n\n  public onMMKVFileLengthError(mmapID: string): MMKVRecoverStrategic {\n    hilog.warn(0x0000, 'mmkvdemo', 'onMMKVFileLengthError: %{public}s', mmapID);\n    return MMKVRecoverStrategic.OnErrorRecover;\n  }\n\n  public wantContentChangeNotification(): boolean {\n    // hilog.info(0x0000, 'mmkvdemo', 'wantContentChangeNotification uniqueId: %{public}d', this.uniqueId);\n    return true;\n  }\n\n  public onContentChangedByOuterProcess(mmapID: string): void {\n    hilog.info(0x0000, 'mmkvdemo', 'onContentChangedByOuterProcess: %{public}s', mmapID);\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/Util/Util.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { hilog } from \"@kit.PerformanceAnalysisKit\";\nimport { MMKV } from '@tencent/mmkv';\nimport { util } from \"@kit.ArkTS\";\n\nexport class GlobalContainer {\n  private static container = new Map<string, Object>();\n\n  static getObject(value: string) {\n    return GlobalContainer.container.get(value);\n  }\n\n  static setObject(key: string, objectClass: Object): void {\n    GlobalContainer.container.set(key, objectClass);\n  }\n}\n\nexport function testOneMMKV(kv: MMKV, decodeOnly: boolean) {\n  hilog.info(0, 'mmkvdemo', 'mmapID = %{public}s, cryptKey = %{public}s', kv.mmapID, kv.cryptKey);\n\n  if (!decodeOnly) {\n    kv.encodeBool('bool', true);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n\n  let pow31 = Math.pow(2, 31);\n  if (!decodeOnly) {\n    kv.encodeInt32('int32', pow31 - 1);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n\n  if (!decodeOnly) {\n    kv.encodeInt32('int32', -pow31);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n  let pow32 = Math.pow(2, 32);\n  if (!decodeOnly) {\n    kv.encodeUInt32('uint32', pow32 - 1);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n\n  if (!decodeOnly) {\n    kv.encodeUInt32('uint32', 0);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n  let pow63 = BigInt(2**63);\n  if (!decodeOnly) {\n    kv.encodeInt64('int64', pow63 - BigInt(1));\n  }\n  hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n\n  if (!decodeOnly) {\n    kv.encodeInt64('int64', -pow63);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n  let pow64 = BigInt(2**64);\n  if (!decodeOnly) {\n    kv.encodeUInt64('uint64', pow64 - BigInt(1));\n  }\n  hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n\n  if (!decodeOnly) {\n    kv.encodeUInt64('uint64', BigInt(0));\n  }\n  hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n  if (!decodeOnly) {\n    kv.encodeFloat('float', Number.MAX_VALUE);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode float max = %{public}d', kv.decodeFloat('float'));\n\n  if (!decodeOnly) {\n    kv.encodeFloat('float', Number.MIN_VALUE);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode float min = %{public}d', kv.decodeFloat('float'));\n\n  if (!decodeOnly) {\n    kv.encodeDouble('double', Number.MAX_VALUE);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n\n  if (!decodeOnly) {\n    kv.encodeDouble('double', Number.MIN_VALUE);\n  }\n  hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n  if (!decodeOnly) {\n    kv.encodeString('string', 'Hello world to OpenHarmony!');\n  }\n  hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n  hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n  hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n  if (!decodeOnly) {\n    let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n    kv.encodeBytes('bytes', arrayBuffer);\n  }\n  let bytes = kv.decodeBytes('bytes');\n  hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n  hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n    kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n  let sizeNeeded = kv.getValueSize('bytes', true);\n  let nativeBuffer = MMKV.createNativeBuffer(sizeNeeded);\n  if (nativeBuffer) {\n    let size = kv.writeValueToNativeBuffer('bytes', nativeBuffer);\n    hilog.info(0, 'mmkvdemo', 'NativeBuffer: size Needed = %{public}d,  written size = %{public}d', sizeNeeded, size);\n    MMKV.destroyNativeBuffer(nativeBuffer);\n  }\n\n  if (!decodeOnly) {\n    let strArr: string[] = ['abc', 'defg', 'hijk'];\n    kv.encodeStringSet('string-set', strArr);\n  }\n  let newStrArr = kv.decodeStringSet('string-set');\n  hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n  if (!decodeOnly) {\n    kv.encodeStringSet('empty-string-set', []);\n  }\n  let emptyStrArr = kv.decodeStringSet('empty-string-set');\n  hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n  let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n  hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n  hilog.info(0, 'mmkvdemo', 'contains bool = %{public}s, contains bool_not_exit = %{public}s',\n    kv.containsKey('bool'), kv.containsKey('bool_not_exit'));\n\n  kv.removeValueForKey('bool');\n  hilog.info(0, 'mmkvdemo', 'after remove, contains bool = %{public}s', kv.containsKey('bool'));\n\n  hilog.info(0, 'mmkvdemo', 'total count = %{public}i, total size = %{public}i, actual size = %{public}i',\n    kv.count(), kv.totalSize(), kv.actualSize());\n\n  // kv.lock();\n  // kv.trim();\n  // kv.sync(true);\n  // kv.unlock();\n  // hilog.info(0, 'mmkvdemo', 'tryLock = %{public}s', kv.tryLock());\n\n  kv.clearMemoryCache();\n  hilog.info(0, 'mmkvdemo', 'allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n  kv.removeValuesForKeys(['int32', 'uint32']);\n  hilog.info(0, 'mmkvdemo', 'remove \"int32\" & \"uint32\", allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n  // kv.clearAll();\n  // hilog.info(0, 'mmkvdemo', 'clearAll(), allKeys = %{public}s', StringArrayToString(kv.allKeys()));\n\n  // kv.close();\n}\n\nexport function StringToArrayBuffer(str: string | undefined): ArrayBuffer {\n  if (str == undefined) {\n    return new ArrayBuffer(0);\n  }\n  let enc = new util.TextEncoder(); // always utf-8\n  let i8Arr = enc.encodeInto(str);\n  return i8Arr.buffer;\n}\n\nexport function ArrayBufferToString(arr: ArrayBuffer | undefined): string {\n  if (arr == undefined) {\n    return 'undefined';\n  }\n  let out8Arr = new Uint8Array(arr);\n  let dnc = util.TextDecoder.create('utf-8');\n  return dnc.decodeWithStream(out8Arr);\n}\n\nexport interface Joinable {\n  join(separator?: string): string;\n}\n\nexport function ArrayToString<T extends Joinable>(arr: T | undefined): string {\n  if (arr == undefined) {\n    return 'undefined';\n  }\n  return '[' + arr.join(', ') + ']';\n}\n\nexport function ArraysEqual(arr1: Uint8Array, arr2: Uint8Array) {\n  // Check if the lengths are different\n  if (arr1.length !== arr2.length) {\n    return false;\n  }\n\n  // Compare each element\n  for (let i = 0; i < arr1.length; i++) {\n    if (arr1[i] !== arr2[i]) {\n      return false;\n    }\n  }\n\n  // If all elements are equal, return true\n  return true;\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/entryability/EntryAbility.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { window } from '@kit.ArkUI';\nimport { MMKV, MMKVLogLevel } from '@tencent/mmkv';\nimport { GlobalContainer, testOneMMKV } from '../Util/Util';\nimport childProcessManager from '@ohos.app.ability.childProcessManager';\nimport { DemoProcess } from '../process/DemoProcess';\nimport { MyMMKVHandler } from '../Util/MyMMKVHandler';\nimport { BusinessError } from '@ohos.base';\nimport native from 'libentry.so';\n\nexport default class EntryAbility extends UIAbility {\n  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onCreate');\n\n    // test NameSpace before MMKV.initialize()\n    this.testNameSpace();\n\n    let appCtx = this.context.getApplicationContext();\n    let result = MMKV.initialize(appCtx, MMKVLogLevel.LevelInfo, new MyMMKVHandler);\n    hilog.info(0x0000, 'mmkvdemo', 'rootDir: %{public}s', result);\n\n    // share between pages\n    GlobalContainer.setObject('context', appCtx);\n\n    // looks like there's noway to test multi-process for the time being\n    // this.startChildProcess();\n  }\n\n  onDestroy(): void {\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onDestroy');\n  }\n\n  onWindowStageCreate(windowStage: window.WindowStage): void {\n    // Main window is created, set main page for this ability\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onWindowStageCreate');\n\n    windowStage.loadContent('pages/Index', (err) => {\n      if (err.code) {\n        hilog.error(0x0000, 'mmkvdemo', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');\n        return;\n      }\n      hilog.info(0x0000, 'mmkvdemo', 'Succeeded in loading the content.');\n    });\n  }\n\n  onWindowStageDestroy(): void {\n    // Main window is destroyed, release UI related resources\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onWindowStageDestroy');\n  }\n\n  onForeground(): void {\n    // Ability has brought to foreground\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onForeground');\n  }\n\n  onBackground(): void {\n    // Ability has back to background\n    hilog.info(0x0000, 'mmkvdemo', '%{public}s', 'Ability onBackground');\n  }\n\n  startChildProcess(): void {\n    try {\n      // 这里要调用下DemoProcess类的任意方法，防止没有引用到而被构建工具优化掉\n      hilog.info(0x0000, 'mmkvdemo', 'startChildProcess %{public}s', DemoProcess.toString());\n      childProcessManager.startChildProcess(\"./ets/process/DemoProcess.ts\", childProcessManager.StartMode.APP_SPAWN_FORK, (err, data) => {\n        if (data) {\n          console.log(`startChildProcess success, pid: ${data}`);\n        } else {\n          console.error(`startChildProcess error, errorCode: ${err.code}`);\n        }\n      });\n    } catch (err) {\n      console.error(`startChildProcess error, errorCode: ${(err as BusinessError).code}`);\n    }\n  }\n\n  testNameSpace(): void {\n    let appCtx = this.context.getApplicationContext();\n    let rootDir = appCtx.filesDir + '/mmkv_namespace';\n    let ns = MMKV.nameSpace(rootDir);\n    let kv = ns.mmkvWithID('test_namespace');\n    testOneMMKV(kv, false);\n    let result = native.TestNativeMMKV(rootDir, 'test_namespace_native');\n    hilog.info(0x0000, 'mmkvdemo', 'mmkv native result:%{public}d', result);\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/pages/Index.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { MMKV, NativeBuffer } from '@tencent/mmkv';\nimport util from '@ohos.util';\nimport dataPreferences from '@ohos.data.preferences';\nimport { ArrayBufferToString,\n  ArraysEqual,\n  ArrayToString, GlobalContainer, StringToArrayBuffer, testOneMMKV } from '../Util/Util'\nimport { Baseline } from '../Util/Baseline';\n// import fs from '@ohos.file.fs';\n\n@Entry\n@Component\nstruct Index {\n  @State message: string = 'Hello, world!';\n\n  build() {\n    Row() {\n      Column({ space: 20 }) {\n        Button('Functionality Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.functionalTest('test', MMKV.MULTI_PROCESS_MODE, 'encrypt_key');\n          })\n        Button('Encryption Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testReKey();\n          })\n        Button('Import Preferences Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testImportPreferences();\n            this.testImport();\n          })\n        Button('Backup & Restore Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testBackup();\n            this.testRestore();\n          })\n        Button('Auto Expiration Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testAutoExpire();\n          })\n        Button('Compare-Before-Set Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testCompareBeforeSet()\n          })\n        Button('Ashmem & ReadOnly Test')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.testAshmem()\n            this.testReadOnly()\n          })\n        Button('Run Baseline')\n          .type(ButtonType.Normal)\n          .buttonStyle(ButtonStyleMode.TEXTUAL)\n          .fontSize(18)\n          .onClick(() => {\n            this.baseline();\n          })\n        Text('mmkv version: ' + MMKV.version)\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n        Text('pagesize: ' + MMKV.pageSize.toString())\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n        Text('rootDir: ' + MMKV.rootDir)\n          .fontSize(16)\n          .fontWeight(FontWeight.Medium)\n      }\n      .width('100%')\n    }\n    .height('100%')\n  }\n\n  functionalTest(mmapID: string, mode?: number, cryptKey?: string, rootPath?: string, expectedCapacity?: bigint) {\n    let kv = MMKV.mmkvWithID(mmapID, mode, cryptKey, rootPath, expectedCapacity);\n    hilog.info(0, 'mmkvdemo', 'mmapID = %{public}s, cryptKey = %{public}s', kv.mmapID, kv.cryptKey);\n\n    kv.encodeBool('bool', true);\n    hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist bool = %{public}s', kv.decodeBool('non-bool'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist bool = %{public}s', kv.decodeBool('non-bool', false));\n\n    let pow31 = Math.pow(2, 31);\n    kv.encodeInt32('int32', pow31 - 1);\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist int32 = %{public}s', kv.decodeInt32('non-int32'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist int32 = %{public}d', kv.decodeInt32('non-int32', 0));\n\n    kv.encodeInt32('int32', -pow31);\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n    let pow32 = Math.pow(2, 32);\n    kv.encodeUInt32('uint32', pow32 - 1);\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist uint32 = %{public}s', kv.decodeUInt32('non-uint32'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist uint32 = %{public}d', kv.decodeUInt32('non-uint32', 0));\n\n    kv.encodeUInt32('uint32', 0);\n    hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n    let pow63 = BigInt(2**63);\n    kv.encodeInt64('int64', pow63 - BigInt(1));\n    hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist int64 = %{public}s', kv.decodeInt64('non-int64'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist int64 = %{public}d', kv.decodeInt64('non-int64', BigInt(0)));\n\n    kv.encodeInt64('int64', -pow63);\n    hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n    let pow64 = BigInt(2**64);\n    kv.encodeUInt64('uint64', pow64 - BigInt(1));\n    hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist uint64 = %{public}s', kv.decodeUInt64('non-uint64'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist uint64 = %{public}i', kv.decodeUInt64('non-uint64', BigInt(0)));\n\n    kv.encodeUInt64('uint64', BigInt(0));\n    hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n    kv.encodeFloat('float', Number.MAX_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode float max = %{public}d', kv.decodeFloat('float'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist float = %{public}s', kv.decodeFloat('non-float'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist float = %{public}d', kv.decodeFloat('non-float', 0));\n\n    kv.encodeFloat('float', Number.MIN_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode float min = %{public}d', kv.decodeFloat('float'));\n\n    kv.encodeDouble('double', Number.MAX_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist double = %{public}s', kv.decodeDouble('non-double'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist double = %{public}d', kv.decodeDouble('non-double', 0));\n\n    kv.encodeDouble('double', Number.MIN_VALUE);\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n    kv.encodeString('string', 'Hello world to OpenHarmony!');\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n    let json: string = '{\"courseId\":39575,\"name\":\"通识课测试2\",\"type\":4,\"status\":1,\"limit\":1819956082000,\"needapprove\":\"0\",\"lastModifyDate\":1712821563000,\"openCourse\":1,\"md5\":\"967cdcbd284197d68efb19ca9b27f06b\"}';\n    kv.encodeString('json', json);\n    hilog.info(0, 'mmkvdemo', 'decode json = %{public}s', kv.decodeString('json'));\n\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n    let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n    kv.encodeBytes('bytes', arrayBuffer);\n    let bytes = kv.decodeBytes('bytes');\n    hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n    hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n      kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n    let typedArr = new Uint8Array(arrayBuffer, 5, 11);\n    kv.encodeTypedArray('bytes', typedArr);\n    let typedArrResult = kv.decodeUint8Array('bytes')!;\n    if (!ArraysEqual(typedArr, typedArrResult)) {\n      hilog.error(0, 'mmkvdemo', 'typedArr = %{public}s, typeArrResult = %{public}s',\n        ArrayToString(typedArr), ArrayToString(typedArrResult));\n    }\n\n    this.testContainer(kv, false);\n\n    hilog.info(0, 'mmkvdemo', 'contains bytes = %{public}s, contains bytes_not_exit = %{public}s',\n      kv.containsKey('bytes'), kv.containsKey('bytes_not_exit'));\n\n    kv.removeValueForKey('bytes');\n    hilog.info(0, 'mmkvdemo', 'after remove, contains bytes = %{public}s', kv.containsKey('bytes'));\n\n    hilog.info(0, 'mmkvdemo', 'total count = %{public}i, total size = %{public}i, actual size = %{public}i',\n      kv.count(), kv.totalSize(), kv.actualSize());\n\n    kv.lock();\n    kv.trim();\n    kv.sync(true);\n    kv.unlock();\n    hilog.info(0, 'mmkvdemo', 'tryLock = %{public}s', kv.tryLock());\n\n    kv.clearMemoryCache();\n    hilog.info(0, 'mmkvdemo', 'allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.removeValuesForKeys(['bool', 'int32']);\n    hilog.info(0, 'mmkvdemo', 'remove \"bool\" & \"int32\", allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.checkContentChanged();\n    kv.clearAll();\n    hilog.info(0, 'mmkvdemo', 'clearAll(), allKeys = %{public}s', ArrayToString(kv.allKeys()));\n\n    kv.close();\n    hilog.info(0, 'mmkvdemo', 'isFileValue %{public}s', MMKV.isFileValid(mmapID, rootPath));\n    hilog.info(0, 'mmkvdemo', 'checkExist %{public}s', MMKV.checkExist(mmapID, rootPath));\n    hilog.info(0, 'mmkvdemo', 'remove storage %{public}s', MMKV.removeStorage(mmapID, rootPath));\n    hilog.info(0, 'mmkvdemo', 'checkExist %{public}s', MMKV.checkExist(mmapID, rootPath));\n  }\n\n  testContainer(kv: MMKV, decodeOnly: boolean) {\n    if (!decodeOnly) {\n      let strArr: string[] = ['abc', 'defg', 'hijk'];\n      kv.encodeStringSet('string-set', strArr);\n    }\n    let newStrArr = kv.decodeStringSet('string-set');\n    hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n    if (!decodeOnly) {\n      kv.encodeStringSet('empty-string-set', []);\n    }\n    let emptyStrArr = kv.decodeStringSet('empty-string-set');\n    hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n    let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n    hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n    if (!decodeOnly) {\n      let arr = new Int8Array([-128, 0, 127, 1]);\n      kv.encodeTypedArray('int8-array', arr);\n    }\n    let newI8Arr = kv.decodeInt8Array('int8-array');\n    hilog.info(0, 'mmkvdemo', 'int8-array = %{public}s', ArrayToString(newI8Arr));\n\n    if (!decodeOnly) {\n      let arr = new Uint8Array([0, 255, 1, 255]);\n      kv.encodeTypedArray('uint8-array', arr);\n    }\n    let newUI8Arr = kv.decodeUint8Array('uint8-array');\n    hilog.info(0, 'mmkvdemo', 'uint8-array = %{public}s', ArrayToString(newUI8Arr));\n\n    if (!decodeOnly) {\n      let arr = new Uint8ClampedArray([0, 255, 256, -1]);\n      kv.encodeTypedArray('uint8Clamped-array', arr);\n    }\n    let newUint8ClampedArray = kv.decodeUint8ClampedArray('uint8Clamped-array');\n    hilog.info(0, 'mmkvdemo', 'uint8Clamped-array = %{public}s', ArrayToString(newUint8ClampedArray));\n\n    if (!decodeOnly) {\n      let pow15 = Math.pow(2, 15);\n      let arr = new Int16Array([1024, 0, pow15 - 1, -pow15]);\n      kv.encodeTypedArray('int16-array', arr);\n      let arrPart = new Int16Array(arr.buffer, 2, arr.length - 2);\n      kv.encodeTypedArray('int16-array-part', arrPart);\n    }\n    let newI16Arr = kv.decodeInt16Array('int16-array');\n    let newI16ArrPart = kv.decodeInt16Array('int16-array-part');\n    hilog.info(0, 'mmkvdemo', 'int16-array = %{public}s, int16-array-part = %{public}s',\n      ArrayToString(newI16Arr), ArrayToString(newI16ArrPart));\n\n    if (!decodeOnly) {\n      let pow16 = Math.pow(2, 16);\n      let ui32Arr = new Uint16Array([2048, 0, pow16 - 1]);\n      kv.encodeTypedArray('uint16-array', ui32Arr);\n    }\n    let newUI16Arr = kv.decodeUint16Array('uint16-array');\n    hilog.info(0, 'mmkvdemo', 'uint16-array = %{public}s', ArrayToString(newUI16Arr));\n\n    if (!decodeOnly) {\n      let pow31 = Math.pow(2, 31);\n      let i32Arr = new Int32Array([1024, 0, pow31 - 1, -pow31]);\n      kv.encodeTypedArray('int32-array', i32Arr);\n    }\n    let newI32Arr = kv.decodeInt32Array('int32-array');\n    hilog.info(0, 'mmkvdemo', 'int32-array = %{public}s', ArrayToString(newI32Arr));\n\n    if (!decodeOnly) {\n      let pow32 = Math.pow(2, 32);\n      let ui32Arr = new Uint32Array([2048, 0, pow32 - 1]);\n      kv.encodeTypedArray('uint32-array', ui32Arr);\n    }\n    let newUI32Arr = kv.decodeUint32Array('uint32-array');\n    hilog.info(0, 'mmkvdemo', 'uint32-array = %{public}s', ArrayToString(newUI32Arr));\n\n    if (!decodeOnly) {\n      let pow63 = BigInt(2**63);\n      let arr = new BigInt64Array([BigInt(4096), BigInt(0), pow63 - BigInt(1), -pow63]);\n      kv.encodeTypedArray('int64-array', arr);\n    }\n    let newI64Arr = kv.decodeInt64Array('int64-array');\n    hilog.info(0, 'mmkvdemo', 'int64-array = %{public}s', ArrayToString(newI64Arr));\n\n    if (!decodeOnly) {\n      let pow64 = BigInt(2**64);\n      let arr = new BigUint64Array([BigInt(8192), BigInt(0), pow64 - BigInt(1)]);\n      kv.encodeTypedArray('uint64-array', arr);\n    }\n    let newUI64Arr = kv.decodeUint64Array('uint64-array');\n    hilog.info(0, 'mmkvdemo', 'uint64-array = %{public}s', ArrayToString(newUI64Arr));\n\n    if (!decodeOnly) {\n      let arr = new Float32Array([0.0, -3.1416, Number.MIN_VALUE, Number.MAX_VALUE]);\n      kv.encodeTypedArray('float32-array', arr);\n    }\n    let newFloat32Array = kv.decodeFloat32Array('float32-array');\n    hilog.info(0, 'mmkvdemo', 'float32-array = %{public}s', ArrayToString(newFloat32Array));\n\n    if (!decodeOnly) {\n      let arr = new Float64Array([0.0, -3.1416, Number.MIN_VALUE, Number.MAX_VALUE]);\n      kv.encodeTypedArray('float64-array', arr);\n    }\n    let newFloat64Array = kv.decodeFloat64Array('float64-array');\n    hilog.info(0, 'mmkvdemo', 'float64-array = %{public}s', ArrayToString(newFloat64Array));\n  }\n\n  testMMKV(mmapID: string, decodeOnly: boolean, cryptKey?: string, rootPath?: string, aes256?: boolean): MMKV {\n    let kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, rootPath, undefined, aes256);\n\n    testOneMMKV(kv, decodeOnly);\n    hilog.info(0, 'mmkvdemo', 'isFileValue %{public}s', MMKV.isFileValid(kv.mmapID, rootPath));\n\n    return kv;\n  }\n\n  testReKey() {\n    let mmapID: string = 'test/AES_reKey1';\n    let kv = this.testMMKV(mmapID, false);\n\n    kv.reKey('Key_seq_1');\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true, 'Key_seq_1');\n\n    kv.reKey('Key_Seq_Very_Looooooooong', true);\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true, 'Key_Seq_Very_Looooooooong', undefined, true);\n\n    kv.reKey();\n    kv.clearMemoryCache();\n    this.testMMKV(mmapID, true);\n  }\n\n  testImportPreferences() {\n    let context = GlobalContainer.getObject('context') as Context\n    let preferences = dataPreferences.getPreferencesSync(context, { name: 'default' });\n    preferences.putSync('bool', true);\n    let pow31 = Math.pow(2, 31);\n    preferences.putSync('int32_max', pow31 - 1);\n    preferences.putSync('int32_min', -pow31);\n    let pow32 = Math.pow(2, 32);\n    preferences.putSync('uint32', pow32 - 1);\n    preferences.putSync('double_max', Number.MAX_VALUE);\n    preferences.putSync('double_min', Number.MIN_VALUE);\n    preferences.putSync('string', 'hello, world preferenes');\n    preferences.putSync('string_array', ['hello', 'world', 'preferenes']);\n    preferences.putSync('empty_string_array', []);\n    preferences.putSync('number_array', [pow31 - 1, -pow31, pow32, Number.MAX_VALUE, Number.MIN_VALUE]);\n    preferences.putSync('bool_array', [true, false, true, false]);\n    let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n    preferences.putSync('uint8_array', new Uint8Array(arrayBuffer));\n    preferences.flush();\n\n    let kv = MMKV.mmkvWithID('imported');\n    let count = kv.importFromPreferences(preferences);\n    hilog.info(0, 'mmkvdemo', 'importFromPreferences count %{public}i', count);\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeDouble('int32_max'));\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeDouble('int32_min'));\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeDouble('uint32'));\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double_max'));\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double_min'));\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n    hilog.info(0, 'mmkvdemo', 'decode string array = %{public}s', ArrayToString(kv.decodeStringSet('string_array')));\n    hilog.info(0, 'mmkvdemo', 'decode empty string array = %{public}s', ArrayToString(kv.decodeStringSet('empty_string_array')));\n    hilog.info(0, 'mmkvdemo', 'decode number array = %{public}s', ArrayToString(kv.decodeNumberSet('number_array')));\n    hilog.info(0, 'mmkvdemo', 'decode bool array = %{public}s', ArrayToString(kv.decodeBoolSet('bool_array')));\n    hilog.info(0, 'mmkvdemo', 'decode uint8 array = %{public}s', ArrayBufferToString(kv.decodeBytes('uint8_array')));\n  }\n\n  testBackup() {\n    let context = GlobalContainer.getObject('context') as Context;\n    let backupRootDir = context.filesDir + '/mmkv_backup_3';\n    let otherDir = context.filesDir + '/mmkv_3';\n    let mmapID = 'test/AES';\n    let cryptKey = 'Tencent MMKV';\n\n    {\n      let kv = this.testMMKV(mmapID, false, cryptKey, otherDir);\n      kv.removeValueForKey('test_restore');\n      kv.close();\n    }\n\n    let ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, otherDir);\n    hilog.info(0, 'mmkvdemo', 'backup one [%{public}s] ret = %{public}s', mmapID, ret);\n    if (ret) {\n      let mmkv = MMKV.backedUpMMKVWithID(mmapID, backupRootDir, MMKV.SINGLE_PROCESS_MODE, cryptKey);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, otherDir);\n      hilog.info(0, 'mmkvdemo', 'check on origin file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n\n    /*{\n      MMKV mmkv = MMKV.mmkvWithID('imported');\n      mmkv.close();\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      mmkv.close();\n    }*/\n    backupRootDir = context.filesDir + '/mmkv_backup';\n    let count = MMKV.backupAllToDirectory(backupRootDir);\n    hilog.info(0, 'mmkvdemo', 'backup all count %{public}d', count);\n    if (count > 0) {\n      let mmkv = MMKV.backedUpMMKVWithID('imported', backupRootDir);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.backedUpMMKVWithID('test/AES_reKey1', backupRootDir);\n      hilog.info(0, 'mmkvdemo', 'check on backup file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n  }\n\n  testRestore() {\n    hilog.info(0, 'mmkvdemo', 'test restore begin');\n\n    let context = GlobalContainer.getObject('context') as Context;\n    let backupRootDir = context.filesDir + '/mmkv_backup_3';\n    let otherDir = context.filesDir + '/mmkv_3';\n    let mmapID = 'test/AES';\n    let cryptKey = 'Tencent MMKV';\n\n    let mmkv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey, otherDir);\n    mmkv.encodeBool('test_restore', true);\n    hilog.info(0, 'mmkvdemo', 'before restore [%{public}s] allKeys: %{public}s',\n      mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    let ret = MMKV.restoreOneFromDirectory(mmapID, backupRootDir, otherDir);\n    hilog.info(0, 'mmkvdemo', 'restore one [%{public}s] ret = %{public}s', mmapID, ret);\n    if (ret) {\n      hilog.info(0, 'mmkvdemo', 'after restore [%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n\n    /*{\n      mmkv = MMKV.mmkvWithID('imported');\n      mmkv.close();\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      mmkv.close();\n    }*/\n    backupRootDir = context.filesDir + '/mmkv_backup';\n    let count = MMKV.restoreAllFromDirectory(backupRootDir);\n    hilog.info(0, 'mmkvdemo', 'restore all count %{public}d', count);\n    if (count > 0) {\n      mmkv = MMKV.mmkvWithID('imported');\n      hilog.info(0, 'mmkvdemo', 'check on restore file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n\n      mmkv = MMKV.mmkvWithID('test/AES_reKey1');\n      hilog.info(0, 'mmkvdemo', 'check on restore file[%{public}s] allKeys: %{public}s',\n        mmkv.mmapID, ArrayToString(mmkv.allKeys()));\n    }\n  }\n\n  private testAutoExpireOne(kv: MMKV, decodeOnly: boolean, expiration: number): void {\n    if (!decodeOnly) {\n      kv.encodeBool('bool', true, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode bool = %{public}s', kv.decodeBool('bool'));\n\n    let pow31 = Math.pow(2, 31);\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', pow31 - 1, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 max = %{public}d', kv.decodeInt32('int32'));\n\n    if (!decodeOnly) {\n      kv.encodeInt32('int32', -pow31, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int32 min = %{public}d', kv.decodeInt32('int32'));\n\n    let pow32 = Math.pow(2, 32);\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', pow32 - 1, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 max = %{public}d', kv.decodeUInt32('uint32'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt32('uint32', 0, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint32 min = %{public}d', kv.decodeUInt32('uint32'));\n\n    let pow63 = BigInt(2**63);\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', pow63 - BigInt(1), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 max = %{public}i', kv.decodeInt64('int64'));\n\n    if (!decodeOnly) {\n      kv.encodeInt64('int64', -pow63, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode int64 min = %{public}i', kv.decodeInt64('int64'));\n\n    let pow64 = BigInt(2**64);\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', pow64 - BigInt(1), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 max = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeUInt64('uint64', BigInt(0), expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode uint64 min = %{public}i', kv.decodeUInt64('uint64'));\n\n    if (!decodeOnly) {\n      kv.encodeFloat('float', Number.MAX_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode float max = %{public}d', kv.decodeFloat('float'));\n\n    if (!decodeOnly) {\n      kv.encodeFloat('float', Number.MIN_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode float min = %{public}d', kv.decodeFloat('float'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MAX_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double max = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeDouble('double', Number.MIN_VALUE, expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode double min = %{public}d', kv.decodeDouble('double'));\n\n    if (!decodeOnly) {\n      kv.encodeString('string', 'Hello world to OpenHarmony!', expiration);\n    }\n    hilog.info(0, 'mmkvdemo', 'decode string = %{public}s', kv.decodeString('string'));\n\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist'));\n    hilog.info(0, 'mmkvdemo', 'decode non-exist string = %{public}s', kv.decodeString('non-exist', 'default_value'));\n\n    if (!decodeOnly) {\n      let arrayBuffer = StringToArrayBuffer('This is a string 转换成 a Uint8Array 💯');\n      kv.encodeBytes('bytes', arrayBuffer, expiration);\n    }\n    let bytes = kv.decodeBytes('bytes');\n    hilog.info(0, 'mmkvdemo', 'decode bytes = %{public}s', ArrayBufferToString(bytes));\n\n    hilog.info(0, 'mmkvdemo', 'bytes value size = %{public}d, actual value size = %{public}d',\n      kv.getValueSize('bytes'), kv.getValueSize('bytes', true));\n\n    if (!decodeOnly) {\n      let strArr: string[] = ['abc', 'defg', 'hijk'];\n      kv.encodeStringSet('string-set', strArr, expiration);\n    }\n    let newStrArr = kv.decodeStringSet('string-set');\n    hilog.info(0, 'mmkvdemo', 'string-set = %{public}s', ArrayToString(newStrArr));\n\n    if (!decodeOnly) {\n      kv.encodeStringSet('empty-string-set', [], expiration);\n    }\n    let emptyStrArr = kv.decodeStringSet('empty-string-set');\n    hilog.info(0, 'mmkvdemo', 'empty-string-set = %{public}s', ArrayToString(emptyStrArr));\n    let nonStrArr = kv.decodeStringSet('non-exist-string-set');\n    hilog.info(0, 'mmkvdemo', 'non-exist-string-set = %{public}s', ArrayToString(nonStrArr));\n\n    hilog.info(0, 'mmkvdemo', 'contains bool = %{public}s, contains bool_not_exit = %{public}s',\n      kv.containsKey('bool'), kv.containsKey('bool_not_exit'));\n\n    kv.removeValueForKey('bool');\n    hilog.info(0, 'mmkvdemo', 'after remove, contains bool = %{public}s', kv.containsKey('bool'));\n\n    hilog.info(0, 'mmkvdemo', 'total NonExpire count = %{public}i, total size = %{public}i, actual size = %{public}i',\n      kv.count(true), kv.totalSize(), kv.actualSize());\n\n    kv.clearMemoryCache();\n    hilog.info(0, 'mmkvdemo', 'all NonExpire Keys = %{public}s', ArrayToString(kv.allKeys(true)));\n\n    kv.removeValuesForKeys(['int32', 'uint32']);\n    hilog.info(0, 'mmkvdemo', 'remove \"int32\" & \"uint32\", allKeys = %{public}s', ArrayToString(kv.allKeys(true)));\n  }\n\n  private async testAutoExpire(): Promise<void> {\n    // test disable expire by config\n    let mmkv = MMKV.mmkvWithConfig('test_auto_expire', {enableKeyExpire: false, itemSizeLimit: 1});\n    // mmkv.encodeBool('never_expire_key_1', true, MMKV.ExpireNever);\n    mmkv.clearAll();\n    // mmkv.disableAutoKeyExpire();\n\n    // test enable expire by config\n    mmkv.close();\n    mmkv = MMKV.mmkvWithConfig('test_auto_expire', {enableKeyExpire: true, expiredInSeconds: 1});\n    // mmkv.enableAutoKeyExpire(1);\n    mmkv.encodeBool('auto_expire_key_1', true);\n    mmkv.encodeBool('never_expire_key_1', true, MMKV.ExpireNever);\n\n    this.testAutoExpireOne(mmkv, false, 1);\n    await new Promise<void>(resolve => setTimeout(resolve, 1000 * 2));\n    this.testAutoExpireOne(mmkv, true, 1);\n\n    if (mmkv.containsKey('auto_expire_key_1')) {\n      hilog.error(0, 'mmkvdemo', 'auto key expiration auto_expire_key_1');\n    } else {\n      hilog.info(0, 'mmkvdemo', 'auto key expiration auto_expire_key_1');\n    }\n    if (mmkv.containsKey('never_expire_key_1')) {\n      hilog.info(0, 'mmkvdemo', 'auto key expiration never_expire_key_1');\n    } else {\n      hilog.error(0, 'mmkvdemo', 'auto key expiration never_expire_key_1');\n    }\n  }\n\n  private testCompareBeforeSet(): void {\n    let mmkv = MMKV.mmkvWithID('testCompareBeforeSet');\n    mmkv.enableCompareBeforeSet();\n\n    mmkv.encodeString('key', 'extra');\n\n    {\n      let key = 'int';\n      let v = 12345;\n      mmkv.encodeInt32(key, v);\n      let actualSize = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}d', mmkv.decodeInt32(key, -1));\n      mmkv.encodeInt32(key, v);\n      let actualSize2 = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize2);\n      if (actualSize2 != actualSize) {\n        hilog.error(0, 'mmkvdemo', 'testCompareBeforeSet fail');\n      }\n\n      mmkv.encodeInt32(key, v * 23);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', mmkv.actualSize());\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}d', mmkv.decodeInt32(key, -1));\n    }\n\n    {\n      let key = 'string';\n      let v = 'w012A🏊🏻good';\n      mmkv.encodeString(key, v);\n      let actualSize = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize);\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}s', mmkv.decodeString(key, ''));\n      mmkv.encodeString(key, v);\n      let actualSize2 = mmkv.actualSize();\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', actualSize2);\n      if (actualSize2 != actualSize) {\n        hilog.error(0, 'mmkvdemo', 'testCompareBeforeSet fail');\n      }\n\n      mmkv.encodeString(key, 'temp data 👩🏻‍🏫');\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet actualSize = %{public}i', mmkv.actualSize());\n      hilog.info(0, 'mmkvdemo', 'testCompareBeforeSet v = %{public}s', mmkv.decodeString(key, ''));\n    }\n  }\n\n  private testAshmem() {\n    let mmapID = 'testAshmem';\n    let cryptKey = 'Tencent MMKV';\n\n    let kv = MMKV.mmkvWithAshmemID(mmapID, MMKV.pageSize, MMKV.SINGLE_PROCESS_MODE, cryptKey);\n    testOneMMKV(kv, false);\n\n    let newKV = MMKV.mmkvWithAshmemFD(mmapID, kv.ashmemFD, kv.ashmemMetaFD, cryptKey);\n    testOneMMKV(newKV, true);\n  }\n\n  private testReadOnly() {\n    let mmapID = 'testReadOnly';\n    let cryptKey = 'ReadOnly+Key';\n\n    {\n      let kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey);\n      testOneMMKV(kv, false);\n      kv.close();\n    }\n\n    // HW no longer provide chmod() for nonsense, sigh..\n    /*\n    let path = MMKV.rootDir + '/' + mmapID;\n    fs.chmod(path, 0o444);\n    let crcPath = path + '.crc';\n    fs.chmod(crcPath, 0o444);\n    */\n    {\n      let kv = MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE | MMKV.READ_ONLY_MODE, cryptKey);\n      testOneMMKV(kv, true);\n\n      // also check if it tolerate update operations without crash\n      testOneMMKV(kv, false);\n    }\n    /*\n    fs.chmod(path, 0o666);\n    fs.chmod(crcPath, 0o666);\n    */\n  }\n\n  private baseline() {\n    let baseline = GlobalContainer.getObject('baseline') as Baseline;\n    if (!baseline) {\n      let context = GlobalContainer.getObject('context') as Context;\n      baseline = new Baseline(context, 1000);\n      GlobalContainer.setObject('baseline', baseline);\n    }\n    baseline.mmkvBaselineTest();\n    baseline.preferencesBaselineTest();\n  }\n\n  private async testImport(): Promise<void> {\n    const mmapID: string = \"testImportSrc\";\n    const src: MMKV = MMKV.mmkvWithID(mmapID);\n    src.encodeBool(\"bool\", true);\n    src.encodeInt32(\"int\", -2147483648); // Integer.MIN_VALUE\n    src.encodeUInt64(\"long\", 9223372036854775807n); // Long.MAX_VALUE as bigint\n    src.encodeString(\"string\", \"test import\");\n\n    const dst: MMKV = MMKV.mmkvWithID(\"testImportDst\");\n    dst.clearAll();\n    dst.enableAutoKeyExpire(1);\n    dst.encodeBool(\"bool\", false);\n    dst.encodeInt32(\"int\", -1);\n    dst.encodeUInt64(\"long\", 0n);\n    dst.encodeString(\"string\", mmapID);\n\n    const count: bigint = dst.importFrom(src);\n    if (count !== BigInt(4) || dst.count() !== BigInt(4)) {\n      hilog.error(0x0000, 'mmkvdemo', 'import check count fail');\n    }\n    if (!dst.decodeBool(\"bool\")) {\n      hilog.error(0x0000, 'mmkvdemo', 'import check bool fail');\n    }\n    if (dst.decodeInt32(\"int\") !== -2147483648) {\n      hilog.error(0x0000, 'mmkvdemo', 'import check int fail');\n    }\n    if (dst.decodeUInt64(\"long\") !== 9223372036854775807n) {\n      hilog.error(0x0000, 'mmkvdemo', 'import check long fail');\n    }\n    if (dst.decodeString(\"string\") !== \"test import\") {\n      hilog.error(0x0000, 'mmkvdemo', 'import check string fail');\n    }\n\n    // ArkTS/TypeScript equivalent of sleep\n    await new Promise<void>(resolve => setTimeout(resolve, 1000 * 2));\n\n    if (dst.count(true) !== BigInt(0)) {\n      console.error(\"MMKV: import check expire fail\");\n    }\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/ets/process/DemoProcess.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport ChildProcess from '@ohos.app.ability.ChildProcess';\n\nexport class DemoProcess extends ChildProcess {\n  onStart() {\n    console.log(\"DemoProcess OnStart() called\");\n  }\n\n  static toString() {\n    return 'a DemoProcess class';\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/module.json5",
    "content": "{\n  \"module\": {\n    \"name\": \"entry\",\n    \"type\": \"entry\",\n    \"description\": \"$string:module_desc\",\n    \"mainElement\": \"EntryAbility\",\n    \"deviceTypes\": [\n      \"phone\",\n      \"tablet\",\n      \"2in1\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:main_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"EntryAbility\",\n        \"srcEntry\": \"./ets/entryability/EntryAbility.ets\",\n        \"description\": \"$string:EntryAbility_desc\",\n        \"icon\": \"$media:layered_image\",\n        \"label\": \"$string:EntryAbility_label\",\n        \"startWindowIcon\": \"$media:startIcon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"exported\": true,\n        \"skills\": [\n          {\n            \"entities\": [\n              \"entity.system.home\"\n            ],\n            \"actions\": [\n              \"action.system.home\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"MMKV Demo\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/base/media/layered_image.json",
    "content": "{\n  \"layered-image\":\n  {\n    \"background\" : \"$media:background\",\n    \"foreground\" : \"$media:foreground\"\n  }\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/base/profile/main_pages.json",
    "content": "{\n  \"src\": [\n    \"pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/en_US/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"MMKV Demo\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/main/resources/zh_CN/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"模块描述\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"MMKV Demo\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/mock/mock-config.json5",
    "content": "{\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/ets/test/Ability.test.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';\n\nexport default function abilityTest() {\n  describe('ActsAbilityTest', () => {\n    // Defines a test suite. Two parameters are supported: test suite name and test suite function.\n    beforeAll(() => {\n      // Presets an action, which is performed only once before all test cases of the test suite start.\n      // This API supports only one parameter: preset action function.\n    })\n    beforeEach(() => {\n      // Presets an action, which is performed before each unit test case starts.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: preset action function.\n    })\n    afterEach(() => {\n      // Presets a clear action, which is performed after each unit test case ends.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: clear action function.\n    })\n    afterAll(() => {\n      // Presets a clear action, which is performed after all test cases of the test suite end.\n      // This API supports only one parameter: clear action function.\n    })\n    it('assertContain', 0, () => {\n      // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n      hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');\n      let a = 'abc';\n      let b = 'b';\n      // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n      expect(a).assertContain(b);\n      expect(a).assertEqual(a);\n    })\n  })\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/ets/test/List.test.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport abilityTest from './Ability.test';\n\nexport default function testsuite() {\n  abilityTest();\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/ets/testability/TestAbility.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';\nimport { abilityDelegatorRegistry } from '@kit.TestKit';\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { window } from '@kit.ArkUI';\nimport { Hypium } from '@ohos/hypium';\nimport testsuite from '../test/List.test';\n\nexport default class TestAbility extends UIAbility {\n  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');\n    hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');\n    hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');\n    let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;\n    abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator();\n    let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;\n    abilityDelegatorArguments = abilityDelegatorRegistry.getArguments();\n    hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');\n    Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);\n  }\n\n  onDestroy() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');\n  }\n\n  onWindowStageCreate(windowStage: window.WindowStage) {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');\n    windowStage.loadContent('testability/pages/Index', (err) => {\n      if (err.code) {\n        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');\n        return;\n      }\n      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');\n    });\n  }\n\n  onWindowStageDestroy() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');\n  }\n\n  onForeground() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');\n  }\n\n  onBackground() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');\n  }\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/ets/testability/pages/Index.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@Entry\n@Component\nstruct Index {\n  @State message: string = 'Hello World';\n\n  build() {\n    Row() {\n      Column() {\n        Text(this.message)\n          .fontSize(50)\n          .fontWeight(FontWeight.Bold)\n      }\n      .width('100%')\n    }\n    .height('100%')\n  }\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit';\nimport { UIAbility, Want } from '@kit.AbilityKit';\nimport { BusinessError } from '@kit.BasicServicesKit';\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport { resourceManager } from '@kit.LocalizationKit';\nimport { util } from '@kit.ArkTS';\n\nlet abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;\nlet abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;\nlet jsonPath: string = 'mock/mock-config.json';\nlet tag: string = 'testTag';\n\nasync function onAbilityCreateCallback(data: UIAbility) {\n  hilog.info(0x0000, 'testTag', 'onAbilityCreateCallback, data: ${}', JSON.stringify(data));\n}\n\nasync function addAbilityMonitorCallback(err: BusinessError) {\n  hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');\n}\n\nexport default class OpenHarmonyTestRunner implements TestRunner {\n  constructor() {\n  }\n\n  onPrepare() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare');\n  }\n\n  async onRun() {\n    let tag = 'testTag';\n    hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run');\n    abilityDelegatorArguments = abilityDelegatorRegistry.getArguments()\n    abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator()\n    let moduleName = abilityDelegatorArguments.parameters['-m'];\n    let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName);\n    let mResourceManager = context.resourceManager;\n    await checkMock(abilityDelegator, mResourceManager);\n    const bundleName = abilityDelegatorArguments.bundleName;\n    const testAbilityName: string = 'TestAbility';\n    let lMonitor: abilityDelegatorRegistry.AbilityMonitor = {\n      abilityName: testAbilityName,\n      onAbilityCreate: onAbilityCreateCallback,\n      moduleName: moduleName\n    };\n    abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)\n    const want: Want = {\n      bundleName: bundleName,\n      abilityName: testAbilityName,\n      moduleName: moduleName\n    };\n    abilityDelegator.startAbility(want, (err: BusinessError, data: void) => {\n      hilog.info(0x0000, tag, 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');\n      hilog.info(0x0000, tag, 'startAbility : data : %{public}s', JSON.stringify(data) ?? '');\n    })\n    hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end');\n  }\n}\n\nasync function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) {\n  let rawFile: Uint8Array;\n  try {\n    rawFile = resourceManager.getRawFileContentSync(jsonPath);\n    hilog.info(0x0000, tag, 'MockList file exists');\n    let mockStr: string = util.TextDecoder.create(\"utf-8\", { ignoreBOM: true }).decodeWithStream(rawFile);\n    let mockMap: Record<string, string> = getMockList(mockStr);\n    try {\n      abilityDelegator.setMockList(mockMap)\n    } catch (error) {\n      let code = (error as BusinessError).code;\n      let message = (error as BusinessError).message;\n      hilog.error(0x0000, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`);\n    }\n  } catch (error) {\n    let code = (error as BusinessError).code;\n    let message = (error as BusinessError).message;\n    hilog.error(0x0000, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`);\n  }\n}\n\nfunction getMockList(jsonStr: string) {\n  let jsonObj: Record<string, Object> = JSON.parse(jsonStr);\n  let map: Map<string, object> = new Map<string, object>(Object.entries(jsonObj));\n  let mockList: Record<string, string> = {};\n  map.forEach((value: object, key: string) => {\n    let realValue: string = value['source'].toString();\n    mockList[key] = realValue;\n  });\n  hilog.info(0x0000, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? '');\n  return mockList;\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/module.json5",
    "content": "{\n  \"module\": {\n    \"name\": \"entry_test\",\n    \"type\": \"feature\",\n    \"description\": \"$string:module_test_desc\",\n    \"mainElement\": \"TestAbility\",\n    \"deviceTypes\": [\n      \"phone\",\n      \"tablet\",\n      \"2in1\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:test_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"TestAbility\",\n        \"srcEntry\": \"./ets/testability/TestAbility.ets\",\n        \"description\": \"$string:TestAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:TestAbility_label\",\n        \"exported\": true,\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"skills\": [\n          {\n            \"actions\": [\n              \"action.system.home\"\n            ],\n            \"entities\": [\n              \"entity.system.home\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_test_desc\",\n      \"value\": \"test ability description\"\n    },\n    {\n      \"name\": \"TestAbility_desc\",\n      \"value\": \"the test ability\"\n    },\n    {\n      \"name\": \"TestAbility_label\",\n      \"value\": \"test label\"\n    }\n  ]\n}"
  },
  {
    "path": "OpenHarmony/entry/src/ohosTest/resources/base/profile/test_pages.json",
    "content": "{\n  \"src\": [\n    \"testability/pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "OpenHarmony/entry/src/test/List.test.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport localUnitTest from './LocalUnit.test';\n\nexport default function testsuite() {\n  localUnitTest();\n}"
  },
  {
    "path": "OpenHarmony/entry/src/test/LocalUnit.test.ets",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';\n\nexport default function localUnitTest() {\n  describe('localUnitTest',() => {\n    // Defines a test suite. Two parameters are supported: test suite name and test suite function.\n    beforeAll(() => {\n      // Presets an action, which is performed only once before all test cases of the test suite start.\n      // This API supports only one parameter: preset action function.\n    });\n    beforeEach(() => {\n      // Presets an action, which is performed before each unit test case starts.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: preset action function.\n    });\n    afterEach(() => {\n      // Presets a clear action, which is performed after each unit test case ends.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: clear action function.\n    });\n    afterAll(() => {\n      // Presets a clear action, which is performed after all test cases of the test suite end.\n      // This API supports only one parameter: clear action function.\n    });\n    it('assertContain', 0, () => {\n      // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n      let a = 'abc';\n      let b = 'b';\n      // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n      expect(a).assertContain(b);\n      expect(a).assertEqual(a);\n    });\n  });\n}"
  },
  {
    "path": "OpenHarmony/hvigor/hvigor-config.json5",
    "content": "{\n  \"modelVersion\": \"5.0.0\",\n  \"dependencies\": {\n  },\n  \"execution\": {\n    // \"analyze\": \"default\",                    /* Define the build analyze mode. Value: [ \"default\" | \"verbose\" | false ]. Default: \"default\" */\n    // \"daemon\": true,                          /* Enable daemon compilation. Value: [ true | false ]. Default: true */\n    // \"incremental\": true,                     /* Enable incremental compilation. Value: [ true | false ]. Default: true */\n    // \"parallel\": true,                        /* Enable parallel compilation. Value: [ true | false ]. Default: true */\n    // \"typeCheck\": false,                      /* Enable typeCheck. Value: [ true | false ]. Default: false */\n  },\n  \"logging\": {\n    // \"level\": \"info\"                          /* Define the log level. Value: [ \"debug\" | \"info\" | \"warn\" | \"error\" ]. Default: \"info\" */\n  },\n  \"debugging\": {\n    // \"stacktrace\": false                      /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */\n  },\n  \"nodeOptions\": {\n    // \"maxOldSpaceSize\": 4096                  /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */\n  }\n}"
  },
  {
    "path": "OpenHarmony/hvigorfile.ts",
    "content": "import { appTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: appTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}\n"
  },
  {
    "path": "OpenHarmony/oh-package.json5",
    "content": "{\n  \"modelVersion\": \"5.0.0\",\n  \"name\": \"mmkvdemo\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A demo for MMKV in OpenHarmony OS.\",\n  \"main\": \"\",\n  \"author\": \"guoling\",\n  \"license\": \"BSD 3-Clause\",\n  \"dependencies\": {\n  },\n  \"devDependencies\": {\n    \"@ohos/hypium\": \"1.0.16\",\n    \"@ohos/hamock\": \"1.0.0\"\n  }\n}"
  },
  {
    "path": "POSIX/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2019 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ncmake_minimum_required(VERSION 3.10.0)\n\nproject(MMKV)\n\nadd_subdirectory(src)\nadd_subdirectory(demo)\n\n"
  },
  {
    "path": "POSIX/demo/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2019 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ncmake_minimum_required(VERSION 3.10.0)\n\nproject(demo)\n\nIF(APPLE)\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\nadd_executable(demo\n        demo.cpp)\ntarget_link_libraries(demo\n        mmkv)\nset_target_properties(demo PROPERTIES\n        CXX_STANDARD 20\n        )\n\nadd_executable(process\n        process.cpp)\ntarget_link_libraries(process\n        mmkv)\nset_target_properties(process PROPERTIES\n        CXX_STANDARD 17\n        )\n\nadd_executable(TestInterProcessLock\n        TestInterProcessLock.cpp)\ntarget_link_libraries(TestInterProcessLock\n        mmkv)\nset_target_properties(TestInterProcessLock PROPERTIES\n        CXX_STANDARD 17\n        )\n\nadd_executable(UnitTest\n        UnitTest.cpp)\ntarget_link_libraries(UnitTest\n        mmkv)\nset_target_properties(UnitTest PROPERTIES\n        CXX_STANDARD 20\n        )\n\nadd_dependencies(demo\n        process\n        TestInterProcessLock\n        UnitTest)\n\n"
  },
  {
    "path": "POSIX/demo/TestInterProcessLock.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n#include <iostream>\n#include <pthread.h>\n#include <semaphore.h>\n#include <string>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <cstring>\n#include <sys/mman.h>\n\n// it's not a must-have for most app so do it the handy way\n#include \"../../Core/InterProcessLock.h\"\n\nusing namespace std;\nusing namespace mmkv;\n\n#define MMKV_ID \"TestInterProcessLock\"\n\nsem_t *semEnded = nullptr;\nint threadIndex = 0;\nFileLock *flockPtr = nullptr;\n\nvoid *threadFunction(void *lpParam) {\n    auto sem = (sem_t *) lpParam;\n    sem_post(sem);\n    auto index = threadIndex;\n    cout << \"Thread \" << index << \" started\" << endl;\n\n    //auto mmkv = MMKV::mmkvWithID(MMKV_ID, MMKV_MULTI_PROCESS);\n    // this should hold forever\n    //mmkv->count();\n    flockPtr->lock(SharedLockType);\n    //flockPtr->unlock(SharedLockType);\n\n    // something is wrong with inter-process lock\n    sem_post(semEnded);\n    cout << \"Thread \" << index << \" ended\" << endl;\n    return nullptr;\n}\n\nbool threadTest() {\n    sem_t *semParent = sem_open(\"/mmkv_main\", O_CREAT, 0644, 0);\n\n    //auto mmkv = MMKV::mmkvWithID(MMKV_ID, MMKV_MULTI_PROCESS);\n    auto fd = open(\"/tmp/mmkv/TestInterProcessLock.file\", O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);\n    flockPtr = new FileLock(fd);\n\n    sem_post(semParent);\n    cout << \"Waiting for parent...\" << endl;\n    usleep(1000);\n    sem_wait(semParent);\n    sem_close(semParent);\n    cout << \"Parent locked\" << endl;\n\n    semEnded = sem_open(MMKV_ID, O_CREAT, 0644, 0);\n\n    sem_t *semStarted = sem_open(\"/mmkv_sem_started\", O_CREAT, 0644, 0);\n    if (!semStarted) {\n        printf(\"fail to create semphare: %d(%s)\\n\", errno, strerror(errno));\n        exit(1);\n    }\n    for (int index = 0; index < 2; ++index) {\n        pthread_t threadHandle;\n        pthread_create(&threadHandle, nullptr, threadFunction, semStarted);\n        sem_wait(semStarted);\n        threadIndex++;\n    }\n    sem_close(semStarted);\n\n    cout << \"Waiting for any child exit...\" << endl;\n    usleep(1000);\n    auto ret = sem_trywait(semEnded);\n    sem_close(semEnded);\n    cout << \"Any child exit: \" << (ret == 0) << endl;\n\n    delete flockPtr;\n    flockPtr = nullptr;\n    //close(fd);\n\n    return (ret != 0);\n}\n\nint main() {\n    locale::global(locale(\"\"));\n    wcout.imbue(locale(\"\"));\n\n    string rootDir = \"/tmp/mmkv\";\n    MMKV::initializeMMKV(rootDir);\n\n    auto processID = getpid();\n    cout << \"TestInterProcessLock: \" << processID << \" started\\n\";\n\n//    auto fd3 = open(\"/tmp/mmkv/TestInterProcessLock2.file\", O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);\n//    FileLock flock3(fd3);\n//\n//    auto ptr = ::mmap(nullptr, DEFAULT_MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd3, 0);\n//    printf(\"mmap fd %d to %p\\n\", fd3, ptr);\n//\n//    printf(\"trying flock.try_lock %d\\n\", fd3);\n//    flock3.try_lock(ExclusiveLockType, nullptr);\n//    printf(\"trying flock.lock %d\\n\", fd3);\n//    flock3.lock(ExclusiveLockType);\n//    printf(\"finish flock.lock %d\\n\", fd3);\n\n    auto ret = threadTest();\n    cout << \"TestInterProcessLock: \" << (ret ? \"pass\" : \"failed\") << endl;\n    cout << \"TestInterProcessLock: \" << processID << \" ended\\n\";\n\n    return 0;\n}\n"
  },
  {
    "path": "POSIX/demo/UnitTest.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n#include <cassert>\n#include <cmath>\n#include <cstdio>\n#include <cstring>\n#include <iostream>\n#include <limits>\n#include <numeric>\n#include <unistd.h>\n\nusing namespace std;\nusing namespace mmkv;\n\nstatic const string KeyNotExist = \"KeyNotExist\";\n\nvoid testBool(MMKV *mmkv) {\n    auto ret = mmkv->set(true, \"bool\");\n    assert(ret);\n\n    auto value = mmkv->getBool(\"bool\");\n    assert(value);\n\n    value = mmkv->getBool(KeyNotExist);\n    assert(!value);\n\n    value = mmkv->getBool(KeyNotExist, true);\n    assert(value);\n\n    printf(\"test bool: passed\\n\");\n}\n\nvoid testInt32(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<int32_t>::max(), \"int32\");\n    assert(ret);\n\n    auto value = mmkv->getInt32(\"int32\");\n    assert(value == numeric_limits<int32_t>::max());\n\n    value = mmkv->getInt32(KeyNotExist);\n    assert(value == 0);\n\n    value = mmkv->getInt32(KeyNotExist, -1);\n    assert(value == -1);\n\n    printf(\"test int32: passed\\n\");\n}\n\nvoid testUInt32(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<uint32_t>::max(), \"uint32\");\n    assert(ret);\n\n    auto value = mmkv->getUInt32(\"uint32\");\n    assert(value == numeric_limits<uint32_t>::max());\n\n    value = mmkv->getUInt32(KeyNotExist);\n    assert(value == 0);\n\n    value = mmkv->getUInt32(KeyNotExist, -1);\n    assert(value == -1);\n\n    printf(\"test uint32: passed\\n\");\n}\n\nvoid testInt64(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<int64_t>::min(), \"int64\");\n    assert(ret);\n\n    auto value = mmkv->getInt64(\"int64\");\n    assert(value == numeric_limits<int64_t>::min());\n\n    value = mmkv->getInt64(KeyNotExist);\n    assert(value == 0);\n\n    value = mmkv->getInt64(KeyNotExist, -1);\n    assert(value == -1);\n\n    printf(\"test int64: passed\\n\");\n}\n\nvoid testUInt64(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<uint64_t>::max(), \"uint64\");\n    assert(ret);\n\n    auto value = mmkv->getUInt64(\"uint64\");\n    assert(value == numeric_limits<uint64_t>::max());\n\n    value = mmkv->getUInt64(KeyNotExist);\n    assert(value == 0);\n\n    value = mmkv->getUInt64(KeyNotExist, -1);\n    assert(value == -1);\n\n    printf(\"test uint64: passed\\n\");\n}\n\ntemplate <typename T>\nbool EqualWithAccuracy(T value1, T value2, T accuracy) {\n    return fabs(value1 - value2) <= accuracy;\n}\n\nvoid testFloat(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<float>::max(), \"float\");\n    assert(ret);\n\n    auto value = mmkv->getFloat(\"float\");\n    assert(EqualWithAccuracy(value, numeric_limits<float>::max(), 0.001f));\n\n    value = mmkv->getFloat(KeyNotExist);\n    assert(EqualWithAccuracy(value, 0.0f, 0.001f));\n\n    value = mmkv->getFloat(KeyNotExist, -1.0f);\n    assert(EqualWithAccuracy(value, -1.0f, 0.001f));\n\n    printf(\"test float: passed\\n\");\n}\n\nvoid testDouble(MMKV *mmkv) {\n    auto ret = mmkv->set(numeric_limits<double>::max(), \"double\");\n    assert(ret);\n\n    auto value = mmkv->getDouble(\"double\");\n    assert(EqualWithAccuracy(value, numeric_limits<double>::max(), 0.001));\n\n    value = mmkv->getDouble(KeyNotExist);\n    assert(EqualWithAccuracy(value, 0.0, 0.001));\n\n    value = mmkv->getDouble(KeyNotExist, -1.0);\n    assert(EqualWithAccuracy(value, -1.0, 0.001));\n\n    printf(\"test double: passed\\n\");\n}\n\nvoid testString(MMKV *mmkv) {\n    string str = \"Hello 2018 world cup 世界杯\";\n    auto ret = mmkv->set(str, \"string\");\n    assert(ret);\n\n    string value;\n    ret = mmkv->getString(\"string\", value);\n    assert(ret && str == value);\n\n    const char *cString = \"Hello 2022 world cup 世界杯\";\n    ret = mmkv->set(cString, \"cstring\");\n    assert(ret);\n\n    ret = mmkv->getString(\"cstring\", value);\n    assert(ret && value == cString);\n\n    ret = mmkv->getString(KeyNotExist, value);\n    assert(!ret);\n\n    printf(\"test string: passed\\n\");\n}\n\nvoid testBytes(MMKV *mmkv) {\n    string str = \"Hello 2018 world cup 世界杯\";\n    MMBuffer buffer((void *) str.data(), str.length(), MMBufferNoCopy);\n    auto ret = mmkv->set(buffer, \"bytes\");\n    assert(ret);\n\n    auto value = mmkv->getBytes(\"bytes\");\n    assert(value.length() == buffer.length() && memcmp(value.getPtr(), buffer.getPtr(), value.length()) == 0);\n\n    value = mmkv->getBytes(KeyNotExist);\n    assert(value.length() == 0);\n\n    printf(\"test bytes: passed\\n\");\n}\n\nvoid testVector(MMKV *mmkv) {\n    vector<string> v = {\"1\", \"0\", \"2\", \"4\"};\n    auto ret = mmkv->set(v, \"vector\");\n    assert(ret);\n\n    vector<string> value;\n    ret = mmkv->getVector(\"vector\", value);\n    assert(ret && value == v);\n\n    printf(\"test vector: passed\\n\");\n}\n\nvoid testRemove(MMKV *mmkv) {\n    auto ret = mmkv->set(true, \"bool_1\");\n    ret &= mmkv->set(numeric_limits<int32_t>::max(), \"int_1\");\n    ret &= mmkv->set(numeric_limits<int64_t>::max(), \"long_1\");\n    ret &= mmkv->set(numeric_limits<float>::min(), \"float_1\");\n    ret &= mmkv->set(numeric_limits<double>::min(), \"double_1\");\n    ret &= mmkv->set(\"hello\", \"string_1\");\n    vector<string> v = vector<string>{\"key\", \"value\"};\n    ret &= mmkv->set(v, \"vector_1\");\n    assert(ret);\n\n    {\n        long count = mmkv->count();\n\n        mmkv->removeValueForKey(\"bool_1\");\n        mmkv->removeValuesForKeys({\"int_1\", \"long_1\"});\n\n        auto newCount = mmkv->count();\n        assert(count == newCount + 3);\n    }\n\n    auto bValue = mmkv->getBool(\"bool_1\");\n    assert(!bValue);\n\n    auto iValue = mmkv->getInt32(\"int_1\");\n    assert(iValue == 0);\n\n    auto lValue = mmkv->getInt64(\"long_1\");\n    assert(lValue == 0);\n\n    auto fValue = mmkv->getFloat(\"float_1\");\n    assert(EqualWithAccuracy(fValue, numeric_limits<float>::min(), 0.001f));\n\n    double dValue = mmkv->getDouble(\"double_1\");\n    assert(EqualWithAccuracy(dValue, numeric_limits<double>::min(), 0.001));\n\n    string sValue;\n    ret = mmkv->getString(\"string_1\", sValue);\n    assert(ret && sValue == \"hello\");\n\n    vector<string> vValue;\n    ret = mmkv->getVector(\"vector_1\", vValue);\n    assert(ret && vValue == v);\n\n    printf(\"test remove: passed\\n\");\n}\n\nint main() {\n    locale::global(locale(\"\"));\n    wcout.imbue(locale(\"\"));\n    char c;\n    srand((uint64_t) &c);\n\n    string rootDir = \"/tmp/mmkv\";\n    MMKV::initializeMMKV(rootDir);\n\n    auto mmkv = MMKV::mmkvWithID(\"unit_test\");\n    mmkv->clearAll();\n\n    testBool(mmkv);\n    testInt32(mmkv);\n    testInt64(mmkv);\n    testUInt32(mmkv);\n    testUInt64(mmkv);\n    testFloat(mmkv);\n    testDouble(mmkv);\n    testString(mmkv);\n    testBytes(mmkv);\n    testVector(mmkv);\n    testRemove(mmkv);\n}\n"
  },
  {
    "path": "POSIX/demo/demo.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n#include <chrono>\n#include <cstdio>\n#include <iostream>\n#include <limits>\n#include <pthread.h>\n#include <semaphore.h>\n#include <string>\n#include <sys/wait.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <unistd.h>\n#include <cstring>\n#include <cassert>\n#include <ctime>\n#include <cmath>\n#include <cinttypes> // For PRId64 & PRIu64\n#include <sys/mman.h>\n\n// it's not a must-have for most app so do it the handy way\n#include \"../../Core/InterProcessLock.h\"\n\nusing namespace std;\nusing namespace mmkv;\n\nstring to_string(const std::string& str) {  return str; }\n\ntemplate <class T>\nstring to_string(const vector<T> &arr, const char* sp = \", \") {\n    string str;\n    for (const auto &element : arr) {\n        str += to_string(element);\n        str += sp;\n    }\n    if (!str.empty()) {\n        str.erase(str.length() - strlen(sp));\n    }\n    return str;\n}\n\nvoid containerTest(MMKV *mmkv, bool decodeOnly);\n\nvoid functionalTest(MMKV *mmkv, bool decodeOnly) {\n    if (!decodeOnly) {\n        mmkv->set(true, \"bool\");\n    }\n    cout << \"bool = \" << mmkv->getBool(\"bool\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(1024, \"int32\");\n    }\n    cout << \"int32 = \" << mmkv->getInt32(\"int32\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<uint32_t>::max(), \"uint32\");\n    }\n    cout << \"uint32 = \" << mmkv->getUInt32(\"uint32\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<int64_t>::min(), \"int64\");\n    }\n    cout << \"int64 = \" << mmkv->getInt64(\"int64\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<uint64_t>::max(), \"uint64\");\n    }\n    cout << \"uint64 = \" << mmkv->getUInt64(\"uint64\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(3.14f, \"float\");\n    }\n    cout << \"float = \" << mmkv->getFloat(\"float\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<double>::max(), \"double\");\n    }\n    cout << \"double = \" << mmkv->getDouble(\"double\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(\"Hello, MMKV-示例 for POSIX\", \"raw_string\");\n        string str = \"Hello, MMKV-示例 for POSIX string\";\n        mmkv->set(str, \"string\");\n        mmkv->set(string_view(str).substr(7, 21), \"string_view\");\n    }\n    string result;\n    mmkv->getString(\"raw_string\", result);\n    cout << \"raw_string = \" << result << endl;\n    mmkv->getString(\"string\", result);\n    cout << \"string = \" << result << endl;\n    mmkv->getString(\"string_view\", result);\n    cout << \"string_view = \" << result << endl;\n\n    containerTest(mmkv, decodeOnly);\n\n    cout << \"allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n    cout << \"count = \" << mmkv->count() << \", totalSize = \" << mmkv->totalSize() << endl;\n    cout << \"containsKey[string]: \" << mmkv->containsKey(\"string\") << endl;\n\n    mmkv->removeValueForKey(\"bool\");\n    cout << \"bool: \" << mmkv->getBool(\"bool\") << endl;\n    mmkv->removeValuesForKeys({\"int\", \"long\"});\n\n    mmkv->set(\"some string\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    cout << \"string before set null: \" << result << endl;\n    mmkv->set((const char *) nullptr, \"null string\");\n    //mmkv->set(\"\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    cout << \"string after set null: \" << result << \", containsKey:\" << mmkv->containsKey(\"null string\") << endl;\n\n    //kv.sync();\n    //kv.async();\n    //kv.clearAll();\n    mmkv->clearMemoryCache();\n    cout << \"allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n    cout << \"isFileValid[\" << mmkv->mmapID() + \"]: \" << MMKV::isFileValid(mmkv->mmapID()) << endl;\n}\n\nvoid containerTest(MMKV *mmkv, bool decodeOnly) {\n    {\n        if (!decodeOnly) {\n            vector<string> vec = {\"Hello\", \"MMKV-示例\", \"for\", \"POSIX\"};\n            mmkv->set(vec, \"string-set\");\n        }\n        vector<string> vecResult;\n        mmkv->getVector(\"string-set\", vecResult);\n        cout << \"string-set = \" << to_string(vecResult) << endl;\n    }\n#if __cplusplus>=202002L\n    {\n        if (!decodeOnly) {\n            vector<bool> vec = {true, false, std::numeric_limits<bool>::min(), std::numeric_limits<bool>::max()};\n            mmkv->set(vec, \"bool-set\");\n        }\n        vector<bool> vecResult;\n        mmkv->getVector(\"bool-set\", vecResult);\n        cout << \"bool-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            vector<int32_t> vec = {1024, 0, std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max()};\n            mmkv->set(vec, \"int32-set\");\n        }\n        vector<int32_t> vecResult;\n        mmkv->getVector(\"int32-set\", vecResult);\n        cout << \"int32-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            constexpr uint32_t arr[] = {2048, 0, std::numeric_limits<uint32_t>::min(), std::numeric_limits<uint32_t>::max()};\n            std::span vec = arr;\n            mmkv->set(vec, \"uint32-set\");\n        }\n        vector<uint32_t> vecResult;\n        mmkv->getVector(\"uint32-set\", vecResult);\n        cout << \"uint32-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            constexpr int64_t vec[] = {1024, 0, std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()};\n            mmkv->set(std::span(vec), \"int64-set\");\n        }\n        vector<int64_t> vecResult;\n        mmkv->getVector(\"int64-set\", vecResult);\n        cout << \"int64-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            vector<uint64_t> vec = {1024, 0, std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max()};\n            mmkv->set(vec, \"uint64-set\");\n        }\n        vector<uint64_t> vecResult;\n        mmkv->getVector(\"uint64-set\", vecResult);\n        cout << \"uint64-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            vector<float> vec = {1024.0, 0.0, std::numeric_limits<float>::min(), std::numeric_limits<float>::max()};\n            mmkv->set(vec, \"float-set\");\n        }\n        vector<float> vecResult;\n        mmkv->getVector(\"float-set\", vecResult);\n        cout << \"float-set = \" << to_string(vecResult) << endl;\n    }\n\n    {\n        if (!decodeOnly) {\n            vector<double> vec = {1024.0, 0.0, std::numeric_limits<double>::min(), std::numeric_limits<double>::max()};\n            mmkv->set(vec, \"double-set\");\n        }\n        vector<double> vecResult;\n        mmkv->getVector(\"double-set\", vecResult);\n        cout << \"double-set = \" << to_string(vecResult) << endl;\n\n        // un-comment to test the functionality of set<!MMKV_SUPPORTED_VALUE_TYPE<T>>(const T& value, key)\n        // mmkv->set(&vecResult, \"unsupported-type\");\n    }\n#endif // __cplusplus>=202002L\n}\n\nconstexpr int32_t keyCount = 10000;\nconstexpr int32_t threadCount = 10;\nstatic const string MMKV_ID = \"thread_test1\";\nvector<string> arrIntKeys;\nvector<string> arrStringKeys;\n\nvoid *threadFunction(void *lpParam) {\n    auto threadIndex = (size_t) lpParam;\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID);\n    mmkv->lock();\n    cout << \"thread \" << threadIndex << \" starts\" << endl;\n    mmkv->unlock();\n\n    auto segmentCount = keyCount / threadCount;\n    auto startIndex = segmentCount * threadIndex;\n    for (int32_t index = startIndex; index < startIndex + segmentCount; index++) {\n        mmkv->set(index, arrIntKeys[index]);\n        mmkv->set(\"str-\" + to_string(index), arrStringKeys[index]);\n    }\n\n    mmkv->lock();\n    cout << \"thread \" << threadIndex << \" ends\" << endl;\n    mmkv->unlock();\n    return nullptr;\n}\n\nvoid threadTest() {\n    pthread_t threadHandles[threadCount] = {0};\n    for (size_t index = 0; index < threadCount; index++) {\n        pthread_create(&threadHandles[index], nullptr, threadFunction, (void *) index);\n    }\n    for (auto threadHandle : threadHandles) {\n        pthread_join(threadHandle, nullptr);\n    }\n\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID);\n    cout << \"total count \" << mmkv->count() << endl;\n}\n\nvoid brutleTest() {\n    using hclock = chrono::high_resolution_clock;\n    auto start = hclock::now();\n\n    auto mmkv = MMKV::mmkvWithID(\"brutleTest\");\n    for (int32_t i = 0; i < keyCount; i++) {\n        mmkv->set(i, arrIntKeys[i]);\n        mmkv->set(\"str-\" + to_string(i), arrStringKeys[i]);\n    }\n\n    auto finish = hclock::now();\n    long long used = chrono::duration_cast<chrono::milliseconds>(finish - start).count();\n    printf(\"encode int & string %d times, cost: %lld ms\\n\", keyCount, used);\n}\n\nvoid processTest() {\n    constexpr auto processCount = 2;\n    pid_t processHandles[processCount] = {0};\n    for (int &processHandle : processHandles) {\n        auto pid = fork();\n        // this is child\n        if (pid <= 0) {\n            execl(\"process\", \"process\", nullptr);\n            perror(\"execl\"); // execl doesn't return unless there is a problem\n            exit(1);\n        } else {\n            processHandle = pid;\n        }\n    }\n\n    for (int &processHandle : processHandles) {\n        printf(\"Waiting for child %d ...\\n\", processHandle);\n        auto pid = waitpid(processHandle, nullptr, 0);\n        printf(\"Child quit pid: %d\\n\", pid);\n    }\n\n    auto mmkv = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS);\n    cout << \"total count of process_test: \" << mmkv->count() << endl;\n}\n\nvoid testInterProcessLock() {\n    //auto mmkv = MMKV::mmkvWithID(\"TestInterProcessLock\", MMKV_MULTI_PROCESS);\n    //mmkv->set(true, \"bool\");\n\n    auto fd = open(\"/tmp/mmkv/TestInterProcessLock.file\", O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);\n    FileLock flock(fd);\n\n//    auto fd2 = open(\"/tmp/mmkv/TestInterProcessLock2.file\", O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);\n//    FileLock flock2(fd2);\n//    flock2.lock(SharedLockType);\n//    auto ptr = ::mmap(nullptr, DEFAULT_MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);\n//    printf(\"mmap fd %d to %p\\n\", fd2, ptr);\n//    ::close(fd2);\n//    flock2.destroyLock();\n\n    auto pid = fork();\n    // this is child\n    if (pid <= 0) {\n        execl(\"TestInterProcessLock\", \"TestInterProcessLock\", nullptr);\n        perror(\"execl\"); // execl doesn't return unless there is a problem\n        exit(1);\n    }\n    printf(\"Waiting for child %d to start ...\\n\", pid);\n    sem_t *sem = sem_open(\"/mmkv_main\", O_CREAT, 0644, 0);\n    if (!sem) {\n        printf(\"fail to create semaphore: %d(%s)\\n\", errno, strerror(errno));\n        exit(1);\n    }\n    sem_wait(sem);\n    printf(\"Child %d to started\\n\", pid);\n\n    //mmkv->clearAll();\n    //mmkv->lock();\n    flock.lock(ExclusiveLockType);\n\n    sem_post(sem);\n    sem_close(sem);\n\n    printf(\"Waiting for child %d to finish...\\n\", pid);\n    waitpid(pid, nullptr, 0);\n    printf(\"Child %d to finished\\n\", pid);\n\n    //mmkv->unlock();\n    flock.unlock(ExclusiveLockType);\n    close(fd);\n}\n\nvoid cornetSizeTest() {\n    string aesKey = \"aes\";\n    auto mmkv = MMKV::mmkvWithID(\"cornerSize\", MMKV_MULTI_PROCESS, &aesKey);\n    mmkv->clearAll();\n    auto size = getpagesize() - 2;\n    size -= 4;\n    string key = \"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    mmkv::MMBuffer value(size);\n    mmkv->set(value, key);\n    mmkv->trim();\n}\n\nvoid itemSizeHolderTest() {\n    auto mmkv = MMKV::mmkvWithID(\"itemsize\");\n    // mmkv->clearAll();\n    mmkv->set(true, \"bool\");\n    mmkv->clearMemoryCache();\n    // you won't find the key \"bool\"\n    cout << \"allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n\n    string aesKey = \"cryptKey\";\n    mmkv = MMKV::mmkvWithID(\"itemsizecrypt\", MMKV_SINGLE_PROCESS, &aesKey);\n    mmkv->set(true, \"bool\");\n    mmkv->clearMemoryCache();\n    // you won't find the key \"bool\"\n    cout << \"allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n}\n\nvoid fastRemoveCornetSizeTest() {\n    string aesKey = \"aes\";\n    auto mmkv = MMKV::mmkvWithID(\"fastRemoveCornerSize\", MMKV_MULTI_PROCESS, &aesKey);\n    mmkv->clearAll();\n    auto size = getpagesize() - 4;\n    size -= 4;\n    string key = \"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    size -= (keySize + 1); // total size of fast remove\n    size /= 16;\n    mmkv::MMBuffer value(size);\n    auto ptr = (char *) value.getPtr();\n    for (size_t i = 0; i < value.length(); i++) {\n        ptr[i] = 'A';\n    }\n    for (int i = 0; i < 16; i++) {\n        mmkv->set(value, key); // when a full write back is occur, here's corruption happens\n        mmkv->removeValueForKey(key);\n    }\n}\n\nvoid testClearEmptyMMKV() {\n    auto mmkv = MMKV::mmkvWithID(\"emptyMMKV\");\n    mmkv->set(true, \"bool\");\n    mmkv->clearAll();\n    mmkv->clearAll();\n}\n\nvoid testClearAllKeepSpace() {\n    {\n        auto mmkv = MMKV::mmkvWithID(\"testClearAllKeepSpace\");\n        string big(10, '0');\n        for (int i = 0; i < 100; i++) {\n            mmkv->set(big, to_string(i));\n        }\n        size_t size0 = mmkv->totalSize();\n\n        mmkv->clearAll(true);\n        size_t size1 = mmkv->totalSize();\n        assert(size0 == size1);\n        assert(mmkv->count() == 0);\n        string key1 = \"key1\";\n        string key2 = \"key2\";\n        mmkv->set(123, key1);\n        assert(mmkv->getInt32(key1, 0) == 123);\n\n        mmkv->set(456, key2);\n        assert(mmkv->getInt32(key2, 0) == 456);\n\n        // test normal clearAll\n        assert(mmkv->count() == 2);\n        mmkv->clearAll();\n        assert(mmkv->count() == 0);\n\n        // test reopen\n        mmkv->set(1234, key1);\n        mmkv->set(12345, key2);\n        mmkv->clearAll(true);\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testClearAllKeepSpace\");\n        assert(mmkv->count() == 0);\n\n        mmkv->set(456, key2);\n        assert(mmkv->getInt32(key2, 0) == 456);\n    }\n\n    {\n        string aesKey = \"cryptKey111\";\n        auto mmkv = MMKV::mmkvWithID(\"testClearAllKeepSpaceWithCrypt\", MMKV_SINGLE_PROCESS, &aesKey);\n        string big(10, '0');\n        for (int i = 0; i < 100; i++) {\n            mmkv->set(big, to_string(i));\n        }\n        size_t size0 = mmkv->totalSize();\n\n        mmkv->clearAll(true);\n        size_t size1 = mmkv->totalSize();\n        assert(size0 == size1);\n        assert(mmkv->count() == 0);\n        string key1 = \"key1\";\n        string key2 = \"key2\";\n        mmkv->set(123, key1);\n        assert(mmkv->getInt32(key1, 0) == 123);\n\n        mmkv->set(456, key2);\n        assert(mmkv->getInt32(key2, 0) == 456);\n\n        // test normal clearAll\n        assert(mmkv->count() == 2);\n        mmkv->clearAll();\n        assert(mmkv->count() == 0);\n\n        // test reopen\n        mmkv->set(1234, key1);\n        mmkv->set(12345, key2);\n        mmkv->clearAll(true);\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testClearAllKeepSpaceWithCrypt\", MMKV_SINGLE_PROCESS, &aesKey);\n        assert(mmkv->count() == 0);\n\n        mmkv->set(456, key2);\n        assert(mmkv->getInt32(key2, 0) == 456);\n    }\n}\n\nvoid testBackup() {\n    string rootDir = \"/tmp/mmkv_backup\";\n    string mmapID = \"test/Encrypt\";\n    string aesKey = \"cryptKey\";\n    auto ret = MMKV::backupOneToDirectory(mmapID, rootDir);\n    printf(\"backup one return %d\\n\", ret);\n    if (ret) {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey, &rootDir);\n        cout << \"after backup allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n    }\n\n    auto count = MMKV::backupAllToDirectory(rootDir);\n    printf(\"backup all count: %zu\\n\", count);\n    if (count > 0) {\n        auto backupMMKV = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey, &rootDir);\n        cout << \"check on backup [\" << backupMMKV->mmapID() << \"] allKeys: \" << ::to_string(backupMMKV->allKeys(), \",\\n\") << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"brutleTest\", MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n        cout << \"check on backup [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS, nullptr, &rootDir);\n        cout << \"check on backup [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"thread_test1\", MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n        cout << \"check on backup [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n    }\n}\n\nvoid testRestore() {\n    string rootDir = \"/tmp/mmkv_backup\";\n    string mmapID = \"test/Encrypt\";\n    string aesKey = \"cryptKey\";\n    auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n    mmkv->set(__LINE__, \"test_restore_key\");\n    cout << \"before restore [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n\n    auto ret = MMKV::restoreOneFromDirectory(mmapID, rootDir);\n    printf(\"restore one return %d\\n\", ret);\n    if (ret) {\n        cout << \"after restore [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n    }\n\n    auto count = MMKV::restoreAllFromDirectory(rootDir);\n    printf(\"restore all count: %zu\\n\", count);\n    if (count > 0) {\n        auto backupMMKV = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n        cout << \"check on restore [\" << backupMMKV->mmapID() << \"] allKeys: \" << ::to_string(backupMMKV->allKeys(), \",\\n\") << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"brutleTest\");\n        cout << \"check on restore [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS);\n        cout << \"check on restore [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n\n        backupMMKV = MMKV::mmkvWithID(\"thread_test1\", MMKV_SINGLE_PROCESS);\n        cout << \"check on restore [\" << backupMMKV->mmapID() << \"] allKeys count: \" << backupMMKV->count() << endl;\n    }\n}\n\nvoid testAutoExpiration() {\n    string mmapID = \"testAutoExpire\";\n    // disable auto expire by config\n    auto config = MMKVConfig();\n    config.enableKeyExpire = false;\n    config.recover = OnErrorRecover;\n    // config.itemSizeLimit = 1;\n    auto mmkv = MMKV::mmkvWithID(mmapID, config);\n    mmkv->clearAll();\n    mmkv->trim();\n    mmkv->disableAutoKeyExpire(); // this call become a no-op\n\n    mmkv->set(true, \"auto_expire_key_1\");\n\n    // enable auto expire by config\n    mmkv->close();\n    config.enableKeyExpire = true;\n    config.expiredInSeconds = 1;\n    mmkv = MMKV::mmkvWithID(mmapID, config);\n    mmkv->enableAutoKeyExpire(1); // this call become a no-op\n\n    mmkv->set(\"never_expire_value_1\", \"never_expire_key_1\", MMKV::ExpireNever);\n    mmkv->set(\"\", \"never_expire_key_2\", MMKV::ExpireNever);\n    mmkv->set(MMBuffer(), \"never_expire_key_3\", MMKV::ExpireNever);\n\n    sleep(2);\n    assert(mmkv->containsKey(\"auto_expire_key_1\") == false);\n    assert(mmkv->containsKey(\"never_expire_key_1\") == true);\n    assert(mmkv->containsKey(\"never_expire_key_2\") == true);\n    assert(mmkv->containsKey(\"never_expire_key_3\") == true);\n    {\n        string result;\n        auto ret = mmkv->getString(\"never_expire_key_2\", result);\n        assert(ret && result.empty());\n\n        MMBuffer buffer;\n        ret = mmkv->getBytes(\"never_expire_key_3\", buffer);\n        assert(ret && buffer.length() == 0);\n    }\n\n    mmkv->removeValueForKey(\"never_expire_key_1\");\n    mmkv->enableAutoKeyExpire(MMKV::ExpireNever);\n    mmkv->set(\"never_expire_value_1\", \"never_expire_key_1\");\n    mmkv->set(true, \"auto_expire_key_1\", 1);\n    sleep(2);\n    assert(mmkv->containsKey(\"never_expire_key_1\") == true);\n    assert(mmkv->containsKey(\"auto_expire_key_1\") == false);\n\n    auto count = mmkv->count(true);\n    cout << \"count all non expire keys: \" << count << endl;\n    auto allKeys = mmkv->allKeys(true);\n    cout << \"all non expire keys: \" << ::to_string(allKeys) << endl;\n}\n\nvoid testExpectedCapacity() {\n    auto mmkv0 = MMKV::mmkvWithID(\"testExpectedCapacity0\", MMKV_SINGLE_PROCESS, nullptr, nullptr, 0);\n    assert(mmkv0->totalSize() == DEFAULT_MMAP_SIZE);\n\n    auto mmkv1 = MMKV::mmkvWithID(\"testExpectedCapacity1\", MMKV_SINGLE_PROCESS, nullptr, nullptr, DEFAULT_MMAP_SIZE + 1);\n    assert(mmkv1->totalSize() == DEFAULT_MMAP_SIZE << 1);\n\n    auto mmkv2 = MMKV::mmkvWithID(\"testExpectedCapacity2\", MMKV_SINGLE_PROCESS, nullptr, nullptr, DEFAULT_MMAP_SIZE >> 1);\n    assert(mmkv2->totalSize() == DEFAULT_MMAP_SIZE);\n\n    auto mmkv3 = MMKV::mmkvWithID(\"testExpectedCapacity3\");\n    mmkv3->clearAll();\n    assert(mmkv3->totalSize() == DEFAULT_MMAP_SIZE);\n    mmkv3->close();\n    // expand it\n    mmkv3 = MMKV::mmkvWithID(\"testExpectedCapacity3\", MMKV_SINGLE_PROCESS, nullptr, nullptr, 100 * DEFAULT_MMAP_SIZE + 100);\n    assert(mmkv3->totalSize() == DEFAULT_MMAP_SIZE * 101);\n\n    // if new size is smaller than file size, keep file its origin size\n    mmkv3->close();\n    mmkv3 = MMKV::mmkvWithID(\"testExpectedCapacity3\", MMKV_SINGLE_PROCESS, nullptr, nullptr, 0);\n    assert(mmkv3->totalSize() == DEFAULT_MMAP_SIZE * 101);\n\n    int len = 10000;\n    std::string value(len, '0');\n    value = \"🏊🏻®4️⃣🐅_\" + value;\n    cout << \"value length = \" << value.size() << endl;\n    std::string key = \"key\";\n    // if you know exactly the sizes of key and value, set expectedCapacity for performance improvement\n    size_t expectedSize = key.size() + value.size();\n    auto mmkv4 = MMKV::mmkvWithID(\"testExpectedCapacity4\", MMKV_SINGLE_PROCESS, nullptr, nullptr, expectedSize);\n    // 0 times expand\n    mmkv4->set(value, key);\n\n    int count = 10;\n    expectedSize = (key.size() + value.size()) * count;\n    auto mmkv5 = MMKV::mmkvWithID(\"testExpectedCapacity5\", MMKV_SINGLE_PROCESS, nullptr, nullptr, expectedSize);\n    for (int i = 0; i < count; i++) {\n        key[0] = static_cast<char>('a' + i);\n        // 0 times expand\n        mmkv5->set(value, key);\n    }\n}\n\nvoid testOnlyOneKey() {\n    string key = \"name\";\n    string s;\n    {\n        s = \"\";\n        auto mmkv = MMKV::mmkvWithID(\"testOneKey\");\n\n        mmkv->getString(key, s);\n        printf(\"testOneKey: value = %s\\n\", s.c_str());\n\n        mmkv->set(\"world\", key);\n        mmkv->getString(key, s);\n        printf(\"testOneKey: value = %s\\n\", s.c_str());\n\n        for (int i = 0; i < 10; i++) {\n            string value = \"world_\";\n            value += to_string(i);\n            mmkv->set(value, key);\n            mmkv->getString(key, s);\n            printf(\"testOneKey: value = %s\\n\", s.c_str());\n        }\n\n        // test file expanding\n        string bigValue(100000, '0');\n        mmkv->set(bigValue, key);\n        mmkv->getString(key, s);\n        assert(s == bigValue);\n\n        mmkv->set(\"OK\", key);\n        mmkv->getString(key, s);\n        printf(\"testOneKey: value = %s\\n\", s.c_str());\n\n        // close it and reopen it\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testOneKey\");\n        mmkv->getString(key, s);\n        printf(\"testOneKey: after reopen value = %s\\n\", s.c_str());\n        assert(s == \"OK\");\n    }\n\n    {\n        s = \"\";\n        string cryptKey = \"fastest\";\n        auto mmkv = MMKV::mmkvWithID(\"testOneKeyCrypt\", MMKV_SINGLE_PROCESS, &cryptKey);\n\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCrypt: value = %s\\n\", s.c_str());\n\n        mmkv->set(\"cryptworld\", key);\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCrypt: value = %s\\n\", s.c_str());\n\n        for (int i = 0; i < 10; i++) {\n            string value = \"cryptworld_\";\n            value += to_string(i);\n            mmkv->set(value, key);\n            mmkv->getString(key, s);\n            printf(\"testOneKeyCrypt: value = %s\\n\", s.c_str());\n        }\n\n        // close it and reopen it\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testOneKeyCrypt\", MMKV_SINGLE_PROCESS, &cryptKey);\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCrypt: after reopen value = %s\\n\", s.c_str());\n        assert(s == \"cryptworld_9\");\n\n        mmkv->set(\"cryptworld_good\", key);\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCrypt: value = %s\\n\", s.c_str());\n        assert(s == \"cryptworld_good\");\n    }\n\n    {\n        string cryptKey = \"expiretest\";\n        auto mmkv = MMKV::mmkvWithID(\"testOneKeyCryptExpire\", MMKV_SINGLE_PROCESS, &cryptKey);\n        mmkv->enableAutoKeyExpire(24 * 60 * 60);\n\n        s = \"\";\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCryptExpire: value = %s\\n\", s.c_str());\n\n        mmkv->set(\"expire\", key, 1000);\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCryptExpire: value = %s\\n\", s.c_str());\n\n        for (int i = 0; i < 10; i++) {\n            string value = \"expire_\";\n            value += to_string(i);\n            mmkv->set(value, key);\n            mmkv->getString(key, s);\n            printf(\"testOneKeyCryptExpire: value = %s\\n\", s.c_str());\n        }\n\n        // close it and reopen it\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testOneKeyCryptExpire\", MMKV_SINGLE_PROCESS, &cryptKey);\n        mmkv->getString(key, s);\n        printf(\"testOneKeyCryptExpire: after reopen value = %s\\n\", s.c_str());\n        assert(s == \"expire_9\");\n    }\n}\n\nvoid testOverride() {\n    string key1 = \"key1\";\n    string key2 = \"key2\";\n    string key3 = \"key3\";\n    string s;\n    {\n        s = \"\";\n        auto mmkv = MMKV::mmkvWithID(\"testOverride\");\n\n        mmkv->set(\"world1\", key1);\n        mmkv->getString(key1, s);\n        printf(\"testOverride: key1 = %s\\n\", s.c_str());\n\n        mmkv->set(\"world2\", key2);\n        mmkv->getString(key2, s);\n        printf(\"testOverride: key2 = %s\\n\", s.c_str());\n\n        mmkv->removeValueForKey(key1);\n        mmkv->removeValueForKey(key2);\n\n        printf(\"testOverride: actualSize = %lu\\n\", mmkv->actualSize());\n\n        mmkv->set(\"world3\", key3);\n        mmkv->getString(key3, s);\n        printf(\"testOverride: key3 = %s\\n\", s.c_str());\n        printf(\"testOverride: actualSize = %lu\\n\", mmkv->actualSize());\n\n        mmkv->removeValueForKey(key3);\n\n        // test file expanding\n        string bigValue(100000, '0');\n        mmkv->set(bigValue, key1);\n        mmkv->getString(key1, s);\n        assert(s == bigValue);\n\n        mmkv->removeValueForKey(key1);\n\n        mmkv->set(\"OK\", key2);\n        mmkv->getString(key2, s);\n        printf(\"testOverride: value = %s\\n\", s.c_str());\n\n        // close it and reopen it\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testOverride\");\n        mmkv->getString(key2, s);\n        printf(\"testOverride: after reopen key2 = %s\\n\", s.c_str());\n        assert(s == \"OK\");\n\n        mmkv->removeValueForKey(key2);\n        mmkv->trim();\n    }\n\n    {\n        s = \"\";\n        string cryptKey = \"fastest2\";\n        auto mmkv = MMKV::mmkvWithID(\"testOverrideCrypt\", MMKV_SINGLE_PROCESS, &cryptKey);\n        mmkv->enableAutoKeyExpire(24 * 60 * 60);\n\n        mmkv->set(\"world1\", key1);\n        mmkv->getString(key1, s);\n        printf(\"testOverrideCrypt: key1 = %s\\n\", s.c_str());\n\n        mmkv->set(\"world2\", key2);\n        mmkv->getString(key2, s);\n        printf(\"testOverrideCrypt: key2 = %s\\n\", s.c_str());\n\n        mmkv->removeValueForKey(key1);\n        mmkv->removeValueForKey(key2);\n\n        printf(\"testOverrideCrypt: actualSize = %lu\\n\", mmkv->actualSize());\n\n        mmkv->set(\"world3\", key3);\n        mmkv->getString(key3, s);\n        printf(\"testOverrideCrypt: key3 = %s\\n\", s.c_str());\n        printf(\"testOverrideCrypt: actualSize = %lu\\n\", mmkv->actualSize());\n\n        mmkv->removeValueForKey(key3);\n\n        // test file expanding\n        string bigValue(100000, '0');\n        mmkv->set(bigValue, key1);\n        mmkv->getString(key1, s);\n        assert(s == bigValue);\n\n        mmkv->removeValueForKey(key1);\n\n        mmkv->set(\"OK\", key2);\n        mmkv->getString(key2, s);\n        printf(\"testOverrideCrypt: value = %s\\n\", s.c_str());\n\n        // close it and reopen it\n        mmkv->close();\n        mmkv = MMKV::mmkvWithID(\"testOverrideCrypt\", MMKV_SINGLE_PROCESS, &cryptKey);\n        mmkv->getString(key2, s);\n        printf(\"testOverrideCrypt: after reopen key2 = %s\\n\", s.c_str());\n        assert(s == \"OK\");\n\n        mmkv->removeValueForKey(key2);\n        mmkv->trim();\n    }\n\n}\n\nuint64_t getTimeInMs() {\n    using namespace std::chrono;\n    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();\n}\n\nvoid testGetStringSpeed() {\n    string bigValue(100000, '0');\n    string key = \"key1\";\n    auto mmkv = MMKV::mmkvWithID(\"testGetStringSpeed\");\n    mmkv->set(bigValue, key);\n\n    string result;\n    uint64_t start1, end1, start2, end2;\n\n    start2 = getTimeInMs();\n    for (int i = 0; i < 2000000; i++) {\n        mmkv->getString(\"key1\", result, true);\n    }\n    end2 = getTimeInMs();\n\n    start1 = getTimeInMs();\n    for (int i = 0; i < 2000000; i++) {\n        mmkv->getString(\"key1\", result, false);\n    }\n    end1 = getTimeInMs();\n\n    printf(\"old_method = %\" PRId64 \", new_method = %\" PRId64 \"\\n\", end1 - start1, end2 - start2);\n\n    start1 = getTimeInMs();\n    for (int i = 0; i < 2000000; i++) {\n        mmkv->getString(\"key1\", result, false);\n    }\n    end1 = getTimeInMs();\n\n    start2 = getTimeInMs();\n    for (int i = 0; i < 2000000; i++) {\n        mmkv->getString(\"key1\", result, true);\n    }\n    end2 = getTimeInMs();\n    printf(\"old_method = %\" PRId64 \", new_method = %\" PRId64 \"\\n\", end1 - start1, end2 - start2);\n}\n\nvoid printVector(vector<string> &v) {\n    printf(\"testCompareBeforeSet: string<vector>: \");\n    if (v.empty()) {\n        return;\n    }\n    for (int i = 0; i < v.size() - 1; i++) {\n        printf(\"%s, \", v[i].c_str());\n    }\n    if (!v.empty()) {\n        printf(\"%s\\n\", v[v.size() - 1].c_str());\n    } else {\n        printf(\"\\n\");\n    }\n}\n\nvoid testCompareBeforeSet() {\n    auto mmkv = MMKV::mmkvWithID(\"testCompareBeforeSet\", MMKV_SINGLE_PROCESS);\n    mmkv->enableCompareBeforeSet();\n    mmkv->set(\"extraValue\", \"extraKey\");\n\n    size_t actualSize1 = -1;\n    size_t actualSize2 = -1;\n\n    string key;\n    {\n        key = \"bool\";\n        mmkv->set(true, key);\n        printf(\"testCompareBeforeSet: bool value = %d\\n\", mmkv->getBool(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: bool value = %d\\n\", mmkv->getBool(key));\n        mmkv->set(true, key);\n        actualSize2 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize2 = %lu\\n\", actualSize2);\n        assert(actualSize1 == actualSize2);\n        mmkv->set(false, key);\n        printf(\"testCompareBeforeSet: bool value = %d\\n\", mmkv->getBool(key));\n        assert(mmkv->getBool(key) == false);\n    }\n    {\n        key = \"int32_t\";\n        int32_t v = 12345;\n        mmkv->set(v, key);\n        printf(\"testCompareBeforeSet: int32 value = %d\\n\", mmkv->getInt32(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: int32 value = %d\\n\", mmkv->getInt32(key));\n        mmkv->set(v, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(v << 1, key);\n        printf(\"testCompareBeforeSet: int32 value = %d\\n\", mmkv->getInt32(key));\n        assert(mmkv->getInt32(key)  == v << 1);\n    }\n\n    {\n        key = \"uint32_t\";\n        uint32_t v32u = 6379;\n        mmkv->set(v32u, key);\n        printf(\"testCompareBeforeSet: uint32_t value = %d\\n\", mmkv->getUInt32(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: uint32_t value = %d\\n\", mmkv->getUInt32(key));\n        mmkv->set(v32u, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(v32u >> 1, key);\n        printf(\"testCompareBeforeSet: uint32_t value = %d\\n\", mmkv->getUInt32(key));\n        assert(mmkv->getUInt32(key) == v32u >> 1);\n    }\n    {\n        key = \"int64_t\";\n        int64_t v64 = 8080;\n        mmkv->set(v64, key);\n        printf(\"testCompareBeforeSet: int64_t value = %\" PRId64 \"\\n\", mmkv->getInt64(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: int64_t value = %\" PRId64 \"\\n\", mmkv->getInt64(key));\n        mmkv->set(v64, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(v64 >> 1, key);\n        printf(\"testCompareBeforeSet: int64_t value = %\" PRId64 \"\\n\", mmkv->getInt64(key));\n        assert(mmkv->getInt64(key) == v64 >> 1);\n    }\n\n    {\n        key = \"uint64_t\";\n        uint64_t v64u = 8848;\n        mmkv->set(v64u, key);\n        printf(\"testCompareBeforeSet: uint64_t value = %\" PRIu64 \"\\n\", mmkv->getUInt64(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: uint64_t value = %\" PRIu64 \"\\n\", mmkv->getUInt64(key));\n        mmkv->set(v64u, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(v64u >> 1, key);\n        printf(\"testCompareBeforeSet: uint64_t value = %\" PRIu64 \"\\n\", mmkv->getUInt64(key));\n        assert(mmkv->getUInt64(key) == v64u >> 1);\n    }\n\n    {\n        key = \"float\";\n        float flt = -987.012f;\n        mmkv->set(flt, key);\n        printf(\"testCompareBeforeSet: float value = %lf\\n\", mmkv->getFloat(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: float value = %lf\\n\", mmkv->getFloat(key));\n        mmkv->set(flt, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(flt * 12.56f, key);\n        printf(\"testCompareBeforeSet: float value = %lf\\n\", mmkv->getFloat(key));\n        assert(fabs(mmkv->getFloat(key) - flt * 12.56f) <= 1e-6);\n    }\n\n    {\n        key = \"double\";\n        double db = 8888987.012f;\n        mmkv->set(db, key);\n        printf(\"testCompareBeforeSet: double value = %lf\\n\", mmkv->getDouble(key));\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        printf(\"testCompareBeforeSet: double value = %lf\\n\", mmkv->getDouble(key));\n        mmkv->set(db, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(db * -12.56f, key);\n        printf(\"testCompareBeforeSet: double value = %lf\\n\", mmkv->getDouble(key));\n        assert(fabs(mmkv->getDouble(key) - db * -12.56f) <= 1e-6);\n    }\n\n    bool ret = false;\n    string resultString;\n    const char* raws = \"🏊🏻®4️⃣🐅_\";\n    const char* raws2 = \"12🏊🏻e®4️⃣🐅_34)(*()\";\n    {\n        key = \"char*\";\n        mmkv->set(raws, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: char* = %s\\n\", resultString.c_str());\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: char* = %s\\n\", resultString.c_str());\n        mmkv->set(raws, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(raws2, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: char* = %s\\n\", resultString.c_str());\n        assert(resultString == raws2);\n    }\n\n    string s1 = \"🏊🏻®hhh4️⃣🐅_yyy\";\n    string s2 = \"0aA🏊🏻®hhh4️⃣🐅_zzz\";\n\n    {\n        key = \"string\";\n        mmkv->set(s1, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: string = %s\\n\", resultString.c_str());\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: actualSize = %lu\\n\", actualSize1);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: string = %s\\n\", resultString.c_str());\n        mmkv->set(s1, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(s2, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: string = %s\\n\", resultString.c_str());\n        assert(resultString == s2);\n    }\n\n    {\n        s1 = \"buffer_🏊🏻®hhh4️⃣🐅_yyy\";\n        s2 = \"buffer_0aA🏊🏻®hhh4️⃣🐅_zzz\";\n        MMBuffer buffer1((void *) s1.c_str(), s1.size());\n        MMBuffer buffer2((void *) s2.c_str(), s2.size());\n        key = \"mmbuffer\";\n        mmkv->set(buffer1, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: MMBuffer = %s\\n\", resultString.c_str());\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: MMBuffer = %lu\\n\", actualSize1);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: MMBuffer = %s\\n\", resultString.c_str());\n        mmkv->set(buffer1, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(buffer2, key);\n        ret = mmkv->getString(key, resultString);\n        printf(\"testCompareBeforeSet: MMBuffer = %s\\n\", resultString.c_str());\n        assert(resultString == s2);\n    }\n\n    {\n        key = \"string<vector>\";\n        vector<string> v1 = {\"1\", s1, s2};\n        vector<string> v2 = {\"2\", s1, s2};\n        vector<string> vectorResult;\n        mmkv->set(v1, key);\n        ret = mmkv->getVector(key, vectorResult);\n        printVector(vectorResult);\n        actualSize1 = mmkv->actualSize();\n        printf(\"testCompareBeforeSet: string<vector> size = %lu\\n\", actualSize1);\n        ret = mmkv->getVector(key, vectorResult);\n        printVector(vectorResult);\n        mmkv->set(v1, key);\n        actualSize2 = mmkv->actualSize();\n        assert(actualSize1 == actualSize2);\n        mmkv->set(v2, key);\n        ret = mmkv->getVector(key, vectorResult);\n        printVector(vectorResult);\n        assert(vectorResult == v2);\n    }\n\n//    {\n//        string key = \"keyyy\";\n//        auto mmkv1 = MMKV::mmkvWithID(\"testCompareBeforeSet1\", MMKV_SINGLE_PROCESS, &key);\n//        mmkv1->enableCompareBeforeSet();\n//    }\n\n//    {\n//        auto mmkv2 = MMKV::mmkvWithID(\"testCompareBeforeSet2\", MMKV_SINGLE_PROCESS);\n//        mmkv2->enableAutoKeyExpire();\n//        mmkv2->enableCompareBeforeSet();\n//    }\n//\n//    {\n//        auto mmkv3 = MMKV::mmkvWithID(\"testCompareBeforeSet3\", MMKV_SINGLE_PROCESS);\n//        mmkv3->enableCompareBeforeSet();\n//        mmkv3->enableAutoKeyExpire();\n//    }\n\n    {\n        actualSize1 = -1;\n        auto mmkv1 = MMKV::mmkvWithID(\"differentType2\");\n        mmkv1->clearAll();\n        mmkv1->enableCompareBeforeSet();\n        mmkv1->set(\"xxx\", \"yyy\");\n        actualSize1 = mmkv1->actualSize();\n\n        mmkv1->set(\"value1\", \"key1\");\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        actualSize1 = actualSize2;\n        printf(\"%d\\n\", mmkv1->getBool(\"key1\", false));\n        printf(\"actualSize = %lu\\n\", mmkv1->actualSize());\n        assert( mmkv1->getBool(\"key1\", false));\n        mmkv1->set(true, \"key1\");\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        actualSize1 = actualSize2;\n\n        mmkv1->set(false, \"key1\");\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        actualSize1 = actualSize2;\n\n        printf(\"%d\\n\", mmkv1->getBool(\"key1\", false)); // print 1\n        printf(\"actualSize = %lu\\n\", mmkv1->actualSize());\n        mmkv1->set(\"value1\", \"key1\");\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        actualSize1 = actualSize2;\n\n        string v;\n        mmkv1->getString(\"key1\", v);\n        printf(\"%s\\n\", v.c_str()); // print value1\n\n        vector<string> v1 = {\"1\", s1, s2};\n        vector<string> v2 = {\"2\", s1, s2};\n        mmkv1->set(v1, \"key1\");\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        actualSize1 = actualSize2;\n        vector<string> vectorResult;\n        ret = mmkv1->getVector(\"key1\", vectorResult);\n        printVector(vectorResult);\n\n        // test exception\n        mmkv1->set(12345, \"key2\");\n        actualSize1 = mmkv1->actualSize();\n        // \"<MMKV_IO.cpp::setDataForKey> compareBeforeSet exception: InvalidProtocolBuffer truncatedMessage\" from log\n        string vv = \"abcdefg\";\n        mmkv1->set(vv, \"key2\");\n        actualSize2 = mmkv1->actualSize();\n        string vv2(vv.size(), 0);\n        mmkv1->getString(\"key2\", vv2, true);\n        assert(vv == vv2);\n        assert(actualSize2 > actualSize1);\n    }\n\n    {\n        key = \"key\";\n        auto mmkv1 = MMKV::mmkvWithID(\"compareEmpty\");\n        mmkv1->enableCompareBeforeSet();\n        mmkv1->set(\"\", key);\n        actualSize1 = mmkv1->actualSize();\n        mmkv1->set(\"\", key);\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize1 == actualSize2);\n\n        string vv = \"abcdefG\";\n        actualSize1 = mmkv1->actualSize();\n        mmkv1->set(vv, key);\n        actualSize2 = mmkv1->actualSize();\n        assert(actualSize2 > actualSize1);\n        string result;\n        mmkv1->getString(key, result, true);\n        assert(result == vv);\n    }\n}\n\nvoid testFtruncateFail() {\n    auto mmkv = MMKV::mmkvWithID(\"testFtruncateFail\");\n    signal(SIGXFSZ, SIG_IGN);\n    struct rlimit rlim_new,rlim;\n    string bigValue(1000, '0');\n    if (getrlimit(RLIMIT_FSIZE, &rlim) == 0) {\n        rlim_new.rlim_cur = rlim_new.rlim_max = 5000 * 1024;\n        int ret = setrlimit(RLIMIT_FSIZE, &rlim_new);\n        printf(\"setrlimit ret = %d\\n\", ret);\n\n        for (int i = 0; i < 1000000; i++) {\n            string key = \"qwerttt\" + to_string(i);\n//            fail to truncate [/tmp/mmkv/testFtruncateFail] to size 8388608, File too large\n            bool ret = mmkv->set(bigValue, key);\n            if (!ret) {\n                break;\n            }\n        }\n    }\n}\n\nvoid testRemoveStorage() {\n    string mmapID = \"test_remove\";\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_MULTI_PROCESS);\n        mmkv->set(true, \"bool\");\n    }\n    MMKV::removeStorage(mmapID);\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_MULTI_PROCESS);\n        if (mmkv->count() != 0) {\n            abort();\n        }\n    }\n\n    mmapID = \"test_remove/sg\";\n    string rootDir = \"/tmp/mmkv_1\";\n    auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n    mmkv->set(true, \"bool\");\n    MMKV::removeStorage(mmapID, &rootDir);\n    mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n    if (mmkv->count() != 0) {\n        abort();\n    }\n}\n\nvoid setReadOnly(const MMKVPath_t& path, bool readOnly) {\n    int mode = 0;\n    // alter the read-only attribute\n    if (readOnly) {\n        mode = 0444;\n    } else {\n        mode = 0666;\n    }\n    // Set the file attributes to the new value\n    if (chmod(path.c_str(), mode) != 0) {\n        // If the function fails, print an error message\n        auto err = errno;\n        printf(\"Failed to set file attributes. Error code: %d, %s\\n\", err, strerror(err));\n    }\n}\n\nvoid testReadOnly() {\n    string mmapID = \"testReadOnly\";\n    string aesKey = \"ReadOnly+Key\";\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n        functionalTest(mmkv, false);\n        mmkv->close();\n    }\n\n    auto path = MMKV::getRootDir() + MMKV_PATH_SLASH + mmapID;\n    setReadOnly(path, true);\n    auto crcPath = path + \".crc\";\n    setReadOnly(crcPath, true);\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, (MMKV_SINGLE_PROCESS | MMKV_READ_ONLY), &aesKey);\n        functionalTest(mmkv, true);\n\n        // also check if it tolerate update operations without crash\n        functionalTest(mmkv, false);\n\n        mmkv->close();\n    }\n    setReadOnly(path, false);\n    setReadOnly(crcPath, false);\n}\n\nvoid testNameSpace() {\n    string root = \"/tmp/mmkv_namespace\";\n    auto ns = MMKV::nameSpace(root);\n    auto kv = ns.mmkvWithID(\"test_namespace\");\n    functionalTest(kv, false);\n}\n\nvoid testImport() {\n    string mmapID = \"test_import_src\";\n    auto src = MMKV::mmkvWithID(mmapID);\n    src->set(true, \"bool\");\n    src->set(std::numeric_limits<int32_t>::min(), \"int\");\n    src->set(std::numeric_limits<uint64_t>::max(), \"long\");\n    src->set(\"test import\", \"string\");\n\n    auto dst = MMKV::mmkvWithID(\"test_import_dst\");\n    dst->clearAll();\n    dst->enableAutoKeyExpire(1);\n    dst->set(true, \"bool\");\n    dst->set(-1, \"int\");\n    dst->set(0, \"long\");\n    dst->set(mmapID, \"string\");\n\n    auto count = dst->importFrom(src);\n    assert(count == 4 && dst->count() == 4);\n    assert(dst->getBool(\"bool\"));\n    assert(dst->getInt32(\"int\") == std::numeric_limits<int32_t>::min());\n    assert(dst->getUInt64(\"long\") == std::numeric_limits<uint64_t>::max());\n    string result;\n    dst->getString(\"string\", result);\n    assert(result == \"test import\");\n    sleep(2);\n    assert(dst->count(true) == 0);\n}\n\nMMKV* testMMKV(const string& mmapID, const string* cryptKey, bool aes256, bool decodeOnly, const string* rootPath) {\n    MMKV* kv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, cryptKey, rootPath, 0, aes256);\n    functionalTest(kv, decodeOnly);\n    return kv;\n}\n\nvoid testReKey() {\n    string mmapID = \"test/AES_reKey1\";\n    MMKV* kv = testMMKV(mmapID, nullptr, false, false, nullptr);\n\n    string cryptKey = \"Key_seq_1\";\n    kv->reKey(cryptKey);\n    kv->clearMemoryCache();\n    testMMKV(mmapID, &cryptKey, false, true, nullptr);\n\n    string cryptKey2 = \"Key_Seq_Very_Looooooooong\";\n    kv->reKey(cryptKey2, true);\n    kv->clearMemoryCache();\n    testMMKV(mmapID, &cryptKey2, true, true, nullptr);\n\n    kv->reKey(string());\n    kv->clearMemoryCache();\n    testMMKV(mmapID, nullptr, false, true, nullptr);\n}\n\nvoid MyLogHandler(MMKVLogLevel level, const char *file, int line, const char *function, const string &message) {\n\n    auto desc = [level] {\n        switch (level) {\n            case MMKVLogDebug:\n                return \"D\";\n            case MMKVLogInfo:\n                return \"I\";\n            case MMKVLogWarning:\n                return \"W\";\n            case MMKVLogError:\n                return \"E\";\n            default:\n                return \"N\";\n        }\n    }();\n    printf(\"redirecting-[%s] <%s:%d::%s> %s\\n\", desc, file, line, function, message.c_str());\n}\n\nvoid testReadonlyCrash() {\n    std::string *key = nullptr;\n    const std::string g_ro_path = \"/tmp/mmkv_readonly\";\n    MMKV *self = MMKV::mmkvWithID(\"UnitTestRo\", MMKVMode::MMKV_MULTI_PROCESS | MMKVMode::MMKV_READ_ONLY, key, &g_ro_path);\n//    MMKV *self = MMKV::mmkvWithID(\"UnitTestRo\", MMKVMode::MMKV_MULTI_PROCESS, key, &g_ro_path);\n    std::string tmp;\n//    self->set(\"\", \"test_ro_string\");\n    self->getString(\"test_ro_string\", tmp);\n    printf(\"value: %s\\n\", tmp.c_str());\n}\n\nint main() {\n    locale::global(locale(\"\"));\n    wcout.imbue(locale(\"\"));\n    char c;\n    srand((uint64_t) &c);\n\n    // test NameSpace before MMKV.initializeMMKV()\n    testNameSpace();\n\n    string rootDir = \"/tmp/mmkv\";\n    MMKV::initializeMMKV(rootDir, MMKVLogInfo, MyLogHandler);\n    // MMKV::setLogLevel(MMKVLogNone);\n    // MMKV::registerLogHandler(MyLogHandler);\n\n    //auto mmkv = MMKV::defaultMMKV();\n    string aesKey = \"cryptKey\";\n    auto mmkv = MMKV::mmkvWithID(\"test/Encrypt\", MMKV_SINGLE_PROCESS, &aesKey);\n    functionalTest(mmkv, false);\n\n    for (size_t index = 0; index < keyCount; index++) {\n        arrIntKeys.push_back(\"int-\" + to_string(index));\n        arrStringKeys.push_back(\"string-\" + to_string(index));\n    }\n\n    //fastRemoveCornetSizeTest();\n    // cornetSizeTest();\n    //testClearEmptyMMKV();\n    brutleTest();\n    threadTest();\n    processTest();\n    testInterProcessLock();\n    testExpectedCapacity();\n    testOnlyOneKey();\n    testOverride();\n    testClearAllKeepSpace();\n//    testGetStringSpeed();\n    testCompareBeforeSet();\n    testBackup();\n    testRestore();\n    testAutoExpiration();\n//    testFtruncateFail();\n    testRemoveStorage();\n    testReadOnly();\n    testReadonlyCrash();\n    testImport();\n    itemSizeHolderTest();\n    testReKey();\n}\n"
  },
  {
    "path": "POSIX/demo/process.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n#include <chrono>\n#include <cstdio>\n#include <iostream>\n#include <string>\n#include <unistd.h>\n\nusing namespace std;\nusing namespace mmkv;\n\nconstexpr auto keyCount = 1000;\nstatic const string MMKV_ID = \"process_test\";\nvector<string> arrIntKeys;\nvector<string> arrStringKeys;\n\nvoid brutleTest(int processID) {\n    using hclock = chrono::high_resolution_clock;\n    auto start = hclock::now();\n\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID, MMKV_MULTI_PROCESS);\n    // mmkv->lock();\n    // sleep(10);\n    for (int32_t i = 0; i < keyCount; i++) {\n        mmkv->set(i, arrIntKeys[i]);\n        mmkv->set(\"str-\" + to_string(i), arrStringKeys[i]);\n        mmkv->getInt32(arrIntKeys[i]);\n        string result;\n        mmkv->getString(arrStringKeys[i], result);\n    }\n\n    auto finish = hclock::now();\n    auto used = chrono::duration_cast<chrono::milliseconds>(finish - start).count();\n    mmkv->lock();\n    cout << endl << processID << \": \" << used << \" ms\\n\";\n    mmkv->unlock();\n}\n\nint main() {\n    locale::global(locale(\"\"));\n    wcout.imbue(locale(\"\"));\n    char c;\n    srand((uint64_t) &c);\n\n    string rootDir = \"/tmp/mmkv\";\n    MMKV::initializeMMKV(rootDir);\n\n    auto processID = getpid();\n    cout << processID << \": started\\n\";\n\n    for (size_t index = 0; index < keyCount; index++) {\n        arrIntKeys.push_back(\"int-\" + to_string(index));\n        arrStringKeys.push_back(\"string-\" + to_string(index));\n    }\n    brutleTest(processID);\n\n    cout << processID << \": ended\\n\";\n    return 0;\n}\n"
  },
  {
    "path": "POSIX/golang/.gitignore",
    "content": "Core/\nMakefile\nlibmmkv.*\n*.cbp\nTesting/\ntencent.com/\ninstall_manifest.txt\n"
  },
  {
    "path": "POSIX/golang/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2020 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets the minimum version of CMake required to build the native library.\ncmake_minimum_required(VERSION 3.10.0)\n\nproject(mmkv)\n\nIF(APPLE)\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\nadd_subdirectory(../../Core Core)\n\nfind_library(zlib\n        z\n        )\n\nIF (NOT zlib)\n    message(STATUS \"Zlib library not found. Using internal zlib.\")\n    add_subdirectory(../../Core/crc32 zlib)\nELSE()\n    message(STATUS \"Zlib library found. ${zlib}\")\nENDIF()\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\nadd_library( # Sets the name of the library.\n             mmkv\n\n             # Sets the library as a shared library.\n             STATIC\n\n             # Provides a relative path to your source file(s).\n             golang-bridge.h\n             golang-bridge.cpp\n        )\n\ntarget_include_directories(mmkv PUBLIC\n        ${CMAKE_CURRENT_SOURCE_DIR})\n\nset_target_properties(mmkv PROPERTIES\n            CXX_STANDARD 17\n            CXX_EXTENSIONS OFF\n            )\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\ntarget_link_libraries( mmkv\n                       core\n                       pthread\n        )\n\n# Install source file & library to tencent.com/mmkv dir\n\ninstall(FILES mmkv.go callback.go go.mod mmkv_test.go golang-bridge.h golang-bridge.cpp\n  DESTINATION \"tencent.com/mmkv\")\n\ninstall(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmmkv.a ${CMAKE_CURRENT_BINARY_DIR}/Core/libcore.a\n  DESTINATION \"tencent.com/mmkv/lib\")\n\nIF (NOT zlib)\ninstall(FILES ${CMAKE_CURRENT_BINARY_DIR}/zlib/libz.a\n  DESTINATION \"tencent.com/mmkv/lib\")\nENDIF()"
  },
  {
    "path": "POSIX/golang/callback.go",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// MMKV is a cross-platform key-value storage framework developed by WeChat.\npackage mmkv\n\n/*\n#include \"golang-bridge.h\"\n#include <stdlib.h>\n*/\nimport \"C\"\n\nconst (\n\tOnErrorDiscard = iota // When there's an error, MMKV will discard everything by default.\n\tOnErrorRecover        // When there's an error, MMKV will try to recover as much data as possible.\n)\n\nconst (\n\tMMKVCRCCheckFail = iota\n\tMMKVFileLength\n)\n\n// Handler is the unified callback interface for MMKV.\ntype Handler interface {\n\t// WantLogRedirect returns true to enable log redirecting.\n\tWantLogRedirect() bool\n\n\t// MMKVLog is called for each log message when log redirecting is enabled.\n\tMMKVLog(level int, file string, line int, function string, message string)\n\n\t// OnMMKVCRCCheckFail is called when a CRC check fails.\n\t// Return OnErrorDiscard (default) or OnErrorRecover.\n\tOnMMKVCRCCheckFail(mmapID string) int\n\n\t// OnMMKVFileLengthError is called when a file length mismatch is detected.\n\t// Return OnErrorDiscard (default) or OnErrorRecover.\n\tOnMMKVFileLengthError(mmapID string) int\n\n\t// WantContentChangeNotification returns true to enable inter-process content change notifications.\n\tWantContentChangeNotification() bool\n\n\t// OnContentChangedByOuterProcess is called when content is changed by another process.\n\tOnContentChangedByOuterProcess(mmapID string)\n\n\t// OnMMKVContentLoadSuccessfully is called when an MMKV file is loaded successfully.\n\tOnMMKVContentLoadSuccessfully(mmapID string)\n}\n\n// DefaultHandler provides default implementations for all Handler methods.\n// Embed this in your handler struct to only override the methods you need.\ntype DefaultHandler struct{}\n\nfunc (d DefaultHandler) WantLogRedirect() bool                     { return false }\nfunc (d DefaultHandler) MMKVLog(level int, file string, line int, function string, message string) {}\nfunc (d DefaultHandler) OnMMKVCRCCheckFail(mmapID string) int      { return OnErrorDiscard }\nfunc (d DefaultHandler) OnMMKVFileLengthError(mmapID string) int   { return OnErrorDiscard }\nfunc (d DefaultHandler) WantContentChangeNotification() bool       { return false }\nfunc (d DefaultHandler) OnContentChangedByOuterProcess(mmapID string) {}\nfunc (d DefaultHandler) OnMMKVContentLoadSuccessfully(mmapID string) {}\n\nvar gHandler Handler\n\n// RegisterHandler registers a unified callback handler for MMKV.\nfunc RegisterHandler(handler Handler) {\n\tgHandler = handler\n\n\twantLog := handler.WantLogRedirect()\n\twantContent := handler.WantContentChangeNotification()\n\tC.setWantsHandler(C.bool(true), C.bool(wantLog), C.bool(wantContent))\n}\n\n// UnRegisterHandler unregisters the callback handler for MMKV.\nfunc UnRegisterHandler() {\n\tgHandler = nil\n\n\tC.setWantsHandler(C.bool(false), C.bool(false), C.bool(false))\n}\n\n//export myLogHandler\nfunc myLogHandler(level int, file string, line int, function string, message string) {\n\tif gHandler != nil {\n\t\tgHandler.MMKVLog(level, file, line, function, message)\n\t}\n}\n\n//export myErrorHandler\nfunc myErrorHandler(mmapID string, error int) int {\n\tif gHandler != nil {\n\t\tif error == MMKVCRCCheckFail {\n\t\t\treturn gHandler.OnMMKVCRCCheckFail(mmapID)\n\t\t} else if error == MMKVFileLength {\n\t\t\treturn gHandler.OnMMKVFileLengthError(mmapID)\n\t\t}\n\t}\n\treturn OnErrorDiscard\n}\n\n//export myContentChangeHandler\nfunc myContentChangeHandler(mmapID string) {\n\tif gHandler != nil {\n\t\tgHandler.OnContentChangedByOuterProcess(mmapID)\n\t}\n}\n\n//export myContentLoadedHandler\nfunc myContentLoadedHandler(mmapID string) {\n\tif gHandler != nil {\n\t\tgHandler.OnMMKVContentLoadSuccessfully(mmapID)\n\t}\n}\n"
  },
  {
    "path": "POSIX/golang/go.mod",
    "content": "module tencent.com/mmkv\n\ngo 1.15\n"
  },
  {
    "path": "POSIX/golang/golang-bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CGO\n\n#    include <MMKV/MMKV.h>\n#    include \"golang-bridge.h\"\n#    include <stdint.h>\n#    include <string>\n#    include <cstring>\n\nusing namespace mmkv;\nusing namespace std;\n\n#    ifdef MMKV_EXPORT\n#        undef MMKV_EXPORT\n#    endif\n#    define MMKV_EXPORT extern \"C\" __attribute__((visibility(\"default\"))) __attribute__((used))\n\nextern \"C\" void myLogHandler(int64_t level, GoStringWrap file, int64_t line, GoStringWrap function, GoStringWrap message);\nextern \"C\" int64_t myErrorHandler(GoStringWrap mmapID, int64_t error);\nextern \"C\" void myContentChangeHandler(GoStringWrap mmapID);\nextern \"C\" void myContentLoadedHandler(GoStringWrap mmapID);\n\nclass GoMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    bool m_wantLog = false;\n    bool m_wantContentChange = false;\n\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) override {\n        if (!m_wantLog) {\n            return;\n        }\n        GoStringWrap oFile { file, static_cast<int64_t>(strlen(file)) };\n        GoStringWrap oFunction { function, static_cast<int64_t>(strlen(function)) };\n        GoStringWrap oMessage { message.data(), static_cast<int64_t>(message.length()) };\n        myLogHandler(level, oFile, line, oFunction, oMessage);\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };\n        return static_cast<MMKVRecoverStrategic>(myErrorHandler(oID, static_cast<int64_t>(MMKVCRCCheckFail)));\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };\n        return static_cast<MMKVRecoverStrategic>(myErrorHandler(oID, static_cast<int64_t>(MMKVFileLength)));\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (!m_wantContentChange) {\n            return;\n        }\n        GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };\n        myContentChangeHandler(oID);\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        GoStringWrap oID { mmapID.data(), static_cast<int64_t>(mmapID.length()) };\n        myContentLoadedHandler(oID);\n    }\n};\n\nstatic GoMMKVHandler g_goHandler;\n\nMMKV_EXPORT void mmkvInitialize(GoStringWrap rootDir, int32_t logLevel, bool redirect) {\n    if (!rootDir.ptr) {\n        return;\n    }\n    g_goHandler.m_wantLog = redirect;\n    mmkv::MMKVHandler *handler = redirect ? &g_goHandler : nullptr;\n    MMKV::initializeMMKV(string(rootDir.ptr, rootDir.length), (MMKVLogLevel) logLevel, handler);\n}\n\nMMKV_EXPORT void onExit() {\n    MMKV::onExit();\n}\n\nMMKV_EXPORT void *getMMKVWithID(GoStringWrap mmapID, MMKVCreationConfig_t cfg) {\n    MMKV *kv = nullptr;\n    if (!mmapID.ptr) {\n        return kv;\n    }\n    auto str = string(mmapID.ptr, mmapID.length);\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) cfg.mode;\n    config.aes256 = cfg.aes256;\n    config.expectedCapacity = cfg.expectedCapacity;\n    if (cfg.enableKeyExpire >= 0) {\n        config.enableKeyExpire = (cfg.enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = cfg.expiredInSeconds;\n    config.enableCompareBeforeSet = cfg.enableCompareBeforeSet;\n    if (cfg.recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(cfg.recover);\n    }\n    config.itemSizeLimit = cfg.itemSizeLimit;\n\n    bool done = false;\n    auto &cryptKey = cfg.cryptKey;\n    auto &rootPath = cfg.rootPath;\n    if (cryptKey.ptr) {\n        auto crypt = string(cryptKey.ptr, cryptKey.length);\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            if (rootPath.ptr) {\n                auto path = string(rootPath.ptr, rootPath.length);\n                config.rootPath = &path;\n                kv = MMKV::mmkvWithID(str, config);\n            } else {\n                kv = MMKV::mmkvWithID(str, config);\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath.ptr) {\n            auto path = string(rootPath.ptr, rootPath.length);\n            config.rootPath = &path;\n            kv = MMKV::mmkvWithID(str, config);\n        } else {\n            kv = MMKV::mmkvWithID(str, config);\n        }\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT void *getDefaultMMKV(MMKVCreationConfig_t cfg) {\n    MMKV *kv = nullptr;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) cfg.mode;\n    config.aes256 = cfg.aes256;\n    config.expectedCapacity = cfg.expectedCapacity;\n    if (cfg.enableKeyExpire >= 0) {\n        config.enableKeyExpire = (cfg.enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = cfg.expiredInSeconds;\n    config.enableCompareBeforeSet = cfg.enableCompareBeforeSet;\n    if (cfg.recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(cfg.recover);\n    }\n    config.itemSizeLimit = cfg.itemSizeLimit;\n\n    auto &cryptKey = cfg.cryptKey;\n    if (cryptKey.ptr) {\n        auto crypt = string(cryptKey.ptr, cryptKey.length);\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            kv = MMKV::defaultMMKV(config);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::defaultMMKV(config);\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT const char *mmapID(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->mmapID().c_str();\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool encodeBool(void *handle, GoStringWrap oKey, bool value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((bool) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBool_v2(void *handle, GoStringWrap oKey, bool value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((bool) value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool decodeBool(void *handle, GoStringWrap oKey, bool defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getBool(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt32(void *handle, GoStringWrap oKey, int32_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((int32_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt32_v2(void *handle, GoStringWrap oKey, int32_t value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((int32_t) value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int32_t decodeInt32(void *handle, GoStringWrap oKey, int32_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeUInt32(void *handle, GoStringWrap oKey, uint32_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set(value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeUInt32_v2(void *handle, GoStringWrap oKey, uint32_t value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set(value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint32_t decodeUInt32(void *handle, GoStringWrap oKey, uint32_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getUInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt64(void *handle, GoStringWrap oKey, int64_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((int64_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt64_v2(void *handle, GoStringWrap oKey, int64_t value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((int64_t) value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int64_t decodeInt64(void *handle, GoStringWrap oKey, int64_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeUInt64(void *handle, GoStringWrap oKey, uint64_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set(value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeUInt64_v2(void *handle, GoStringWrap oKey, uint64_t value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set(value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t decodeUInt64(void *handle, GoStringWrap oKey, uint64_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getUInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeFloat(void *handle, GoStringWrap oKey, float value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((float) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeFloat_v2(void *handle, GoStringWrap oKey, float value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((float) value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT float decodeFloat(void *handle, GoStringWrap oKey, float defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getFloat(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeDouble(void *handle, GoStringWrap oKey, double value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((double) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeDouble_v2(void *handle, GoStringWrap oKey, double value, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->set((double) value, key, expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT double decodeDouble(void *handle, GoStringWrap oKey, double defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->getDouble(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeBytes(void *handle, GoStringWrap oKey, GoStringWrap oValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        if (oValue.ptr) {\n            auto value = MMBuffer((void *) oValue.ptr, oValue.length, MMBufferNoCopy);\n            return kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBytes_v2(void *handle, GoStringWrap oKey, GoStringWrap oValue, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        if (oValue.ptr) {\n            auto value = MMBuffer((void *) oValue.ptr, oValue.length, MMBufferNoCopy);\n            return kv->set(value, key, expireDuration);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *decodeBytes(void *handle, GoStringWrap oKey, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        auto value = kv->getBytes(key);\n        if (value.length() > 0) {\n            if (value.isStoredOnStack()) {\n                auto result = malloc(value.length());\n                if (result) {\n                    memcpy(result, value.getPtr(), value.length());\n                    *lengthPtr = value.length();\n                }\n                return result;\n            } else {\n                void *result = value.getPtr();\n                *lengthPtr = value.length();\n                value.detach();\n                return result;\n            }\n        }\n    }\n    return nullptr;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT bool reKey(void *handle, GoStringWrap oKey, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey.ptr && oKey.length > 0) {\n            string key(oKey.ptr, oKey.length);\n            return kv->reKey(key, aes256);\n        } else {\n            return kv->reKey(string(), aes256);\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *cryptKey(void *handle, uint32_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && lengthPtr) {\n        auto cryptKey = kv->cryptKey();\n        if (cryptKey.length() > 0) {\n            auto ptr = malloc(cryptKey.length());\n            if (ptr) {\n                memcpy(ptr, cryptKey.data(), cryptKey.length());\n                *lengthPtr = cryptKey.length();\n                return ptr;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT void checkReSetCryptKey(void *handle, GoStringWrap oKey, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey.ptr && oKey.length > 0) {\n            string key(oKey.ptr, oKey.length);\n            kv->checkReSetCryptKey(&key, aes256);\n        } else {\n            kv->checkReSetCryptKey(nullptr, aes256);\n        }\n    }\n}\n\n#    else // fix cgo cannot find function reference.\n\nMMKV_EXPORT bool reKey(void *handle, GoStringWrap oKey, bool aes256) {\n    return false;\n}\n\nMMKV_EXPORT void *cryptKey(void *handle, uint32_t *lengthPtr) {\n    return nullptr;\n}\n\nMMKV_EXPORT void checkReSetCryptKey(void *handle, GoStringWrap oKey, bool aes256) {}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT GoStringWrap *allKeys(void *handle, uint64_t *lengthPtr, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        auto keys = kv->allKeys(filterExpire);\n        if (!keys.empty()) {\n            auto keyArray = (GoStringWrap *) calloc(keys.size(), sizeof(GoStringWrap));\n            if (!keyArray) {\n                return nullptr;\n            }\n\n            for (size_t index = 0; index < keys.size(); index++) {\n                auto &key = keys[index];\n                auto &stringWrap = keyArray[index];\n                stringWrap.length = static_cast<uint32_t>(key.length());\n                stringWrap.ptr = (char *) malloc(key.length());\n                if (stringWrap.ptr) {\n                    memcpy((void *) stringWrap.ptr, key.data(), key.length());\n                }\n            }\n            *lengthPtr = keys.size();\n            return keyArray;\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool containsKey(void *handle, GoStringWrap oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        return kv->containsKey(key);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t count(void *handle, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->count(filterExpire);\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t totalSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->totalSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t actualSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->actualSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT void removeValueForKey(void *handle, GoStringWrap oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey.ptr) {\n        auto key = string(oKey.ptr, oKey.length);\n        kv->removeValueForKey(key);\n    }\n}\n\nMMKV_EXPORT void removeValuesForKeys(void *handle, GoStringWrap *keyArray, uint64_t count) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && keyArray && count > 0) {\n        vector<string> arrKeys;\n        arrKeys.reserve(count);\n        for (uint64_t index = 0; index < count; index++) {\n            auto &stringWrap = keyArray[index];\n            if (stringWrap.ptr && stringWrap.length > 0) {\n                arrKeys.emplace_back(stringWrap.ptr, stringWrap.length);\n            }\n        }\n        if (!arrKeys.empty()) {\n            kv->removeValuesForKeys(arrKeys);\n        }\n    }\n}\n\nMMKV_EXPORT void clearAll(void *handle, bool keepSpace) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearAll(keepSpace);\n    }\n}\n\nMMKV_EXPORT void mmkvSync(void *handle, bool sync) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->sync((SyncFlag) sync);\n    }\n}\n\nMMKV_EXPORT void clearMemoryCache(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n}\n\nMMKV_EXPORT int32_t pageSize() {\n    return static_cast<int32_t>(DEFAULT_MMAP_SIZE);\n}\n\nMMKV_EXPORT const char *version() {\n    return MMKV_VERSION;\n}\n\nMMKV_EXPORT const char *getRootDir() {\n    return MMKV::getRootDir().c_str();\n}\n\nMMKV_EXPORT void trim(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->trim();\n    }\n}\n\nMMKV_EXPORT void mmkvClose(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->close();\n    }\n}\n\nMMKV_EXPORT bool backupOneToDirectory(GoStringWrap_t mmapID, GoStringWrap_t dstDir, GoStringWrap_t srcDir) {\n    if (!mmapID.ptr || !dstDir.ptr) {\n        return false;\n    }\n    auto id = string(mmapID.ptr, mmapID.length);\n    auto dst = string(dstDir.ptr, dstDir.length);\n    if (srcDir.ptr) {\n        auto src = string(srcDir.ptr, srcDir.length);\n        return MMKV::backupOneToDirectory(id, dst, &src);\n    }\n    return MMKV::backupOneToDirectory(id, dst, nullptr);\n}\n\nMMKV_EXPORT bool restoreOneFromDirectory(GoStringWrap_t mmapID, GoStringWrap_t srcDir, GoStringWrap_t dstDir) {\n    if (!mmapID.ptr || !srcDir.ptr) {\n        return false;\n    }\n    auto id = string(mmapID.ptr, mmapID.length);\n    auto src = string(srcDir.ptr, srcDir.length);\n    if (dstDir.ptr) {\n        auto dst = string(dstDir.ptr, dstDir.length);\n        return MMKV::restoreOneFromDirectory(id, src, &dst);\n    }\n    return MMKV::restoreOneFromDirectory(id, src, nullptr);\n}\n\nMMKV_EXPORT uint64_t backupAllToDirectory(GoStringWrap_t dstDir, GoStringWrap_t srcDir) {\n    if (!dstDir.ptr) {\n        return 0;\n    }\n\n    auto dst = string(dstDir.ptr, dstDir.length);\n    if (srcDir.ptr) {\n        auto src = string(srcDir.ptr, srcDir.length);\n        return MMKV::backupAllToDirectory(dst, &src);\n    }\n    return MMKV::backupAllToDirectory(dst, nullptr);\n}\n\nMMKV_EXPORT uint64_t restoreAllFromDirectory(GoStringWrap_t srcDir, GoStringWrap_t dstDir) {\n    if (!srcDir.ptr) {\n        return 0;\n    }\n\n    auto src = string(srcDir.ptr, srcDir.length);\n    if (dstDir.ptr) {\n        auto dst = string(dstDir.ptr, dstDir.length);\n        return MMKV::restoreAllFromDirectory(src, &dst);\n    }\n    return MMKV::restoreAllFromDirectory(src, nullptr);\n}\n\nMMKV_EXPORT bool enableAutoExpire(void *handle, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableAutoKeyExpire(expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableAutoExpire(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableAutoKeyExpire();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool enableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableCompareBeforeSet();\n    }\n    return false;\n}\n\nvoid setWantsHandler(bool hasHandler, bool wantLog, bool wantContent) {\n    if (hasHandler) {\n        g_goHandler.m_wantLog = wantLog;\n        g_goHandler.m_wantContentChange = wantContent;\n        MMKV::registerHandler(&g_goHandler);\n    } else {\n        g_goHandler.m_wantLog = false;\n        g_goHandler.m_wantContentChange = false;\n        MMKV::unRegisterHandler();\n    }\n}\n\nMMKV_EXPORT bool removeStorage(GoStringWrap_t mmapID, GoStringWrap_t rootPath) {\n    if (!mmapID.ptr) {\n        return false;\n    }\n    auto id = string(mmapID.ptr, mmapID.length);\n    if (rootPath.ptr) {\n        auto path = string(rootPath.ptr, rootPath.length);\n        return MMKV::removeStorage(id, &path);\n    }\n    return MMKV::removeStorage(id, nullptr);\n}\n\nMMKV_EXPORT bool isMultiProcess(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isMultiProcess();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isReadOnly(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isReadOnly();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool getNameSpace(GoStringWrap_t rootPath) {\n    if (rootPath.ptr && rootPath.length > 0) {\n        auto root = string(rootPath.ptr, rootPath.length);\n        MMKV::nameSpace(root);\n        return true;\n    }\n    return false;\n}\n\nMMKV_EXPORT bool checkExist(GoStringWrap_t mmapID, GoStringWrap_t rootPath) {\n   if (!mmapID.ptr) {\n        return false;\n    }\n    auto id = string(mmapID.ptr, mmapID.length);\n    if (rootPath.ptr) {\n        auto path = string(rootPath.ptr, rootPath.length);\n        return MMKV::checkExist(id, &path);\n    }\n    return MMKV::checkExist(id, nullptr);\n}\n\nMMKV_EXPORT uint64_t importFrom(void *handle, void *srcHandle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    MMKV *kvSrc = static_cast<MMKV *>(srcHandle);\n    if (kv && kvSrc) {\n        return kv->importFrom(kvSrc);\n    }\n    return 0;\n}\n\n#endif // CGO\n"
  },
  {
    "path": "POSIX/golang/golang-bridge.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifdef __cplusplus\n#    include <cstdint>\nextern \"C\" {\n#else\n#    include <stdbool.h>\n#    include <stdint.h>\n#endif\n\nstruct GoStringWrap {\n    const char *ptr;\n    int64_t length;\n};\ntypedef struct GoStringWrap GoStringWrap_t;\n\nstruct GoSliceWrap {\n    void *array;\n    int64_t length;\n    int64_t capacity;\n};\ntypedef struct GoSliceWrap GoSliceWrap_t;\n\ntypedef struct {\n    int32_t mode;\n\n    GoStringWrap_t cryptKey;\n    bool aes256;\n\n    GoStringWrap_t rootPath;\n\n    uint64_t expectedCapacity;\n\n    int32_t enableKeyExpire;\n    uint32_t expiredInSeconds;\n\n    bool enableCompareBeforeSet;\n\n    int32_t recover;\n\n    uint32_t itemSizeLimit;\n} MMKVCreationConfig_t;\n\nvoid mmkvInitialize(GoStringWrap_t rootDir, int32_t logLevel, bool redirect);\nvoid onExit();\n\nvoid *getMMKVWithID(GoStringWrap_t mmapID, MMKVCreationConfig_t config);\nvoid *getDefaultMMKV(MMKVCreationConfig_t config);\nconst char *mmapID(void *handle);\n\nbool encodeBool(void *handle, GoStringWrap_t oKey, bool value);\nbool encodeBool_v2(void *handle, GoStringWrap_t oKey, bool value, uint32_t expireDuration);\nbool decodeBool(void *handle, GoStringWrap_t oKey, bool defaultValue);\n\nbool encodeInt32(void *handle, GoStringWrap_t oKey, int32_t value);\nbool encodeInt32_v2(void *handle, GoStringWrap_t oKey, int32_t value, uint32_t expireDuration);\nint32_t decodeInt32(void *handle, GoStringWrap_t oKey, int32_t defaultValue);\n\nbool encodeUInt32(void *handle, GoStringWrap_t oKey, uint32_t value);\nbool encodeUInt32_v2(void *handle, GoStringWrap_t oKey, uint32_t value, uint32_t expireDuration);\nuint32_t decodeUInt32(void *handle, GoStringWrap_t oKey, uint32_t defaultValue);\n\nbool encodeInt64(void *handle, GoStringWrap_t oKey, int64_t value);\nbool encodeInt64_v2(void *handle, GoStringWrap_t oKey, int64_t value, uint32_t expireDuration);\nint64_t decodeInt64(void *handle, GoStringWrap_t oKey, int64_t defaultValue);\n\nbool encodeUInt64(void *handle, GoStringWrap_t oKey, uint64_t value);\nbool encodeUInt64_v2(void *handle, GoStringWrap_t oKey, uint64_t value, uint32_t expireDuration);\nuint64_t decodeUInt64(void *handle, GoStringWrap_t oKey, uint64_t defaultValue);\n\nbool encodeFloat(void *handle, GoStringWrap_t oKey, float value);\nbool encodeFloat_v2(void *handle, GoStringWrap_t oKey, float value, uint32_t expireDuration);\nfloat decodeFloat(void *handle, GoStringWrap_t oKey, float defaultValue);\n\nbool encodeDouble(void *handle, GoStringWrap_t oKey, double value);\nbool encodeDouble_v2(void *handle, GoStringWrap_t oKey, double value, uint32_t expireDuration);\ndouble decodeDouble(void *handle, GoStringWrap_t oKey, double defaultValue);\n\nbool encodeBytes(void *handle, GoStringWrap_t oKey, GoStringWrap_t oValue);\nbool encodeBytes_v2(void *handle, GoStringWrap_t oKey, GoStringWrap_t oValue, uint32_t expireDuration);\nvoid *decodeBytes(void *handle, GoStringWrap_t oKey, uint64_t *lengthPtr);\n\nbool reKey(void *handle, GoStringWrap_t oKey, bool aes256);\nvoid *cryptKey(void *handle, uint32_t *lengthPtr);\nvoid checkReSetCryptKey(void *handle, GoStringWrap_t oKey, bool aes256);\n\nGoStringWrap_t *allKeys(void *handle, uint64_t *lengthPtr, bool filterExpire);\nbool containsKey(void *handle, GoStringWrap_t oKey);\nuint64_t count(void *handle, bool filterExpire);\nuint64_t totalSize(void *handle);\nuint64_t actualSize(void *handle);\n\nvoid removeValueForKey(void *handle, GoStringWrap_t oKey);\nvoid removeValuesForKeys(void *handle, GoStringWrap_t *keyArray, uint64_t count);\nvoid clearAll(void *handle, bool keepSpace);\nuint64_t importFrom(void *handle, void *srcHandle);\n\nvoid mmkvSync(void *handle, bool sync);\nvoid clearMemoryCache(void *handle);\nvoid trim(void *handle);\nvoid mmkvClose(void *handle);\n\nbool backupOneToDirectory(GoStringWrap_t mmapID, GoStringWrap_t dstDir, GoStringWrap_t srcDir);\nbool restoreOneFromDirectory(GoStringWrap_t mmapID, GoStringWrap_t srcDir, GoStringWrap_t dstDir);\nuint64_t backupAllToDirectory(GoStringWrap_t dstDir, GoStringWrap_t srcDir);\nuint64_t restoreAllFromDirectory(GoStringWrap_t srcDir, GoStringWrap_t dstDir);\n\nbool enableAutoExpire(void *handle, uint32_t expireDuration);\nbool disableAutoExpire(void *handle);\nbool enableCompareBeforeSet(void *handle);\nbool disableCompareBeforeSet(void *handle);\n\nint32_t pageSize();\nconst char *version();\nconst char *getRootDir();\n\nvoid setWantsHandler(bool hasHandler, bool wantLog, bool wantContent);\n\nbool removeStorage(GoStringWrap_t mmapID, GoStringWrap_t rootPath);\n\nbool isMultiProcess(void *handle);\nbool isReadOnly(void *handle);\n\nbool getNameSpace(GoStringWrap_t rootPath);\n\nbool checkExist(GoStringWrap_t mmapID, GoStringWrap_t rootPath);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "POSIX/golang/mmkv.go",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// MMKV is a cross-platform key-value storage framework developed by WeChat.\npackage mmkv\n\n// #cgo CXXFLAGS: -D CGO -D FORCE_POSIX -I ${SRCDIR}/../../Core -std=c++17\n// #cgo LDFLAGS: -L./lib -lmmkv -lcore -lz -lpthread\n/*\n#include \"golang-bridge.h\"\n#include <stdlib.h>\n\ntypedef void* voidptr_t;\n\nstatic GoStringWrap_t wrapGoString(_GoString_ str) {\n    GoStringWrap_t wrap = { _GoStringPtr(str), _GoStringLen(str) };\n    return wrap;\n}\n\nstatic GoStringWrap_t GoStringWrapNil() {\n\tGoStringWrap_t result = { 0, 0 };\n\treturn result;\n}\n\nstatic GoStringWrap_t wrapGoByteSlice(const void *ptr, size_t len) {\n    GoStringWrap_t wrap = { ptr, len };\n    return wrap;\n}\n\nstatic void freeStringArray(GoStringWrap_t *a, size_t size) {\n    for (size_t i = 0; i < size; i++) {\n        free((void*) a[i].ptr);\n    }\n}\n*/\nimport \"C\"\nimport (\n\t\"unsafe\"\n)\nimport \"math\"\n\nconst (\n\tMMKVLogDebug = iota // not available for release/product build\n\tMMKVLogInfo         // default level\n\tMMKVLogWarning\n\tMMKVLogError\n\tMMKVLogNone // special level used to disable all log messages\n)\n\nconst (\n\tMMKV_SINGLE_PROCESS        = 1 << iota\n\tMMKV_MULTI_PROCESS         = 1 << iota\n\tcontext_MODE_MULTI_PROCESS = 1 << iota // not available in golang\n\tmmkv_ASHMEM                = 1 << iota // not available in golang\n\tmmkv_BACKUP                = 1 << iota // not available in golang\n\tMMKV_READ_ONLY             = 1 << iota\n)\n\nconst (\n\tMMKV_Expire_Never  = iota\n\tMMKV_Expire_Minute = 60\n\tMMKV_Expire_Hour   = 60 * 60\n\tMMKV_Expire_Day    = 24 * 60 * 60\n\tMMKV_Expire_Month  = 30 * 24 * 60 * 60\n\tMMKV_Expire_Year   = 365 * 30 * 24 * 60 * 60\n)\n\nconst (\n\tMMKV_ON_ERROR_DISCARD = iota\n\tMMKV_ON_ERROR_RECOVER\n)\n\n// MMBuffer a wrapper of native C memory, efficient for simple usage\n// must call MMBuffer.Destroy() after no longer usage\ntype MMBuffer struct {\n\tptr    uintptr\n\tlength int\n}\n\n// Pointer the address of underlying memory\nfunc (buffer MMBuffer) Pointer() uintptr {\n\treturn buffer.ptr\n}\n\n// Length the size of underlying memory\nfunc (buffer MMBuffer) Length() int {\n\treturn buffer.length\n}\n\n// ByteSliceView get byte slice view of underlying memory\n// the slice is valid as long as MMBuffer.Destroy() not called\nfunc (buffer MMBuffer) ByteSliceView() []byte {\n\tbytes := (*[1 << 30]byte)(unsafe.Pointer(buffer.ptr))[0:buffer.length:buffer.length]\n\treturn bytes\n}\n\n// StringView get string view of underlying memory\n// the string is valid as long as MMBuffer.Destroy() not called\nfunc (buffer MMBuffer) StringView() string {\n\treturn *((*string)(unsafe.Pointer(&buffer)))\n}\n\n// Destroy must call Destroy() after no longer usage\nfunc (buffer MMBuffer) Destroy() {\n\tC.free(unsafe.Pointer(buffer.ptr))\n}\n\n// Config all-in-one configuration for creating MMKV instance\ntype Config struct {\n\tMode                   int\n\tEncryption             *EncryptionConfig\n\tRootPath               string\n\tExpectedCapacity       uint64\n\tExpiration             *ExpirationConfig\n\tEnableCompareBeforeSet bool\n\tRecover                int\n\tItemSizeLimit          uint32\n}\n\ntype EncryptionConfig struct {\n\tKey    []byte\n\tAES256 bool\n}\n\ntype ExpirationConfig struct {\n\tEnabled          bool\n\tExpiredInSeconds uint32\n}\n\nfunc configToC(cfg *Config) C.MMKVCreationConfig_t {\n\tif cfg == nil {\n\t\treturn C.MMKVCreationConfig_t{}\n\t}\n\n\tvar cCfg C.MMKVCreationConfig_t\n\tcCfg.mode = C.int32_t(cfg.Mode)\n\tcCfg.expectedCapacity = C.uint64_t(cfg.ExpectedCapacity)\n\tcCfg.enableCompareBeforeSet = C.bool(cfg.EnableCompareBeforeSet)\n\tcCfg.itemSizeLimit = C.uint32_t(cfg.ItemSizeLimit)\n\n\tif cfg.Encryption != nil {\n\t\tif len(cfg.Encryption.Key) > 0 {\n\t\t\tcCfg.cryptKey = C.wrapGoByteSlice(unsafe.Pointer(&cfg.Encryption.Key[0]), C.size_t(len(cfg.Encryption.Key)))\n\t\t} else {\n\t\t\tcCfg.cryptKey = C.GoStringWrapNil()\n\t\t}\n\t\tcCfg.aes256 = C.bool(cfg.Encryption.AES256)\n\t} else {\n\t\tcCfg.cryptKey = C.GoStringWrapNil()\n\t}\n\n\tif cfg.RootPath != \"\" {\n\t\tcCfg.rootPath = C.wrapGoString(cfg.RootPath)\n\t} else {\n\t\tcCfg.rootPath = C.GoStringWrapNil()\n\t}\n\n\tif cfg.Expiration != nil {\n\t\tif cfg.Expiration.Enabled {\n\t\t\tcCfg.enableKeyExpire = 1\n\t\t} else {\n\t\t\tcCfg.enableKeyExpire = 0\n\t\t}\n\t\tcCfg.expiredInSeconds = C.uint32_t(cfg.Expiration.ExpiredInSeconds)\n\t} else {\n\t\tcCfg.enableKeyExpire = -1\n\t}\n\n\tcCfg.recover = C.int32_t(cfg.Recover)\n\n\treturn cCfg\n}\n\ntype MMKV interface {\n\tSetBool(value bool, key string) bool\n\tSetBoolExpire(value bool, key string, expireDuration uint32) bool\n\tGetBool(key string) bool\n\tGetBoolWithDefault(key string, defaultValue bool) bool\n\n\tSetInt32(value int32, key string) bool\n\tSetInt32Expire(value int32, key string, expireDuration uint32) bool\n\tGetInt32(key string) int32\n\tGetInt32WithDefault(key string, defaultValue int32) int32\n\n\tSetInt64(value int64, key string) bool\n\tSetInt64Expire(value int64, key string, expireDuration uint32) bool\n\tGetInt64(key string) int64\n\tGetInt64WithDefault(key string, defaultValue int64) int64\n\n\tSetUInt32(value uint32, key string) bool\n\tSetUInt32Expire(value uint32, key string, expireDuration uint32) bool\n\tGetUInt32(key string) uint32\n\tGetUInt32WithDefault(key string, defaultValue uint32) uint32\n\n\tSetUInt64(value uint64, key string) bool\n\tSetUInt64Expire(value uint64, key string, expireDuration uint32) bool\n\tGetUInt64(key string) uint64\n\tGetUInt64WithDefault(key string, defaultValue uint64) uint64\n\n\tSetFloat32(value float32, key string) bool\n\tSetFloat32Expire(value float32, key string, expireDuration uint32) bool\n\tGetFloat32(key string) float32\n\tGetFloat32WithDefault(key string, defaultValue float32) float32\n\n\tSetFloat64(value float64, key string) bool\n\tSetFloat64Expire(value float64, key string, expireDuration uint32) bool\n\tGetFloat64(key string) float64\n\tGetFloat64WithDefault(key string, defaultValue float64) float64\n\n\t// SetString string value should be utf-8 encoded\n\tSetString(value string, key string) bool\n\tSetStringExpire(value string, key string, expireDuration uint32) bool\n\tGetString(key string) string\n\t// GetStringBuffer get C memory directly (without memcpy), much more efferent for large value\n\tGetStringBuffer(key string) MMBuffer\n\n\tSetBytes(value []byte, key string) bool\n\tSetBytesExpire(value []byte, key string, expireDuration uint32) bool\n\tGetBytes(key string) []byte\n\t// GetBytesBuffer get C memory directly (without memcpy), much more efferent for large value\n\tGetBytesBuffer(key string) MMBuffer\n\n\tRemoveKey(key string)\n\tRemoveKeys(keys []string)\n\n\t// ClearAll clear all key-values\n\tClearAll()\n\tClearAllKeepSpace()\n\n\t// ImportFrom import all key-value items from src\n\t// Return count of items imported\n\tImportFrom(src MMKV) uint64\n\n\t// Count return count of keys\n\tCount() uint64\n\t// CountNonExpiredKeys same as Count() except that it filters expired keys\n\tCountNonExpiredKeys() uint64\n\n\tAllKeys() []string\n\t// AllNonExpireKeys same as AllKeys() except that it filters expired keys\n\tAllNonExpireKeys() []string\n\n\tContains(key string) bool\n\n\t// TotalSize total size of the file\n\tTotalSize() uint64\n\n\t// ActualSize actual used size of the file\n\tActualSize() uint64\n\n\t// MMAP_ID the mmapID of the instance\n\tMMAP_ID() string\n\n\t// Sync Synchronize memory to file. You don't need to call this, really, I mean it. Unless you worry about running out of battery.\n\tSync(sync bool)\n\t// ClearMemoryCache Clear all caches (on memory warning).\n\tClearMemoryCache()\n\n\t/* Trim the file size to minimal.\n\t * MMKV's size won't reduce after deleting key-values.\n\t * Call this method after lots of deleting if you care about disk usage.\n\t * Note that clearAll() has the similar effect.\n\t */\n\tTrim()\n\n\t// Close the instance when it's no longer needed in the near future.\n\t// Any subsequent call to the instance is undefined behavior.\n\tClose()\n\n\t/* ReKey Change encryption key for the MMKV instance.\n\t * The cryptKey is 16 bytes limited.\n\t * You can transfer a plain-text MMKV into encrypted by setting an non-null, non-empty cryptKey.\n\t * Or vice versa by passing cryptKey with null. See also checkReSetCryptKey().\n\t */\n\tReKey(newKey string, aes256 bool) bool\n\n\t// CheckReSetCryptKey Just reset the cryptKey (will not encrypt or decrypt anything).\n\t// Usually you should call this method after other process reKey() the multi-process mmkv.\n\tCheckReSetCryptKey(key string, aes256 bool)\n\n\t// See also reKey().\n\tCryptKey() string\n\n\t// EnableAutoKeyExpire passing MMKV_Expire_Never (0) means never expire\n\tEnableAutoKeyExpire(expireDurationInSecond uint32) bool\n\n\tDisableAutoKeyExpire() bool\n\n\tEnableCompareBeforeSet() bool\n\tDisableCompareBeforeSet() bool\n\n\tIsMultiProcess() bool\n\tIsReadOnly() bool\n}\n\ntype ctorMMKV uintptr\n\n// Version return the version of MMKV\nfunc Version() string {\n\tversion := C.version()\n\tgoStr := C.GoString(version)\n\treturn goStr\n}\n\n/*\nInitializeMMKV MMKV must be initialized before any usage.\n* Generally speaking you should do this inside main():\n\n\tfunc main() {\n\t\tmmkv.InitializeMMKV(\"/path/to/my/working/dir\")\n\t\t// other logic\n\t}\n*/\nfunc InitializeMMKV(rootDir string) {\n\tC.mmkvInitialize(C.wrapGoString(rootDir), MMKVLogInfo, C.bool(false))\n}\n\n// InitializeMMKVWithLogLevel Same as the function InitializeMMKV() above, except that you can customize MMKV's log level by passing logLevel.\n// You can even turnoff logging by passing MMKVLogNone, which we don't recommend doing.\nfunc InitializeMMKVWithLogLevel(rootDir string, logLevel int) {\n\tC.mmkvInitialize(C.wrapGoString(rootDir), C.int32_t(logLevel), C.bool(false))\n}\n\n// Deprecated: Use InitializeMMKVWithLogLevel() + RegisterHandler() instead.\nfunc InitializeMMKVWithLogLevelAndHandler(rootDir string, logLevel int, logHandler func(level int, file string, line int, function string, message string)) {\n\tC.mmkvInitialize(C.wrapGoString(rootDir), C.int32_t(logLevel), C.bool(false))\n\tif logHandler != nil {\n\t\tRegisterHandler(&logHandlerAdapter{logHandler: logHandler})\n\t}\n}\n\n// logHandlerAdapter wraps a legacy log handler function into the Handler interface.\ntype logHandlerAdapter struct {\n\tDefaultHandler\n\tlogHandler func(level int, file string, line int, function string, message string)\n}\n\nfunc (a *logHandlerAdapter) WantLogRedirect() bool { return true }\nfunc (a *logHandlerAdapter) MMKVLog(level int, file string, line int, function string, message string) {\n\ta.logHandler(level, file, line, function, message)\n}\n\n// OnExit Call before App exists, it's just fine not calling it on most case (except when the device shutdown suddenly).\nfunc OnExit() {\n\tC.onExit()\n}\n\n// PageSize return the page size of memory\nfunc PageSize() int32 {\n\treturn int32(C.pageSize())\n}\n\n// DefaultMMKV a generic purpose instance in single-process mode.\nfunc DefaultMMKV() MMKV {\n\treturn DefaultMMKVWithConfig(Config{})\n}\n\n// DefaultMMKVWithMode a generic purpose instance in single-process or multi-process mode.\nfunc DefaultMMKVWithMode(mode int) MMKV {\n\treturn DefaultMMKVWithConfig(Config{Mode: mode})\n}\n\n// DefaultMMKVWithModeAndCryptKey an encrypted generic purpose instance in single-process or multi-process mode.\nfunc DefaultMMKVWithModeAndCryptKey(mode int, cryptKey string, aes256 bool) MMKV {\n\treturn DefaultMMKVWithConfig(Config{Mode: mode, Encryption: &EncryptionConfig{AES256: aes256, Key: []byte(cryptKey)}})\n}\n\n// DefaultMMKVWithConfig a generic purpose instance with all-in-one configuration.\nfunc DefaultMMKVWithConfig(config Config) MMKV {\n\tcfg := configToC(&config)\n\tmmkv := ctorMMKV(C.getDefaultMMKV(cfg))\n\treturn MMKV(mmkv)\n}\n\n// MMKVWithID an instance with specific location ${MMKV Root}/mmapID, in single-process mode.\nfunc MMKVWithID(mmapID string) MMKV {\n\treturn MMKVWithIDAndConfig(mmapID, Config{})\n}\n\nfunc MMKVWithIDAndConfig(mmapID string, config Config) MMKV {\n\tcfg := configToC(&config)\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\n// MMKVWithIDAndExpectedCapacity an instance with specific location ${MMKV Root}/mmapID, in single-process mode.\nfunc MMKVWithIDAndExpectedCapacity(mmapID string, expectedCapacity uint64) MMKV {\n\treturn MMKVWithIDAndConfig(mmapID, Config{ExpectedCapacity: expectedCapacity})\n}\n\n// MMKVWithIDAndMode an instance with specific location ${MMKV Root}/mmapID, in single-process or multi-process mode.\nfunc MMKVWithIDAndMode(mmapID string, mode int) MMKV {\n\treturn MMKVWithIDAndConfig(mmapID, Config{Mode: mode})\n}\n\n// MMKVWithIDAndModeAndCryptKey an encrypted instance with specific location ${MMKV Root}/mmapID, in single-process or multi-process mode.\nfunc MMKVWithIDAndModeAndCryptKey(mmapID string, mode int, cryptKey string, aes256 bool) MMKV {\n\treturn MMKVWithIDAndConfig(mmapID, Config{Mode: mode, Encryption: &EncryptionConfig{AES256: aes256, Key: []byte(cryptKey)}})\n}\n\n// BackupOneToDirectory backup one MMKV instance (from the root dir of MMKV) to dstDir\nfunc BackupOneToDirectory(mmapID string, dstDir string) bool {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.backupOneToDirectory(C.wrapGoString(mmapID), C.wrapGoString(dstDir), cStrNull)\n\treturn bool(ret)\n}\n\n// RestoreOneFromDirectory restore one MMKV instance from srcDir (to the root dir of MMKV)\nfunc RestoreOneFromDirectory(mmapID string, srcDir string) bool {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.restoreOneFromDirectory(C.wrapGoString(mmapID), C.wrapGoString(srcDir), cStrNull)\n\treturn bool(ret)\n}\n\n// BackupAllToDirectory backup all MMKV instance (from the root dir of MMKV) to dstDir\n// return count of MMKV successfully backup-ed\nfunc BackupAllToDirectory(dstDir string) uint64 {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.backupAllToDirectory(C.wrapGoString(dstDir), cStrNull)\n\treturn uint64(ret)\n}\n\n// RestoreAllFromDirectory restore all MMKV instance from srcDir (to the root dir of MMKV)\n// return count of MMKV successfully restored\nfunc RestoreAllFromDirectory(srcDir string) uint64 {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.restoreAllFromDirectory(C.wrapGoString(srcDir), cStrNull)\n\treturn uint64(ret)\n}\n\n// RemoveStorage Remove the storage of the MMKV, including the data file & meta file (.crc)\n// Note: the existing instance (if any) will be closed & destroyed\nfunc RemoveStorage(mmapID string) bool {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.removeStorage(C.wrapGoString(mmapID), cStrNull)\n\treturn bool(ret)\n}\n\nfunc GetRootDir() string {\n\troot := C.getRootDir()\n\tgoStr := C.GoString(root)\n\treturn goStr\n}\n\n// CheckExist Check the existence of the MMKV\nfunc CheckExist(mmapID string) bool {\n\tcStrNull := C.GoStringWrapNil()\n\tret := C.checkExist(C.wrapGoString(mmapID), cStrNull)\n\treturn bool(ret)\n}\n\n// GetNameSpace Get a wrapper of customize root path.\nfunc GetNameSpace(rootDir string) NameSpace {\n\tif C.getNameSpace(C.wrapGoString(rootDir)) {\n\t\treturn NameSpace{rootDir: rootDir}\n\t}\n\treturn NameSpace{}\n}\n\n// DefaultNameSpace Identical to MMKV with default root path.\nfunc DefaultNameSpace() NameSpace {\n\treturn NameSpace{rootDir: GetRootDir()}\n}\n\nfunc (kv ctorMMKV) SetBool(value bool, key string) bool {\n\tret := C.encodeBool(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetBoolExpire(value bool, key string, expireDuration uint32) bool {\n\tret := C.encodeBool_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetBool(key string) bool {\n\treturn kv.GetBoolWithDefault(key, false)\n}\n\nfunc (kv ctorMMKV) GetBoolWithDefault(key string, defaultValue bool) bool {\n\tvalue := C.decodeBool(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(defaultValue))\n\treturn bool(value)\n}\n\nfunc (kv ctorMMKV) SetInt32(value int32, key string) bool {\n\tret := C.encodeInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.int32_t(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetInt32Expire(value int32, key string, expireDuration uint32) bool {\n\tret := C.encodeInt32_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.int32_t(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetInt32(key string) int32 {\n\treturn kv.GetInt32WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetInt32WithDefault(key string, defaultValue int32) int32 {\n\tvalue := C.decodeInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.int32_t(defaultValue))\n\treturn int32(value)\n}\n\nfunc (kv ctorMMKV) SetUInt32(value uint32, key string) bool {\n\tret := C.encodeUInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.uint32_t(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetUInt32Expire(value uint32, key string, expireDuration uint32) bool {\n\tret := C.encodeUInt32_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.uint32_t(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetUInt32(key string) uint32 {\n\treturn kv.GetUInt32WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetUInt32WithDefault(key string, defaultValue uint32) uint32 {\n\tvalue := C.decodeUInt32(unsafe.Pointer(kv), C.wrapGoString(key), C.uint32_t(defaultValue))\n\treturn uint32(value)\n}\n\nfunc (kv ctorMMKV) SetInt64(value int64, key string) bool {\n\tret := C.encodeInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.int64_t(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetInt64Expire(value int64, key string, expireDuration uint32) bool {\n\tret := C.encodeInt64_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.int64_t(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetInt64(key string) int64 {\n\treturn kv.GetInt64WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetInt64WithDefault(key string, defaultValue int64) int64 {\n\tvalue := C.decodeInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.int64_t(defaultValue))\n\treturn int64(value)\n}\n\nfunc (kv ctorMMKV) SetUInt64(value uint64, key string) bool {\n\tret := C.encodeUInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.uint64_t(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetUInt64Expire(value uint64, key string, expireDuration uint32) bool {\n\tret := C.encodeUInt64_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.uint64_t(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetUInt64(key string) uint64 {\n\treturn kv.GetUInt64WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetUInt64WithDefault(key string, defaultValue uint64) uint64 {\n\tvalue := C.decodeUInt64(unsafe.Pointer(kv), C.wrapGoString(key), C.uint64_t(defaultValue))\n\treturn uint64(value)\n}\n\nfunc (kv ctorMMKV) SetFloat32(value float32, key string) bool {\n\tret := C.encodeFloat(unsafe.Pointer(kv), C.wrapGoString(key), C.float(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetFloat32Expire(value float32, key string, expireDuration uint32) bool {\n\tret := C.encodeFloat_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.float(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetFloat32(key string) float32 {\n\treturn kv.GetFloat32WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetFloat32WithDefault(key string, defaultValue float32) float32 {\n\tvalue := C.decodeFloat(unsafe.Pointer(kv), C.wrapGoString(key), C.float(defaultValue))\n\treturn float32(value)\n}\n\nfunc (kv ctorMMKV) SetFloat64(value float64, key string) bool {\n\tret := C.encodeDouble(unsafe.Pointer(kv), C.wrapGoString(key), C.double(value))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetFloat64Expire(value float64, key string, expireDuration uint32) bool {\n\tret := C.encodeDouble_v2(unsafe.Pointer(kv), C.wrapGoString(key), C.double(value), C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetFloat64(key string) float64 {\n\treturn kv.GetFloat64WithDefault(key, 0)\n}\n\nfunc (kv ctorMMKV) GetFloat64WithDefault(key string, defaultValue float64) float64 {\n\tvalue := C.decodeDouble(unsafe.Pointer(kv), C.wrapGoString(key), C.double(defaultValue))\n\treturn float64(value)\n}\n\nfunc (kv ctorMMKV) SetString(value string, key string) bool {\n\tcValue := C.wrapGoString(value)\n\tret := C.encodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), cValue)\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetStringExpire(value string, key string, expireDuration uint32) bool {\n\tcValue := C.wrapGoString(value)\n\tret := C.encodeBytes_v2(unsafe.Pointer(kv), C.wrapGoString(key), cValue, C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetString(key string) string {\n\tvar length uint64\n\n\tcValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))\n\tvalue := C.GoStringN((*C.char)(cValue), C.int(length))\n\n\tC.free(unsafe.Pointer(cValue))\n\treturn value\n}\n\nfunc (kv ctorMMKV) GetStringBuffer(key string) MMBuffer {\n\treturn kv.GetBytesBuffer(key)\n}\n\nfunc (kv ctorMMKV) SetBytes(value []byte, key string) bool {\n\tcValue := C.wrapGoByteSlice(unsafe.Pointer(&value[0]), C.size_t(len(value)))\n\tret := C.encodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), cValue)\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) SetBytesExpire(value []byte, key string, expireDuration uint32) bool {\n\tcValue := C.wrapGoByteSlice(unsafe.Pointer(&value[0]), C.size_t(len(value)))\n\tret := C.encodeBytes_v2(unsafe.Pointer(kv), C.wrapGoString(key), cValue, C.uint32_t(expireDuration))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) GetBytes(key string) []byte {\n\tvar length uint64\n\n\tcValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))\n\tvalue := C.GoBytes(unsafe.Pointer(cValue), C.int(length))\n\n\tC.free(unsafe.Pointer(cValue))\n\treturn value\n}\n\nfunc (kv ctorMMKV) GetBytesBuffer(key string) MMBuffer {\n\tvar length uint64\n\n\tcValue := C.decodeBytes(unsafe.Pointer(kv), C.wrapGoString(key), (*C.uint64_t)(&length))\n\tvalue := MMBuffer{uintptr(cValue), int(length)}\n\n\treturn value\n}\n\nfunc (kv ctorMMKV) RemoveKey(key string) {\n\tC.removeValueForKey(unsafe.Pointer(kv), C.wrapGoString(key))\n}\n\nfunc (kv ctorMMKV) RemoveKeys(keys []string) {\n\tkeyArray := (*C.struct_GoStringWrap)(unsafe.Pointer(&keys[0]))\n\tC.removeValuesForKeys(unsafe.Pointer(kv), keyArray, C.uint64_t(len(keys)))\n}\n\nfunc (kv ctorMMKV) Count() uint64 {\n\treturn uint64(C.count(unsafe.Pointer(kv), C.bool(false)))\n}\n\nfunc (kv ctorMMKV) CountNonExpiredKeys() uint64 {\n\treturn uint64(C.count(unsafe.Pointer(kv), C.bool(true)))\n}\n\nfunc (kv ctorMMKV) allKeys(filterExpire bool) []string {\n\tcount := uint64(0)\n\tkeyArray := C.allKeys(unsafe.Pointer(kv), (*C.uint64_t)(&count), C.bool(filterExpire))\n\tif keyArray == nil || count == 0 {\n\t\treturn []string{}\n\t}\n\t// turn C array into go slice with offset(0), length(count) & capacity(count)\n\tkeys := (*[math.MaxInt32 / unsafe.Sizeof(C.struct_GoStringWrap{})]C.struct_GoStringWrap)(unsafe.Pointer(keyArray))[0:count:count]\n\n\t// Actually the keys IS a go string slice, but we need to COPY the elements for the caller to use.\n\t// Too bad go doesn't has destructors, hence we can't simply TRANSFER ownership of C memory.\n\tresult := make([]string, count)\n\tfor index := uint64(0); index < count; index++ {\n\t\tkey := keys[index]\n\t\tresult[index] = C.GoStringN(key.ptr, C.int(key.length))\n\t}\n\n\tC.freeStringArray(keyArray, C.size_t(count))\n\tC.free(unsafe.Pointer(keyArray))\n\treturn result\n}\n\nfunc (kv ctorMMKV) AllKeys() []string {\n\treturn kv.allKeys(false)\n}\n\nfunc (kv ctorMMKV) AllNonExpireKeys() []string {\n\treturn kv.allKeys(true)\n}\n\nfunc (kv ctorMMKV) Contains(key string) bool {\n\tret := C.containsKey(unsafe.Pointer(kv), C.wrapGoString(key))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) ClearAll() {\n\tC.clearAll(unsafe.Pointer(kv), false)\n}\n\nfunc (kv ctorMMKV) ClearAllKeepSpace() {\n\tC.clearAll(unsafe.Pointer(kv), true)\n}\n\nfunc (kv ctorMMKV) TotalSize() uint64 {\n\treturn uint64(C.totalSize(unsafe.Pointer(kv)))\n}\n\nfunc (kv ctorMMKV) ActualSize() uint64 {\n\treturn uint64(C.actualSize(unsafe.Pointer(kv)))\n}\n\nfunc (kv ctorMMKV) MMAP_ID() string {\n\tcStr := C.mmapID(unsafe.Pointer(kv))\n\treturn C.GoString(cStr)\n}\n\nfunc (kv ctorMMKV) Sync(sync bool) {\n\tC.mmkvSync(unsafe.Pointer(kv), C.bool(sync))\n}\n\nfunc (kv ctorMMKV) ClearMemoryCache() {\n\tC.clearMemoryCache(unsafe.Pointer(kv))\n}\n\nfunc (kv ctorMMKV) Trim() {\n\tC.trim(unsafe.Pointer(kv))\n}\n\nfunc (kv ctorMMKV) ImportFrom(src MMKV) uint64 {\n\tsrcCtor, ok := src.(ctorMMKV)\n\tif !ok {\n\t\treturn 0\n\t}\n\treturn uint64(C.importFrom(unsafe.Pointer(kv), unsafe.Pointer(srcCtor)))\n}\n\nfunc (kv ctorMMKV) Close() {\n\tC.mmkvClose(unsafe.Pointer(kv))\n}\n\nfunc (kv ctorMMKV) ReKey(newKey string, aes256 bool) bool {\n\tret := C.reKey(unsafe.Pointer(kv), C.wrapGoString(newKey), C.bool(aes256))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) CheckReSetCryptKey(key string, aes256 bool) {\n\tC.checkReSetCryptKey(unsafe.Pointer(kv), C.wrapGoString(key), C.bool(aes256))\n}\n\nfunc (kv ctorMMKV) CryptKey() string {\n\tvar cLen C.uint32_t\n\tcStr := C.cryptKey(unsafe.Pointer(kv), &cLen)\n\tif cStr == nil || cLen == 0 {\n\t\treturn \"\"\n\t}\n\tresult := C.GoStringN((*C.char)(cStr), C.int(cLen))\n\tC.free(unsafe.Pointer(cStr))\n\treturn result\n}\n\nfunc (kv ctorMMKV) EnableAutoKeyExpire(expireDurationInSecond uint32) bool {\n\tret := C.enableAutoExpire(unsafe.Pointer(kv), C.uint32_t(expireDurationInSecond))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) DisableAutoKeyExpire() bool {\n\tret := C.disableAutoExpire(unsafe.Pointer(kv))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) EnableCompareBeforeSet() bool {\n\tret := C.enableCompareBeforeSet(unsafe.Pointer(kv))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) DisableCompareBeforeSet() bool {\n\tret := C.disableCompareBeforeSet(unsafe.Pointer(kv))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) IsMultiProcess() bool {\n\tret := C.isMultiProcess(unsafe.Pointer(kv))\n\treturn bool(ret)\n}\n\nfunc (kv ctorMMKV) IsReadOnly() bool {\n\tret := C.isReadOnly(unsafe.Pointer(kv))\n\treturn bool(ret)\n}\n\ntype NameSpace struct {\n\trootDir string\n}\n\nfunc (ns *NameSpace) GetRootDir() string {\n\treturn ns.rootDir\n}\n\n// MMKVWithID an instance with specific location ${NameSpace Root}/mmapID, in single-process mode.\nfunc (ns *NameSpace) MMKVWithID(mmapID string) MMKV {\n\tcfg := configToC(&Config{RootPath: ns.rootDir})\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\nfunc (ns *NameSpace) MMKVWithIDAndConfig(mmapID string, config Config) MMKV {\n\tconfig.RootPath = ns.rootDir\n\tcfg := configToC(&config)\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\n// MMKVWithIDAndExpectedCapacity an instance with specific location ${NameSpace Root}/mmapID, in single-process mode.\nfunc (ns *NameSpace) MMKVWithIDAndExpectedCapacity(mmapID string, expectedCapacity uint64) MMKV {\n\tcfg := configToC(&Config{RootPath: ns.rootDir, ExpectedCapacity: expectedCapacity})\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\n// MMKVWithIDAndMode an instance with specific location ${NameSpace Root}/mmapID, in single-process or multi-process mode.\nfunc (ns *NameSpace) MMKVWithIDAndMode(mmapID string, mode int) MMKV {\n\tcfg := configToC(&Config{Mode: mode, RootPath: ns.rootDir})\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\n// MMKVWithIDAndModeAndCryptKey an encrypted instance with specific location ${NameSpace Root}/mmapID, in single-process or multi-process mode.\nfunc (ns *NameSpace) MMKVWithIDAndModeAndCryptKey(mmapID string, mode int, cryptKey string, aes256 bool) MMKV {\n\tcfg := configToC(&Config{Mode: mode, RootPath: ns.rootDir, Encryption: &EncryptionConfig{AES256: aes256, Key: []byte(cryptKey)}})\n\tmmkv := ctorMMKV(C.getMMKVWithID(C.wrapGoString(mmapID), cfg))\n\treturn MMKV(mmkv)\n}\n\n// BackupOneToDirectory backup one MMKV instance (from the root dir of NameSpace) to dstDir\nfunc (ns *NameSpace) BackupOneToDirectory(mmapID string, dstDir string) bool {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.backupOneToDirectory(C.wrapGoString(mmapID), C.wrapGoString(dstDir), root)\n\treturn bool(ret)\n}\n\n// RestoreOneFromDirectory restore one MMKV instance from srcDir (to the root dir of NameSpace)\nfunc (ns *NameSpace) RestoreOneFromDirectory(mmapID string, srcDir string) bool {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.restoreOneFromDirectory(C.wrapGoString(mmapID), C.wrapGoString(srcDir), root)\n\treturn bool(ret)\n}\n\n// BackupAllToDirectory backup all MMKV instance (from the root dir of NameSpace) to dstDir\n// return count of MMKV successfully backup-ed\nfunc (ns *NameSpace) BackupAllToDirectory(dstDir string) uint64 {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.backupAllToDirectory(C.wrapGoString(dstDir), root)\n\treturn uint64(ret)\n}\n\n// RestoreAllFromDirectory restore all MMKV instance from srcDir (to the root dir of NameSpace)\n// return count of MMKV successfully restored\nfunc (ns *NameSpace) RestoreAllFromDirectory(srcDir string) uint64 {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.restoreAllFromDirectory(C.wrapGoString(srcDir), root)\n\treturn uint64(ret)\n}\n\n// RemoveStorage Remove the storage of the MMKV inside NameSpace, including the data file & meta file (.crc)\n// Note: the existing instance (if any) will be closed & destroyed\nfunc (ns *NameSpace) RemoveStorage(mmapID string) bool {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.removeStorage(C.wrapGoString(mmapID), root)\n\treturn bool(ret)\n}\n\n// CheckExist Check the existence of the MMKV inside NameSpace\nfunc (ns *NameSpace) CheckExist(mmapID string) bool {\n\troot := C.wrapGoString(ns.rootDir)\n\tret := C.checkExist(C.wrapGoString(mmapID), root)\n\treturn bool(ret)\n}\n"
  },
  {
    "path": "POSIX/golang/mmkv_test.go",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage mmkv\n\nimport (\n\t\"testing\"\n\t//     \"regexp\"\n)\n\nfunc TestVersionEmpty(t *testing.T) {\n\tmsg := Version()\n\tif msg == \"\" {\n\t\tt.Fatalf(\"Version() == \\\"\\\"\")\n\t}\n}\n"
  },
  {
    "path": "POSIX/golang/test/go.mod",
    "content": "module mmkv_test\n\ngo 1.15\n\nreplace tencent.com/mmkv => ../tencent.com/mmkv\n\nrequire tencent.com/mmkv v0.0.0-00010101000000-000000000000\n"
  },
  {
    "path": "POSIX/golang/test/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t//\"log\"\n\t\"math\"\n\t\"os\"\n\t\"time\"\n\n\t\"tencent.com/mmkv\"\n)\n\nfunc main() {\n\t// test NameSpace before mmkv.Initialize()\n\ttestNameSpace()\n\n\t// init MMKV with root dir\n\tmmkv.InitializeMMKVWithLogLevel(\"/tmp/mmkv\", mmkv.MMKVLogInfo)\n\n\t{\n\t\tfmt.Println(\"rootDir:\", mmkv.GetRootDir())\n\t\tns := mmkv.DefaultNameSpace()\n\t\tfmt.Println(\"DefaultNameSpace:\", ns.GetRootDir())\n\t}\n\n\t// register unified callback handler (log redirect, error handler, content change)\n\tmmkv.RegisterHandler(&myHandler{})\n\n\ttestExpectedCapacity()\n\tfunctionalTest()\n\n\ttestMMKV(\"test/Encrypt\", \"cryptKey\", false, false)\n\ttestBackup()\n\ttestRestore()\n\ttestAutoExpire()\n\ttestCompareBeforeSet()\n\ttestRemoveStorage()\n\ttestReadOnly()\n\ttestImport()\n\ttestReKey()\n}\n\nfunc functionalTest() {\n\tkv := mmkv.DefaultMMKV()\n\tfmt.Println(\"actual size:\", kv.ActualSize())\n\tfmt.Println(\"total size:\", kv.TotalSize())\n\n\tkv.SetBool(true, \"bool\")\n\tfmt.Println(\"bool =\", kv.GetBool(\"bool\"))\n\n\tkv.SetInt32(math.MinInt32, \"int32\")\n\tfmt.Println(\"int32 =\", kv.GetInt32(\"int32\"))\n\n\tkv.SetUInt32(math.MaxUint32, \"uint32\")\n\tfmt.Println(\"uint32 =\", kv.GetUInt32(\"uint32\"))\n\n\tkv.SetInt64(math.MinInt64, \"int64\")\n\tfmt.Println(\"int64 =\", kv.GetInt64(\"int64\"))\n\n\tkv.SetUInt64(math.MaxUint64, \"uint64\")\n\tfmt.Println(\"uint64 =\", kv.GetUInt64(\"uint64\"))\n\n\tkv.SetFloat32(math.MaxFloat32, \"float32\")\n\tfmt.Println(\"float32 =\", kv.GetFloat32(\"float32\"))\n\n\tkv.SetFloat64(math.MaxFloat64, \"float64\")\n\tfmt.Println(\"float64 =\", kv.GetFloat64(\"float64\"))\n\n\tkv.SetString(\"Hello world, 你好 from MMKV!\", \"string\")\n\tfmt.Println(\"string =\", kv.GetString(\"string\"))\n\n\t// much more efficient\n\tbuffer := kv.GetStringBuffer(\"string\")\n\tfmt.Println(\"string(buffer) =\", buffer.StringView())\n\t// must call Destroy() after usage\n\tbuffer.Destroy()\n\n\tkv.SetBytes([]byte(\"Hello world, 你好 from MMKV 以及 bytes!\"), \"bytes\")\n\tfmt.Println(\"bytes =\", string(kv.GetBytes(\"bytes\")))\n\n\t// much more efficient\n\tbuffer = kv.GetBytesBuffer(\"bytes\")\n\tfmt.Println(\"bytes(buffer) =\", string(buffer.ByteSliceView()))\n\t// must call Destroy() after usage\n\tbuffer.Destroy()\n\n\tfmt.Println(\"contains \\\"bool\\\"? \", kv.Contains(\"bool\"))\n\tkv.RemoveKey(\"bool\")\n\tfmt.Println(\"after remove, contains \\\"bool\\\"? \", kv.Contains(\"bool\"))\n\n\tkv.RemoveKeys([]string{\"int32\", \"int64\"})\n\tfmt.Println(\"count =\", kv.Count(), \", all keys:\", kv.AllKeys())\n\n\tkv.Trim()\n\tkv.ClearMemoryCache()\n\tfmt.Println(\"all keys:\", kv.AllKeys())\n\tkv.ClearAll()\n\tfmt.Println(\"all keys:\", kv.AllKeys())\n\tkv.Sync(true)\n\tkv.Close()\n}\n\nfunc testMMKV(mmapID string, cryptKey string, decodeOnly bool, aes bool) mmkv.MMKV {\n\tkv := mmkv.MMKVWithIDAndModeAndCryptKey(mmapID, mmkv.MMKV_SINGLE_PROCESS, cryptKey, aes)\n\ttestMMKVImp(kv, decodeOnly)\n\treturn kv\n}\n\nfunc testMMKVImp(kv mmkv.MMKV, decodeOnly bool) {\n\tif !decodeOnly {\n\t\tkv.SetBool(true, \"bool\")\n\t}\n\tfmt.Println(\"bool =\", kv.GetBool(\"bool\"))\n\n\tif !decodeOnly {\n\t\tkv.SetInt32(math.MinInt32, \"int32\")\n\t}\n\tfmt.Println(\"int32 =\", kv.GetInt32(\"int32\"))\n\n\tif !decodeOnly {\n\t\tkv.SetUInt32(math.MaxUint32, \"uint32\")\n\t}\n\tfmt.Println(\"uint32 =\", kv.GetUInt32(\"uint32\"))\n\n\tif !decodeOnly {\n\t\tkv.SetInt64(math.MinInt64, \"int64\")\n\t}\n\tfmt.Println(\"int64 =\", kv.GetInt64(\"int64\"))\n\n\tif !decodeOnly {\n\t\tkv.SetUInt64(math.MaxUint64, \"uint64\")\n\t}\n\tfmt.Println(\"uint64 =\", kv.GetUInt64(\"uint64\"))\n\n\tif !decodeOnly {\n\t\tkv.SetFloat32(math.MaxFloat32, \"float32\")\n\t}\n\tfmt.Println(\"float32 =\", kv.GetFloat32(\"float32\"))\n\n\tif !decodeOnly {\n\t\tkv.SetFloat64(math.MaxFloat64, \"float64\")\n\t}\n\tfmt.Println(\"float64 =\", kv.GetFloat64(\"float64\"))\n\n\tif !decodeOnly {\n\t\tkv.SetString(\"Hello world, 你好 from MMKV!\", \"string\")\n\t}\n\tfmt.Println(\"string =\", kv.GetString(\"string\"))\n\n\tif !decodeOnly {\n\t\tkv.SetBytes([]byte(\"Hello world, 你好 from MMKV 以及 bytes!\"), \"bytes\")\n\t}\n\tfmt.Println(\"bytes =\", string(kv.GetBytes(\"bytes\")))\n\n\tfmt.Println(\"contains \\\"bool\\\"? \", kv.Contains(\"bool\"))\n\tkv.RemoveKey(\"bool\")\n\tfmt.Println(\"after remove, contains \\\"bool\\\"? \", kv.Contains(\"bool\"))\n\n\tkv.RemoveKeys([]string{\"int32\", \"int64\"})\n\tfmt.Println(\"all keys:\", kv.AllKeys())\n}\n\nfunc testReKey() {\n\tmmapID := \"testAES_reKey1\"\n\tkv := testMMKV(mmapID, \"\", false, false)\n\tif kv == nil {\n\t\treturn\n\t}\n\n\tkv.ReKey(\"Key_seq_1\", false)\n\tkv.ClearMemoryCache()\n\ttestMMKV(mmapID, \"Key_seq_1\", true, false)\n\n\tkv.ReKey(\"Key_Seq_Very_Looooooooong\", true)\n\tkv.ClearMemoryCache()\n\ttestMMKV(mmapID, \"Key_Seq_Very_Looooooooong\", true, true)\n\n\tkv.ReKey(\"\", false)\n\tkv.ClearMemoryCache()\n\ttestMMKV(mmapID, \"\", true, false)\n}\n\nfunc testBackup() {\n\trootDir := \"/tmp/mmkv_backup\"\n\tmmapID := \"test/Encrypt\"\n\tret := mmkv.BackupOneToDirectory(mmapID, rootDir)\n\tfmt.Println(\"backup one return: \", ret)\n\n\tcount := mmkv.BackupAllToDirectory(rootDir)\n\tfmt.Println(\"backup all count: \", count)\n}\n\nfunc testExpectedCapacity() {\n\tkey := \"key0\"\n\tvalue := \"🏊🏻®4️⃣🐅_\"\n\tdataLen := 10000\n\tfor i := 0; i < dataLen; i++ {\n\t\tvalue = value + string('0')\n\t}\n\tfmt.Println(\"value size = \", len(value))\n\texpectedSize := uint64(len(key) + len(value))\n\t// if we know exactly the sizes of key and value, set expectedCapacity for performance improvement\n\tkv := mmkv.MMKVWithIDAndExpectedCapacity(\"expectedCapacityTest0\", expectedSize)\n\t// 0 times expand\n\tkv.SetString(value, key)\n\t//     fmt.Println(\"string =\", bytes.Count([]byte(kv.GetString(\"key0\")), nil))\n\n\tcount := 10\n\texpectedSize1 := expectedSize * uint64(count)\n\tfmt.Println(\"expectedSize1 =\", expectedSize1)\n\tkv1 := mmkv.MMKVWithIDAndExpectedCapacity(\"expectedCapacityTest1\", expectedSize1)\n\tfor i := 0; i < count; i++ {\n\t\tkey := \"key\" + string(i)\n\t\t// 0 times expand\n\t\tkv1.SetString(value, key)\n\t}\n\n}\n\nfunc testRestore() {\n\trootDir := \"/tmp/mmkv_backup\"\n\tmmapID := \"test/Encrypt\"\n\taesKey := \"cryptKey\"\n\tkv := mmkv.MMKVWithIDAndModeAndCryptKey(mmapID, mmkv.MMKV_SINGLE_PROCESS, aesKey, false)\n\tkv.SetString(\"string value before restore\", \"test_restore_key\")\n\tfmt.Println(\"before restore [\", kv.MMAP_ID(), \"] allKeys: \", kv.AllKeys())\n\n\tret := mmkv.RestoreOneFromDirectory(mmapID, rootDir)\n\tfmt.Println(\"restore one return: \", ret)\n\tif ret {\n\t\tfmt.Println(\"after restore [\", kv.MMAP_ID(), \"] allKeys: \", kv.AllKeys())\n\t}\n\n\tcount := mmkv.RestoreAllFromDirectory(rootDir)\n\tfmt.Println(\"restore all count: \", count)\n\tif count > 0 {\n\t\tbackupMMKV := mmkv.MMKVWithIDAndModeAndCryptKey(mmapID, mmkv.MMKV_SINGLE_PROCESS, aesKey, false)\n\t\tfmt.Println(\"check on restore [\", backupMMKV.MMAP_ID(), \"] allKeys: \", backupMMKV.AllKeys())\n\n\t\tbackupMMKV = mmkv.MMKVWithID(\"testAES_reKey1\")\n\t\tfmt.Println(\"check on restore [\", backupMMKV.MMAP_ID(), \"] allKeys: \", backupMMKV.AllKeys())\n\n\t\tbackupMMKV = mmkv.DefaultMMKV()\n\t\tfmt.Println(\"check on restore [\", backupMMKV.MMAP_ID(), \"] allKeys: \", backupMMKV.AllKeys())\n\t}\n}\n\nfunc testAutoExpire() {\n\tkv := mmkv.MMKVWithIDAndConfig(\"testAutoExpire\", mmkv.Config{\n\t\tExpiration: &mmkv.ExpirationConfig{Enabled: false}})\n\tkv.ClearAllKeepSpace()\n\tkv.Trim()\n\t//kv.DisableAutoKeyExpire()\n\n\tkv.SetBool(true, \"auto_expire_key_1\")\n\tkv.Close()\n\tkv = mmkv.MMKVWithIDAndConfig(\"testAutoExpire\", mmkv.Config{\n\t\tExpiration: &mmkv.ExpirationConfig{Enabled: true, ExpiredInSeconds: 1}})\n\t//kv.EnableAutoKeyExpire(1)\n\tkv.SetBoolExpire(true, \"never_expire_key_1\", mmkv.MMKV_Expire_Never)\n\n\ttime.Sleep(2 * time.Second)\n\tfmt.Println(\"contains auto_expire_key_1:\", kv.Contains(\"auto_expire_key_1\"))\n\tfmt.Println(\"contains never_expire_key_1:\", kv.Contains(\"never_expire_key_1\"))\n\n\tkv.RemoveKey(\"never_expire_key_1\")\n\tkv.EnableAutoKeyExpire(mmkv.MMKV_Expire_Never)\n\tkv.SetBool(true, \"never_expire_key_1\")\n\tkv.SetBoolExpire(true, \"auto_expire_key_1\", 1)\n\ttime.Sleep(2 * time.Second)\n\tfmt.Println(\"contains never_expire_key_1:\", kv.Contains(\"never_expire_key_1\"))\n\tfmt.Println(\"contains auto_expire_key_1:\", kv.Contains(\"auto_expire_key_1\"))\n\tfmt.Println(\"count non expire keys\", kv.CountNonExpiredKeys())\n\tfmt.Println(\"all non expire keys\", kv.AllNonExpireKeys())\n}\n\nfunc testCompareBeforeSet() {\n\tkv := mmkv.MMKVWithID(\"testCompareBeforeSet\")\n\tkv.EnableCompareBeforeSet()\n\tkv.SetString(\"extraValue\", \"extraKey\")\n\n\tkey := \"\"\n\t{\n\t\tkey = \"bool\"\n\t\tkv.SetBool(true, key)\n\t\tfmt.Println(\"testCompareBeforeSet: bool value = \", kv.GetBool(key))\n\t\tactualSize1 := kv.ActualSize()\n\t\tfmt.Println(\"testCompareBeforeSet: actualSize = \", actualSize1)\n\t\tfmt.Println(\"testCompareBeforeSet: bool value = \", kv.GetBool(key))\n\t\tkv.SetBool(true, key)\n\t\tactualSize2 := kv.ActualSize()\n\t\tfmt.Println(\"testCompareBeforeSet: actualSize2 = \", actualSize2)\n\t\tif actualSize1 != actualSize2 {\n\t\t\tpanic(\"size not match\")\n\t\t}\n\t\tkv.SetBool(false, key)\n\t\tfmt.Println(\"testCompareBeforeSet: bool value = \", kv.GetBool(key))\n\t\tif kv.GetBool(key) != false {\n\t\t\tpanic(\"value not update\")\n\t\t}\n\t}\n\n\ts1 := \"🏊🏻®hhh4️⃣🐅_yyy\"\n\ts2 := \"0aA🏊🏻®hhh4️⃣🐅_zzz\"\n\t{\n\t\tkey = \"string\"\n\t\tkv.SetString(s1, key)\n\t\tresultString := kv.GetString(key)\n\t\tfmt.Println(\"testCompareBeforeSet: string = \", resultString)\n\t\tactualSize1 := kv.ActualSize()\n\t\tfmt.Println(\"testCompareBeforeSet: actualSize = \", actualSize1)\n\t\tresultString = kv.GetString(key)\n\t\tfmt.Println(\"testCompareBeforeSet: string = \", resultString)\n\t\tkv.SetString(s1, key)\n\t\tactualSize2 := kv.ActualSize()\n\t\tif actualSize1 != actualSize2 {\n\t\t\tpanic(\"size not match\")\n\t\t}\n\t\tkv.SetString(s2, key)\n\t\tresultString = kv.GetString(key)\n\t\tfmt.Println(\"testCompareBeforeSet: string = \", resultString)\n\t\tif resultString != s2 {\n\t\t\tpanic(\"value not update\")\n\t\t}\n\t}\n\n\tkv.DisableCompareBeforeSet()\n}\n\nfunc testRemoveStorage() {\n\tkv := mmkv.MMKVWithIDAndMode(\"test_remove\", mmkv.MMKV_MULTI_PROCESS)\n\tkv.SetBool(true, \"bool\")\n\n\tfmt.Println(\"check exist = \", mmkv.CheckExist(\"test_remove\"))\n\tmmkv.RemoveStorage(\"test_remove\")\n\tfmt.Println(\"after remove, check exist = \", mmkv.CheckExist(\"test_remove\"))\n\tkv = mmkv.MMKVWithIDAndMode(\"test_remove\", mmkv.MMKV_MULTI_PROCESS)\n\tif kv.Count() != 0 {\n\t\tpanic(\"storage not successfully remove\")\n\t}\n\n\tkv = mmkv.MMKVWithIDAndMode(\"test_remove/sg\", mmkv.MMKV_SINGLE_PROCESS)\n\tkv.SetBool(true, \"bool\")\n\n\tmmkv.RemoveStorage(\"test_remove/sg\")\n\tkv = mmkv.MMKVWithIDAndMode(\"test_remove/sg\", mmkv.MMKV_SINGLE_PROCESS)\n\tif kv.Count() != 0 {\n\t\tpanic(\"storage not successfully remove\")\n\t}\n}\n\nfunc testReadOnly() {\n\tmmapID := \"testReadOnly\"\n\taesKey := \"ReadOnly+Key\"\n\t{\n\t\tkv := mmkv.MMKVWithIDAndModeAndCryptKey(mmapID, mmkv.MMKV_SINGLE_PROCESS, aesKey, false)\n\t\ttestMMKVImp(kv, false)\n\t\tkv.Close()\n\t}\n\tpath := \"/tmp/mmkv/\" + mmapID\n\tos.Chmod(path, 0444)\n\tcrcPath := path + \".crc\"\n\tos.Chmod(crcPath, 0444)\n\t{\n\t\tkv := mmkv.MMKVWithIDAndModeAndCryptKey(mmapID, (mmkv.MMKV_SINGLE_PROCESS | mmkv.MMKV_READ_ONLY), aesKey, false)\n\t\ttestMMKVImp(kv, true)\n\n\t\t// also check if it tolerate update operations without crash\n\t\ttestMMKVImp(kv, false)\n\n\t\tkv.Close()\n\t}\n\tos.Chmod(path, 0666)\n\tos.Chmod(crcPath, 0666)\n}\n\nfunc testNameSpace() {\n\tns := mmkv.GetNameSpace(\"/tmp/mmkv_namespace\")\n\tfmt.Println(\"NameSpace:\", ns.GetRootDir())\n\tkv := ns.MMKVWithID(\"test_namespace\")\n\ttestMMKVImp(kv, false)\n}\n\nfunc testImport() {\n\tmmapID := \"testImportSrc\"\n\tsrc := mmkv.MMKVWithID(mmapID)\n\tsrc.SetBool(true, \"bool\")\n\tsrc.SetInt32(-2147483648, \"int\")                   // math.MinInt32\n\tsrc.SetUInt64(uint64(9223372036854775807), \"long\") // math.MaxInt64\n\tsrc.SetString(\"test import\", \"string\")\n\n\tdst := mmkv.MMKVWithID(\"testImportDst\")\n\tdst.ClearAll()\n\tdst.EnableAutoKeyExpire(1)\n\tdst.SetBool(false, \"bool\")\n\tdst.SetInt32(-1, \"int\")          // math.MinInt32\n\tdst.SetUInt64(uint64(0), \"long\") // math.MaxInt64\n\tdst.SetString(mmapID, \"string\")\n\n\tcount := dst.ImportFrom(src)\n\tif count != 4 || dst.Count() != 4 {\n\t\tfmt.Println(\"MMKV: import check count fail\")\n\t}\n\tif !dst.GetBool(\"bool\") {\n\t\tfmt.Println(\"MMKV: import check bool fail\")\n\t}\n\tif dst.GetInt32(\"int\") != -2147483648 {\n\t\tfmt.Println(\"MMKV: import check int fail\")\n\t}\n\tif dst.GetUInt64(\"long\") != 9223372036854775807 {\n\t\tfmt.Println(\"MMKV: import check long fail\")\n\t}\n\tif dst.GetString(\"string\") != \"test import\" {\n\t\tfmt.Println(\"MMKV: import check string fail\")\n\t}\n\n\ttime.Sleep(2 * time.Second)\n\tif dst.CountNonExpiredKeys() != 0 {\n\t\tfmt.Println(\"MMKV: import check expire fail\")\n\t}\n}\n\n// myHandler implements mmkv.Handler with DefaultHandler for defaults\ntype myHandler struct {\n\tmmkv.DefaultHandler\n}\n\nfunc (h *myHandler) WantLogRedirect() bool { return true }\n\nfunc (h *myHandler) MMKVLog(level int, file string, line int, function string, message string) {\n\tvar levelStr string\n\tswitch level {\n\tcase mmkv.MMKVLogDebug:\n\t\tlevelStr = \"[D]\"\n\tcase mmkv.MMKVLogInfo:\n\t\tlevelStr = \"[I]\"\n\tcase mmkv.MMKVLogWarning:\n\t\tlevelStr = \"[W]\"\n\tcase mmkv.MMKVLogError:\n\t\tlevelStr = \"[E]\"\n\tdefault:\n\t\tlevelStr = \"[N]\"\n\t}\n\tfmt.Printf(\"Redirect %v <%v:%v::%v> %v\\n\", levelStr, file, line, function, message)\n}\n\nfunc (h *myHandler) OnMMKVCRCCheckFail(mmapID string) int {\n\tfmt.Println(mmapID, \"has error type: CRC-Error\")\n\treturn mmkv.OnErrorRecover\n}\n\nfunc (h *myHandler) OnMMKVFileLengthError(mmapID string) int {\n\tfmt.Println(mmapID, \"has error type: File-Length-Error\")\n\treturn mmkv.OnErrorRecover\n}\n\nfunc (h *myHandler) WantContentChangeNotification() bool { return true }\n\nfunc (h *myHandler) OnContentChangedByOuterProcess(mmapID string) {\n\tfmt.Println(mmapID, \"content changed by other process\")\n}\n\nfunc (h *myHandler) OnMMKVContentLoadSuccessfully(mmapID string) {\n\tfmt.Println(mmapID, \"content loaded successfully\")\n}\n"
  },
  {
    "path": "POSIX/src/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2019 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets the minimum version of CMake required to build the native library.\n\ncmake_minimum_required(VERSION 3.10.0)\nset(CMAKE_TRY_COMPILE_TARGET_TYPE \"STATIC_LIBRARY\")\n\nproject(mmkv)\n\nIF(APPLE)\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\nadd_subdirectory(../../Core Core)\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\nadd_library( # Sets the name of the library.\n             mmkv\n\n             # Sets the library as a shared library.\n             SHARED\n\n             # Provides a relative path to your source file(s).\n             libmmkv.cpp\n        )\n\ntarget_include_directories(mmkv PUBLIC\n        ${CMAKE_CURRENT_SOURCE_DIR})\n\n# Searches for a specified prebuilt library and stores the path as a\n# variable. Because CMake includes system libraries in the search path by\n# default, you only need to specify the name of the public NDK library\n# you want to add. CMake verifies that the library exists before\n# completing its build.\n\n#find_library( # Sets the name of the path variable.\n  #              log-lib\n\n              # Specifies the name of the NDK library that\n              # you want CMake to locate.\n              #            log\n              #)\n\nset_target_properties(mmkv PROPERTIES\n            CXX_STANDARD 20\n            CXX_EXTENSIONS OFF\n            )\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\ntarget_link_libraries( # Specifies the target library.\n                       mmkv\n\n                       # Links the target library to the log library\n                       # included in the NDK.\n                       #          ${log-lib}\n                       core\n        pthread\n        )\n"
  },
  {
    "path": "POSIX/src/libmmkv.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2019 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version: 6.2\nimport PackageDescription\n\nlet package = Package(\n    name: \"MMKV\",\n    platforms: [\n        .iOS(.v13), // Bumped to reasonable modern minimums\n        .macOS(.v10_15),\n        .watchOS(.v6),\n        .tvOS(.v13),\n        .visionOS(.v1)\n    ],\n    products: [\n        .library(name: \"MMKV\", targets: [\"MMKV\"]),\n        .library(name: \"MMKVAppExtension\", type: .dynamic, targets: [\"MMKVAppExtension\"]),\n        .library(name: \"MMKVCore\", type: .static, targets: [\"MMKVCore\"]),\n    ],\n    targets: [\n        // The Core C++ Logic\n        .target(\n            name: \"MMKVCore\",\n            dependencies: [\"MMKVCoreOpenSSL\"], // ASM target linked transitively\n            path: \"Core\",\n            exclude: [\n                \"aes/openssl\", // Moved to separate target\n                \"crc32/zlib\", // Assuming system zlib\n                \"CMakeFiles\",\n                \"CMakeLists.txt\",\n            ],\n            publicHeadersPath: \"fakeinclude\", // Cleanly exposes headers in Core/include\n            cSettings: [\n                .define(\"NDEBUG\", .when(configuration: .release))\n            ],\n            cxxSettings: [\n                .headerSearchPath(\"fakeinclude/MMKVCore\"), // Allow internal imports like #include \"MMKV.h\"\n                .headerSearchPath(\".\"),\n                .unsafeFlags([\"-x\", \"objective-c++\", \"-fno-objc-arc\"]) \n            ],\n            linkerSettings: [\n                .linkedLibrary(\"z\"),\n                .linkedLibrary(\"c++\")\n            ]\n        ),\n\n        // OpenSSL C++ Implementation\n        .target(\n            name: \"MMKVCoreOpenSSL\",\n            // dependencies: [\"MMKVCoreOpenSSLASM\"], // Link the assembly code\n            path: \"Core/aes/openssl\",\n            publicHeadersPath: \".\", // Expose these headers to Core\n            cxxSettings: [\n                // .headerSearchPath(\"../../include/MMKVCore\"),\n                .unsafeFlags([\"-fno-objc-arc\"])\n            ]\n        ),\n\n        // The iOS/ObjC Wrapper (The main target users import)\n        .target(\n            name: \"MMKV\",\n            dependencies: [\"MMKVCore\"],\n            path: \"iOS/MMKV/MMKV\",\n            exclude: [\n                \"MMKVAppExtension\",\n                \"MMKVWatchExtension\",\n            ],\n            resources: [\n                // NEW: Required for App Store submission in 2025\n                .copy(\"Resources/PrivacyInfo.xcprivacy\") \n            ],\n            publicHeadersPath: \"fakeinclude\",\n            cSettings: [\n                .define(\"MMKV_IOS_EXTENSION\", .when(platforms: [.watchOS]))\n            ],\n            cxxSettings: [\n                .headerSearchPath(\".\"),\n                // .headerSearchPath(\"../../../Core/include\"),\n                // Modern Swift 6.2 allows this without blocking dependencies\n                .unsafeFlags([\"-x\", \"objective-c++\", \"-fno-objc-arc\"])\n            ],\n            linkerSettings: [\n                .linkedLibrary(\"z\"),\n                .linkedLibrary(\"c++\")\n            ]\n        ),\n        // for App Extension\n        .target(\n            name: \"MMKVAppExtension\",\n            dependencies: [\"MMKVCore\"],\n            path: \"iOS/MMKV/MMKV/MMKVAppExtension\",\n            resources: [\n                // NEW: Required for App Store submission in 2025\n                .copy(\"../Resources/PrivacyInfo.xcprivacy\") \n            ],\n            publicHeadersPath: \"include\",\n            cSettings: [\n                .define(\"MMKV_IOS_EXTENSION\")\n            ],\n            cxxSettings: [\n                .headerSearchPath(\".\"),\n                .unsafeFlags([\"-x\", \"objective-c++\", \"-fno-objc-arc\", \"-fapplication-extension\"])\n            ],\n            linkerSettings: [\n                .linkedLibrary(\"z\"),\n                .linkedLibrary(\"c++\")\n            ]\n        ),\n    ],\n    cxxLanguageStandard: .cxx20\n)\n"
  },
  {
    "path": "Python/CMakeLists.txt",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2020 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets the minimum version of CMake required to build the native library.\n\ncmake_minimum_required(VERSION 3.10.0)\n\nproject(mmkv_python)\n\nIF(APPLE)\n    add_compile_definitions(FORCE_POSIX)\nENDIF()\n\nadd_subdirectory(../Core Core)\nadd_subdirectory(pybind11)\n\n\npybind11_add_module( # Sets the name of the library.\n             mmkv\n\n             # Sets the library as a python module library.\n             MODULE\n\n             # Provides a relative path to your source file(s).\n             libmmkv_python.cpp\n        )\n\ntarget_include_directories(mmkv PUBLIC\n        ${CMAKE_CURRENT_SOURCE_DIR})\n\nset_target_properties(mmkv PROPERTIES\n            CXX_STANDARD 17\n            CXX_EXTENSIONS OFF\n            )\n\ntarget_link_libraries( # Specifies the target library.\n                       mmkv PRIVATE\n                       core\n        )\n\nIF (NOT WIN32)\n    target_link_libraries(mmkv\n            PRIVATE\n            pthread)\nENDIF()\n"
  },
  {
    "path": "Python/README.md",
    "content": "# MMKV for Python (on POSIX & Windows)\nMMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on both **Android**, **iOS/macOS**, **Windows** and **Python/Golang (POSIX)**.\n\n## Building & API Docs\nCheckout the official wiki for instructions. [https://github.com/Tencent/MMKV/wiki/python_setup](https://github.com/Tencent/MMKV/wiki/python_setup)\n\n"
  },
  {
    "path": "Python/demo.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport sys\nimport mmkv\nimport time\nimport tempfile\nimport os\n\n\ndef functional_test(mmap_id, decode_only):\n    # pass MMKVMode.MultiProcess to get a multi-process instance\n    # kv = mmkv.MMKV('test_python', mmkv.MMKVMode.MultiProcess)\n    kv = mmkv.MMKV(mmap_id)\n    functional_test_imp(kv, decode_only)\n    return kv\n\n\ndef functional_test_imp(kv, decode_only):\n    if not decode_only:\n        kv.set(True, 'bool')\n    print('bool = ', kv.getBool('bool'))\n\n    if not decode_only:\n        kv.set(-1 * (2 ** 31), 'int32')\n    print('int32 = ', kv.getInt('int32'))\n\n    if not decode_only:\n        kv.set((2 ** 32) - 1, 'uint32')\n    print('uint32 = ', kv.getUInt('uint32'))\n\n    if not decode_only:\n        kv.set(2 ** 63, 'int64')\n    print('int64 = ', kv.getLongInt('int64'))\n\n    if not decode_only:\n        kv.set((2 ** 64) - 1, 'uint64')\n    print('uint64 = ', kv.getLongUInt('uint64'))\n\n    if not decode_only:\n        kv.set(3.1415926, 'float')\n    print('float = ', kv.getFloat('float'))\n\n    if not decode_only:\n        kv.set('Hello world, MMKV for Python!', 'string')\n    print('string = ', kv.getString('string'))\n\n    if not decode_only:\n        ls = range(0, 10)\n        kv.set(bytes(ls), 'bytes')\n    b = kv.getBytes('bytes')\n    print('raw bytes = ', b)\n    if sys.version_info >= (3, 0):\n        print('decoded bytes = ', list(b))\n\n    if not decode_only:\n        print('keys before remove:', sorted(kv.keys()))\n        kv.remove('bool')\n        print('\"bool\" exist after remove: ', ('bool' in kv))\n        kv.remove(['int32', 'float'])\n        print('keys after remove:', sorted(kv.keys()))\n    else:\n        print('keys:', sorted(kv.keys()))\n\n\ndef test_backup():\n    temp_dir = tempfile.gettempdir()\n    root_dir = temp_dir + \"/mmkv_backup\"\n    mmap_id = \"test_python\"\n    ret = mmkv.MMKV.backupOneToDirectory(mmap_id, root_dir)\n    print(\"backup one return: \", ret)\n\n    kv = mmkv.MMKV(\"test/Encrypt\", mmkv.MMKVMode.SingleProcess, \"cryptKey\")\n    kv.remove(\"test_restore_key\")\n\n    count = mmkv.MMKV.backupAllToDirectory(root_dir)\n    print(\"backup all count: \", count)\n\n\n# just for testing\ndef utf8len(s):\n    return len(s.encode('utf-8'))\n\n\ndef test_expected_capacity():\n    key = \"key0\"\n    value = \"🏊🏻®4️⃣🐅_\"\n    dataLen = 10000\n    for i in range(dataLen):\n        value += \"0\"\n\n    print(\"value size =\", utf8len(value))\n    expectedSize = utf8len(key) + utf8len(value)\n    # if we know exactly the sizes of key and value, set expectedCapacity for performance improvement\n    kv = mmkv.MMKV(\"mmkv_capacity0\", mmkv.MMKVMode.SingleProcess, \"\", \"\", expectedSize)\n    # 0 times expand\n    kv.set(value, key)\n    print(\"data size from MMKV =\", len(kv.getString(key)))\n\n    countTick = 10\n    expectedSize *= countTick\n    kv = mmkv.MMKV(\"mmkv_capacity1\", mmkv.MMKVMode.SingleProcess, \"\", \"\", expectedSize)\n    for i in range(countTick):\n        key1 = \"key\" + str(i)\n        # 0 times expand\n        kv.set(value, key1)\n\n\ndef test_restore():\n    temp_dir = tempfile.gettempdir()\n    root_dir = temp_dir + \"/mmkv_backup\"\n    mmap_id = \"test/Encrypt\"\n    aes_key = \"cryptKey\"\n    a_kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, aes_key)\n    a_kv.set(\"string value before restore\", \"test_restore_key\")\n    print(\"before restore [\", a_kv.mmapID(), \"] allKeys: \", a_kv.keys())\n\n    ret = mmkv.MMKV.restoreOneFromDirectory(mmap_id, root_dir)\n    print(\"restore one return: \", ret)\n    if ret:\n        print(\"after restore [\", a_kv.mmapID(), \"] allKeys: \", a_kv.keys())\n\n    count = mmkv.MMKV.restoreAllFromDirectory(root_dir)\n    print(\"restore all count: \", count)\n    if count > 0:\n        backup_mmkv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, aes_key)\n        print(\"check on restore [\", backup_mmkv.mmapID(), \"] allKeys: \", backup_mmkv.keys())\n\n        backup_mmkv = mmkv.MMKV(\"test_python\")\n        print(\"check on restore [\", backup_mmkv.mmapID(), \"] allKeys: \", backup_mmkv.keys())\n\n\ndef test_auto_expire():\n    kv = mmkv.MMKV(\"test_auto_expire\", enableKeyExpire = False)\n    kv.clearAll(True)\n    kv.disableAutoKeyExpire() # this call become a no-op\n\n    kv.set(True, \"auto_expire_key_1\")\n    kv.close()\n    kv = mmkv.MMKV(\"test_auto_expire\", enableKeyExpire = True, expiredInSeconds = 1)\n    kv.enableAutoKeyExpire(1) # this call become a no-op\n    kv.set(\"never_expire_value_1\", \"never_expire_key_1\", 0)\n\n    time.sleep(2)\n    print(\"contains auto_expire_key_1:\", \"auto_expire_key_1\" in kv)\n    print(\"contains never_expire_key_1:\", \"never_expire_key_1\" in kv)\n\n    kv.remove(\"never_expire_key_1\")\n    kv.enableAutoKeyExpire(0)\n    kv.set(\"never_expire_value_1\", \"never_expire_key_1\")\n    kv.set(True, \"auto_expire_key_1\", 1)\n    time.sleep(2)\n    print(\"contains never_expire_key_1:\", \"never_expire_key_1\" in kv)\n    print(\"contains auto_expire_key_1:\", \"auto_expire_key_1\" in kv)\n    print(\"count filter expire key:\", kv.count(True))\n    print(\"all non expire keys:\", kv.keys(True))\n\n\ndef test_compare_before_set():\n    kv = mmkv.MMKV(\"testCompareBeforeSet\")\n    kv.enableCompareBeforeSet()\n    kv.set(\"extraValue\", \"extraKey\")\n\n    key = \"bool\"\n    kv.set(True, key)\n    print(\"testCompareBeforeSet: bool value = \", kv.getBool(key))\n    actualSize1 = kv.actualSize()\n    print(\"testCompareBeforeSet: actualSize = \", actualSize1)\n    print(\"testCompareBeforeSet: bool value = \", kv.getBool(key))\n    kv.set(True, key)\n    actualSize2 = kv.actualSize()\n    print(\"testCompareBeforeSet: actualSize2 = \", actualSize2)\n    if actualSize1 != actualSize2:\n        raise (\"size not match\")\n\n    kv.set(False, key)\n    print(\"testCompareBeforeSet: bool value = \", kv.getBool(key))\n    if kv.getBool(key):\n        print(\"value not update\")\n\n    s1 = \"🏊🏻®hhh4️⃣🐅_yyy\"\n    s2 = \"0aA🏊🏻®hhh4️⃣🐅_zzz\"\n    key = \"string\"\n    kv.set(s1, key)\n    resultString = kv.getString(key)\n    print(\"testCompareBeforeSet: string = \", resultString)\n    actualSize1 = kv.actualSize()\n    print(\"testCompareBeforeSet: actualSize = \", actualSize1)\n    resultString = kv.getString(key)\n    print(\"testCompareBeforeSet: string = \", resultString)\n    kv.set(s1, key)\n    actualSize2 = kv.actualSize()\n    if actualSize1 != actualSize2:\n        print(\"size not match\")\n\n    kv.set(s2, key)\n    resultString = kv.getString(key)\n    print(\"testCompareBeforeSet: string = \", resultString)\n    if resultString != s2:\n        print(\"value not update\")\n\n    kv.disableCompareBeforeSet()\n\n\ndef test_remove_storage():\n    kv = mmkv.MMKV(\"test_remove\", mmkv.MMKVMode.MultiProcess)\n    kv.set(True, \"bool\")\n\n    print(\"check exist = \", mmkv.MMKV.checkExist(\"test_remove\"))\n    mmkv.MMKV.removeStorage(\"test_remove\")\n    print(\"after remove, check exist = \", mmkv.MMKV.checkExist(\"test_remove\"))\n    kv = mmkv.MMKV(\"test_remove\", mmkv.MMKVMode.MultiProcess)\n    if kv.count() != 0:\n        print(\"storage not successfully remove\")\n\n    temp_dir = tempfile.gettempdir()\n    rootDir = temp_dir + \"/dev/mmkv_sg\"\n    kv = mmkv.MMKV(\"test_remove/sg\", rootDir=rootDir)\n    kv.set(True, \"bool\")\n\n    print(\"check exist = \", mmkv.MMKV.checkExist(\"test_remove/sg\", rootDir=rootDir))\n    mmkv.MMKV.removeStorage(\"test_remove/sg\", rootDir=rootDir)\n    print(\"after remove, check exist = \", mmkv.MMKV.checkExist(\"test_remove/sg\", rootDir=rootDir))\n    kv = mmkv.MMKV(\"test_remove/sg\", rootDir=rootDir)\n    if kv.count() != 0:\n        print(\"storage not successfully remove\")\n\n\ndef test_read_only():\n    mmap_id = \"testReadOnly\"\n    aes_key = \"ReadOnly+Key\"\n\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, aes_key)\n    functional_test_imp(kv, False)\n    kv.close()\n\n    path = mmkv.MMKV.rootDir() + \"/\" + mmap_id\n    os.chmod(path, 0o444)\n    crc_path = path + \".crc\"\n    os.chmod(crc_path, 0o444)\n\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode(mmkv.MMKVMode.SingleProcess | mmkv.MMKVMode.ReadOnly), aes_key)\n    functional_test_imp(kv, True)\n    functional_test_imp(kv, False)\n    kv.close()\n\n    os.chmod(path, 0o666)\n    os.chmod(crc_path, 0o666)\n\n\ndef test_import():\n    mmap_id = \"testImportSrc\"\n    src = mmkv.MMKV(mmap_id)\n    src.set(True, \"bool\")\n    src.set(-2147483648, \"int\")  # Integer.MIN_VALUE\n    src.set(9223372036854775807, \"long\")  # Long.MAX_VALUE\n    src.set(\"test import\", \"string\")\n\n    dst = mmkv.MMKV(\"testImportDst\")\n    dst.clearAll()\n    dst.enableAutoKeyExpire(1)\n    dst.set(False, \"bool\")\n    dst.set(-1, \"int\")  # Integer.MIN_VALUE\n    dst.set(0, \"long\")  # Long.MAX_VALUE\n    dst.set(mmap_id, \"string\")\n\n    count = dst.importFrom(src)\n    if count != 4 or dst.count() != 4:\n        print(\"MMKV: import check count fail\")\n    if not dst.getBool(\"bool\"):\n        print(\"MMKV: import check bool fail:\", dst.getBool(\"bool\"))\n    if dst.getInt(\"int\") != -2147483648:\n        print(\"MMKV: import check int fail:\", dst.getInt(\"int\"))\n    if dst.getLongUInt(\"long\") != 9223372036854775807:\n        print(\"MMKV: import check long fail\", dst.getLongUInt(\"long\"))\n    if dst.getString(\"string\") != \"test import\":\n        print(\"MMKV: import check string fail\", dst.getString(\"string\"))\n\n    time.sleep(2)  # Sleep for 2 seconds\n    if dst.count(True) != 0:\n        print(\"MMKV: import check expire fail\")\n\n\ndef test_namespace():\n    root_dir = tempfile.gettempdir() + \"/dev/mmkv_namespace\"\n    ns = mmkv.MMKV.nameSpace(root_dir)\n    print(\"NameSpace: [%s]\" % ns.rootDir())\n    kv = ns.mmkvWithID(\"test_namespace\")\n    functional_test_imp(kv, False)\n\ndef test_rekey():\n    mmap_id = \"test/AES_reKey1\"\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess)\n    functional_test_imp(kv, False)\n\n    crypt_key = \"Key_seq_1\"\n    kv.reKey(crypt_key)\n    kv.clearMemoryCache()\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, crypt_key)\n    functional_test_imp(kv, True)\n\n    crypt_key2 = \"Key_Seq_Very_Looooooooong\"\n    kv.reKey(crypt_key2, True)\n    kv.clearMemoryCache()\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess, crypt_key2, aes256 = True)\n    functional_test_imp(kv, True)\n\n    kv.reKey(\"\")\n    kv.clearMemoryCache()\n    kv = mmkv.MMKV(mmap_id, mmkv.MMKVMode.SingleProcess)\n    functional_test_imp(kv, True)\n\ndef logger(log_level, file, line, function, message):\n    level = {mmkv.MMKVLogLevel.NoLog: 'N',\n             mmkv.MMKVLogLevel.Debug: 'D',\n             mmkv.MMKVLogLevel.Info: 'I',\n             mmkv.MMKVLogLevel.Warning: 'W',\n             mmkv.MMKVLogLevel.Error: 'E'}\n    print('r-[{0}] <{1}:{2}:{3}> {4}'.format(level[log_level], file, line, function, message))\n\n\ndef error_handler(mmap_id, error_type):\n    print('[{}] has error {}'.format(mmap_id, error_type))\n    return mmkv.MMKVErrorType.OnErrorRecover\n\n\ndef content_change_handler(mmap_id):\n    print(\"[%s]'s content has been changed by other process\" % mmap_id)\n\n\nif __name__ == '__main__':\n    # test NameSpace before MMKV.initializeMMKV()\n    test_namespace()\n\n    temp_dir = tempfile.gettempdir()\n    root_dir = temp_dir + '/mmkv'\n    print(\"root dir:\", root_dir)\n\n    # you can enable logging & log handler\n    # mmkv.MMKV.initializeMMKV(root_dir, mmkv.MMKVLogLevel.Info, logger)\n    mmkv.MMKV.initializeMMKV(root_dir)\n\n    # redirect logging\n    # mmkv.MMKV.registerLogHandler(logger)\n\n    # try recover on error\n    # mmkv.MMKV.registerErrorHandler(error_handler)\n\n    # get notified after content changed by other process\n    # mmkv.MMKV.registerContentChangeHandler(content_change_handler)\n\n    test_expected_capacity()\n\n    functional_test('test_python', False)\n\n    test_backup()\n\n    test_restore()\n\n    test_auto_expire()\n    test_compare_before_set()\n    test_remove_storage()\n    test_read_only()\n    test_import()\n    test_rekey()\n\n    # mmkv.MMKV.unRegisterLogHandler()\n    # mmkv.MMKV.unRegisterErrorHandler()\n    # mmkv.MMKV.unRegisterContentChangeHandler()\n    mmkv.MMKV.onExit()\n"
  },
  {
    "path": "Python/libmmkv_python.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKV.h>\n#include <pybind11/functional.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#ifdef MMKV_WIN32\n#include <basetsd.h>\ntypedef SSIZE_T ssize_t;\n#endif\n\nusing namespace mmkv;\nusing namespace std;\nnamespace py = pybind11;\n\nstatic MMKVConfig pyArgsToConfig(MMKVMode mode, const string &cryptKey, const MMKVPath_t &rootDir,\n        size_t expectedCapacity, bool aes256, std::optional<bool> enableKeyExpire,\n        uint32_t expiredInSeconds, bool enableCompareBeforeSet,\n        std::optional<MMKVRecoverStrategic> recover, uint32_t itemSizeLimit) {\n    MMKVConfig config;\n    config.mode = mode;\n    config.aes256 = aes256;\n    config.cryptKey = (cryptKey.length() > 0) ? (string *) &cryptKey : nullptr;\n    config.rootPath = (rootDir.length() > 0) ? (MMKVPath_t *) &rootDir : nullptr;\n    config.expectedCapacity = expectedCapacity;\n    config.enableKeyExpire = enableKeyExpire;\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    config.recover = recover;\n    config.itemSizeLimit = itemSizeLimit;\n    return config;\n}\n\nstatic MMBuffer pyBytes2MMBuffer(const py::bytes &bytes) {\n    char *buffer = nullptr;\n    ssize_t length = 0;\n    if (PYBIND11_BYTES_AS_STRING_AND_SIZE(bytes.ptr(), &buffer, &length) == 0) {\n        return {buffer, static_cast<size_t>(length), MMBufferNoCopy};\n    }\n    return MMBuffer(0);\n}\n\nstatic function<void(MMKVLogLevel level, const char *file, int line, const char *function, const string &message)>\n    g_logHandler = nullptr;\nstatic function<MMKVRecoverStrategic(const string &mmapID, MMKVErrorType errorType)> g_errorHandler = nullptr;\nstatic function<void(const string &mmapID)> g_contentHandler = nullptr;\nstatic function<void(const string &mmapID)> g_contentLoadedHandler = nullptr;\n\n// C++ handler that bridges to Python callbacks\nclass PyMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message) override {\n        if (g_logHandler) {\n            g_logHandler(level, file, line, function, message);\n        }\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (g_errorHandler) {\n            return g_errorHandler(mmapID, MMKVErrorType::MMKVCRCCheckFail);\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (g_errorHandler) {\n            return g_errorHandler(mmapID, MMKVErrorType::MMKVFileLength);\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (g_contentHandler) {\n            g_contentHandler(mmapID);\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if (g_contentLoadedHandler) {\n            g_contentLoadedHandler(mmapID);\n        }\n    }\n};\n\nstatic PyMMKVHandler g_pyHandler;\n\nPYBIND11_MODULE(mmkv, m) {\n    m.doc() = \"An efficient, small key-value storage framework developed by WeChat Team.\";\n\n    py::enum_<MMKVMode>(m, \"MMKVMode\", py::arithmetic())\n        .value(\"SingleProcess\", MMKVMode::MMKV_SINGLE_PROCESS)\n        .value(\"MultiProcess\", MMKVMode::MMKV_MULTI_PROCESS)\n        .value(\"ReadOnly\", MMKVMode::MMKV_READ_ONLY)\n        .export_values();\n\n    py::enum_<MMKVLogLevel>(m, \"MMKVLogLevel\")\n        .value(\"NoLog\", MMKVLogLevel::MMKVLogNone)\n        .value(\"Debug\", MMKVLogLevel::MMKVLogDebug)\n        .value(\"Info\", MMKVLogLevel::MMKVLogInfo)\n        .value(\"Warning\", MMKVLogLevel::MMKVLogWarning)\n        .value(\"Error\", MMKVLogLevel::MMKVLogError)\n        .export_values();\n\n    py::enum_<SyncFlag>(m, \"SyncFlag\")\n        .value(\"Sync\", SyncFlag::MMKV_SYNC)\n        .value(\"ASync\", SyncFlag::MMKV_ASYNC)\n        .export_values();\n\n    py::enum_<MMKVRecoverStrategic>(m, \"MMKVRecoverStrategic\")\n        .value(\"OnErrorDiscard\", MMKVRecoverStrategic::OnErrorDiscard)\n        .value(\"OnErrorRecover\", MMKVRecoverStrategic::OnErrorRecover)\n        .export_values();\n\n    py::enum_<MMKVErrorType>(m, \"MMKVErrorType\")\n        .value(\"CRCCheckFail\", MMKVErrorType::MMKVCRCCheckFail)\n        .value(\"FileLength\", MMKVErrorType::MMKVFileLength)\n        .export_values();\n\n    py::class_<NameSpace, unique_ptr<NameSpace>> clsNameSpace(m, \"NameSpace\");\n\n    clsNameSpace.def(\"mmkvWithID\", [](NameSpace &self, const string &mmapID, MMKVMode mode, const string &cryptKey,\n                const size_t expectedCapacity, bool aes256, std::optional<bool> enableKeyExpire,\n                uint32_t expiredInSeconds, bool enableCompareBeforeSet,std::optional<MMKVRecoverStrategic> recover,\n                uint32_t itemSizeLimit) {\n                    auto config = pyArgsToConfig(mode, cryptKey, self.getRootDir(), expectedCapacity, aes256,\n                        enableKeyExpire, expiredInSeconds, enableCompareBeforeSet, recover, itemSizeLimit);\n                    return MMKV::mmkvWithID(mmapID, config);\n                },\n                \"Parameters:\\n\"\n                \"  mmapID: all instances of the same mmapID share the same data and file storage\\n\"\n                \"  mode: pass MMKVMode.MultiProcess for a multi-process MMKV\\n\"\n                \"  cryptKey: pass a non-empty string for an encrypted MMKV, 32 bytes at most\\n\"\n                \"  expectedCapacity: the file size you expected when opening or creating file\\n\"\n                \"  aes256: use AES 256 key length\\n\"\n                \"  enableKeyExpire: enable auto key expiration\\n\"\n                \"  expiredInSeconds: expiration in seconds\\n\"\n                \"  enableCompareBeforeSet: enable compare before set, if new value is the same, dont update/insert\\n\"\n                \"  recover: recover strategy on file corruption\\n\"\n                \"  itemSizeLimit: size limit for a key-value pair\\n\",\n                py::arg(\"mmapID\"), py::arg(\"mode\") = MMKV_SINGLE_PROCESS, py::arg(\"cryptKey\") = string(),\n                py::arg(\"expectedCapacity\") = 0, py::arg(\"aes256\") = false,\n                py::arg(\"enableKeyExpire\") = std::nullopt, py::arg(\"expiredInSeconds\") = 0,\n                py::arg(\"enableCompareBeforeSet\") = false,\n                py::arg(\"recover\") = std::nullopt,\n                py::arg(\"itemSizeLimit\") = 0\n    );\n\n    clsNameSpace.def(\"rootDir\", &NameSpace::getRootDir, \"get the root directory of NameSpace\");\n\n    clsNameSpace.def(\"backupOneToDirectory\", &NameSpace::backupOneToDirectory,\n        \"backup one MMKV instance from the root dir of NameSpace to dstDir\",\n        py::arg(\"mmapID\"), py::arg(\"dstDir\"));\n\n    clsNameSpace.def(\"restoreOneFromDirectory\", &NameSpace::restoreOneFromDirectory,\n        \"restore one MMKV instance from srcDir to dstDir to the root dir of NameSpace\",\n        py::arg(\"mmapID\"), py::arg(\"srcDir\"));\n\n    clsNameSpace.def(\"backupAllToDirectory\", &NameSpace::backupAllToDirectory,\n        \"backup all MMKV instance from the root dir of NameSpace to dstDir\", py::arg(\"dstDir\"));\n\n    clsNameSpace.def(\"restoreAllFromDirectory\", &NameSpace::restoreAllFromDirectory,\n        \"restore all MMKV instance from srcDir to the root dir of NameSpace\", py::arg(\"srcDir\"));\n\n    clsNameSpace.def(\"removeStorage\", &NameSpace::removeStorage);\n    clsNameSpace.def(\"isFileValid\", &NameSpace::isFileValid);\n    clsNameSpace.def(\"checkExist\", &NameSpace::checkExist);\n\n    py::class_<MMKV, unique_ptr<MMKV, py::nodelete>> clsMMKV(m, \"MMKV\");\n\n    // TODO: not working\n    // clsMMKV.def(py::init(&MMKV::mmkvWithID),\n    //             py::arg(\"mmapID\"),\n    //             py::arg(\"mode\") = MMKV_SINGLE_PROCESS,\n    //             py::arg(\"cryptKey\") = (string*) nullptr,\n    //             py::arg(\"rootDir\") = (string*) nullptr);\n\n    clsMMKV.def(py::init([](const string &mmapID, MMKVMode mode, const string &cryptKey, const MMKVPath_t &rootDir,\n                const size_t expectedCapacity, bool aes256, std::optional<bool> enableKeyExpire,\n                uint32_t expiredInSeconds, bool enableCompareBeforeSet,std::optional<MMKVRecoverStrategic> recover,\n                uint32_t itemSizeLimit) {\n                    auto config = pyArgsToConfig(mode, cryptKey, rootDir, expectedCapacity, aes256,\n                        enableKeyExpire, expiredInSeconds, enableCompareBeforeSet, recover, itemSizeLimit);\n                    return MMKV::mmkvWithID(mmapID, config);\n                }),\n                \"Parameters:\\n\"\n                \"  mmapID: all instances of the same mmapID share the same data and file storage\\n\"\n                \"  mode: pass MMKVMode.MultiProcess for a multi-process MMKV\\n\"\n                \"  cryptKey: pass a non-empty string for an encrypted MMKV, 32 bytes at most\\n\"\n                \"  rootDir: custom root directory\\n\"\n                \"  expectedCapacity: the file size you expected when opening or creating file\\n\"\n                \"  aes256: use AES 256 key length\\n\"\n                \"  enableKeyExpire: enable auto key expiration\\n\"\n                \"  expiredInSeconds: expiration in seconds\\n\"\n                \"  enableCompareBeforeSet: enable compare before set, if new value is the same, dont update/insert\\n\"\n                \"  recover: recover strategy on file corruption\\n\"\n                \"  itemSizeLimit: size limit for a key-value pair\\n\",\n                py::arg(\"mmapID\"), py::arg(\"mode\") = MMKV_SINGLE_PROCESS,\n                py::arg(\"cryptKey\") = string(), py::arg(\"rootDir\") = string(),\n                py::arg(\"expectedCapacity\") = 0, py::arg(\"aes256\") = false,\n                py::arg(\"enableKeyExpire\") = std::nullopt, py::arg(\"expiredInSeconds\") = 0,\n                py::arg(\"enableCompareBeforeSet\") = false,\n                py::arg(\"recover\") = std::nullopt,\n                py::arg(\"itemSizeLimit\") = 0);\n\n    clsMMKV.def(\"__eq__\", [](MMKV &kv, const MMKV &other) { return kv.mmapID() == other.mmapID(); });\n\n    clsMMKV.def_static(\n        \"initializeMMKV\",\n        [](const MMKVPath_t &rootDir, MMKVLogLevel logLevel, decltype(g_logHandler) logHandler) {\n            if (logHandler) {\n                g_logHandler = std::move(logHandler);\n                MMKV::initializeMMKV(rootDir, logLevel, &g_pyHandler);\n            } else {\n                MMKV::initializeMMKV(rootDir, logLevel, nullptr);\n            }\n        },\n        \"must call this before getting any MMKV instance\", py::arg(\"rootDir\"), py::arg(\"logLevel\") = MMKVLogNone,\n        py::arg(\"log_handler\") = nullptr);\n\n    clsMMKV.def_static(\n        \"defaultMMKV\",\n        [](MMKVMode mode, const string &cryptKey, bool aes256, size_t expectedCapacity,\n            std::optional<bool> enableKeyExpire, uint32_t expiredInSeconds, bool enableCompareBeforeSet,\n            std::optional<MMKVRecoverStrategic> recover, uint32_t itemSizeLimit) {\n            auto config = pyArgsToConfig(mode, cryptKey, string(), expectedCapacity, aes256,\n                enableKeyExpire, expiredInSeconds, enableCompareBeforeSet, recover, itemSizeLimit);\n            return MMKV::defaultMMKV(config);\n        },\n        \"a generic purpose instance\\n\"\n        \"Parameters:\\n\"\n        \"  mode: pass MMKVMode.MultiProcess for a multi-process MMKV\\n\"\n        \"  cryptKey: pass a non-empty string for an encrypted MMKV, 32 bytes at most\\n\"\n        \"  aes256: use AES 256 key length\\n\"\n        \"  expectedCapacity: the file size you expected when opening or creating file\\n\"\n        \"  enableKeyExpire: enable auto key expiration\\n\"\n        \"  expiredInSeconds: expiration in seconds\\n\"\n        \"  enableCompareBeforeSet: enable compare before set, if new value is the same, dont update/insert\\n\"\n        \"  recover: recover strategy on file corruption\\n\"\n        \"  itemSizeLimit: size limit for a key-value pair\\n\",\n        py::arg(\"mode\") = MMKV_SINGLE_PROCESS, py::arg(\"cryptKey\") = string(),\n        py::arg(\"aes256\") = false, py::arg(\"expectedCapacity\") = 0,\n        py::arg(\"enableKeyExpire\") = std::nullopt, py::arg(\"expiredInSeconds\") = 0,\n        py::arg(\"enableCompareBeforeSet\") = false,\n        py::arg(\"recover\") = std::nullopt,\n        py::arg(\"itemSizeLimit\") = 0);\n\n    clsMMKV.def_static(\"nameSpace\", &MMKV::nameSpace, \"get a namespace with custom root dir\");\n    clsMMKV.def_static(\"defaultNameSpace\", &MMKV::defaultNameSpace, \"identical with the original MMKV with the global root dir\");\n\n    clsMMKV.def(\"mmapID\", &MMKV::mmapID);\n    clsMMKV.def(\"isInterProcess\", &MMKV::isMultiProcess);\n\n    clsMMKV.def(\"cryptKey\", &MMKV::cryptKey);\n    clsMMKV.def(\"reKey\", &MMKV::reKey,\n                \"transform plain text into encrypted text, or vice versa with an empty cryptKey\\n\"\n                \"Parameters:\\n\"\n                \"  newCryptKey: 32 bytes at most\\n\"\n                \"  aes256: use AES 256 key length\",\n                py::arg(\"newCryptKey\"), py::arg(\"aes256\") = false);\n    clsMMKV.def(\"checkReSetCryptKey\", &MMKV::checkReSetCryptKey,\n                \"just reset cryptKey (will not encrypt or decrypt anything),\\n\"\n                \"usually you should call this method after other process reKey() a multi-process mmkv\",\n                py::arg(\"newCryptKey\"), py::arg(\"aes256\") = false);\n\n    // TODO: Doesn't work, why?\n    // clsMMKV.def(\"set\", py::overload_cast<bool, const string&>(&MMKV::set), py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(bool, string_view))(&MMKV::set), \"encode a boolean value\", py::arg(\"value\"),\n                py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(bool, string_view, uint32_t))(&MMKV::set),\n                \"encode a boolean value with expiration\", py::arg(\"value\"), py::arg(\"key\"), py::arg(\"expireDuration\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(int32_t, string_view))(&MMKV::set), \"encode an int32 value\", py::arg(\"value\"),\n                py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(int32_t, string_view, uint32_t))(&MMKV::set),\n                \"encode an int32 value with expiration\", py::arg(\"value\"), py::arg(\"key\"), py::arg(\"expireDuration\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(uint32_t, string_view))(&MMKV::set), \"encode an unsigned int32 value\",\n                py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(uint32_t, string_view, uint32_t))(&MMKV::set),\n                \"encode an unsigned int32 value with expiration\", py::arg(\"value\"), py::arg(\"key\"),\n                py::arg(\"expireDuration\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(int64_t, string_view))(&MMKV::set), \"encode an int64 value\", py::arg(\"value\"),\n                py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(int64_t, string_view, uint32_t))(&MMKV::set),\n                \"encode an int64 value with expiration\", py::arg(\"value\"), py::arg(\"key\"), py::arg(\"expireDuration\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(uint64_t, string_view))(&MMKV::set), \"encode an unsigned int64 value\",\n                py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(uint64_t, string_view, uint32_t))(&MMKV::set),\n                \"encode an unsigned int64 value with expiration\", py::arg(\"value\"), py::arg(\"key\"),\n                py::arg(\"expireDuration\"));\n    //clsMMKV.def(\"set\", (bool (MMKV::*)(float, string_view))(&MMKV::set), py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(double, string_view))(&MMKV::set), \"encode a float/double value\",\n                py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(double, string_view, uint32_t))(&MMKV::set),\n                \"encode a float/double value with expiration\", py::arg(\"value\"), py::arg(\"key\"),\n                py::arg(\"expireDuration\"));\n    //clsMMKV.def(\"set\", (bool (MMKV::*)(const char*, string_view))(&MMKV::set), py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(const string &, string_view))(&MMKV::set),\n                \"encode an UTF-8 String/bytes value\", py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\"set\", (bool (MMKV::*)(const string &, string_view, uint32_t))(&MMKV::set),\n                \"encode an UTF-8 String/bytes value with expiration\", py::arg(\"value\"), py::arg(\"key\"),\n                py::arg(\"expireDuration\"));\n#if PY_MAJOR_VERSION >= 3\n    clsMMKV.def(\n        \"set\", [](MMKV &kv, const py::bytes &value, const string &key) { return kv.set(pyBytes2MMBuffer(value), key); },\n        \"encode a bytes value\", py::arg(\"value\"), py::arg(\"key\"));\n    clsMMKV.def(\n        \"set\",\n        [](MMKV &kv, const py::bytes &value, const string &key, uint32_t expireDuration) {\n            return kv.set(pyBytes2MMBuffer(value), key, expireDuration);\n        },\n        \"encode a bytes value with expiration\", py::arg(\"value\"), py::arg(\"key\"), py::arg(\"expireDuration\"));\n#endif\n\n    clsMMKV.def(\"getBool\", &MMKV::getBool, \"decode a boolean value\", py::arg(\"key\"), py::arg(\"defaultValue\") = false,\n                py::arg(\"hasValue\") = nullptr);\n    clsMMKV.def(\"getInt\", &MMKV::getInt32, \"decode an int32 value\", py::arg(\"key\"), py::arg(\"defaultValue\") = 0,\n                py::arg(\"hasValue\") = nullptr);\n    clsMMKV.def(\"getUInt\", &MMKV::getUInt32, \"decode an unsigned int32 value\", py::arg(\"key\"),\n                py::arg(\"defaultValue\") = 0, py::arg(\"hasValue\") = nullptr);\n    clsMMKV.def(\"getLongInt\", &MMKV::getInt64, \"decode an int64 value\", py::arg(\"key\"), py::arg(\"defaultValue\") = 0,\n                py::arg(\"hasValue\") = nullptr);\n    clsMMKV.def(\"getLongUInt\", &MMKV::getUInt64, \"decode an unsigned int64 value\", py::arg(\"key\"),\n                py::arg(\"defaultValue\") = 0, py::arg(\"hasValue\") = nullptr);\n    //clsMMKV.def(\"getFloat\", &MMKV::getFloat, py::arg(\"key\"), py::arg(\"defaultValue\") = 0);\n    clsMMKV.def(\"getFloat\", &MMKV::getDouble, \"decode a float/double value\", py::arg(\"key\"),\n                py::arg(\"defaultValue\") = 0, py::arg(\"hasValue\") = nullptr);\n    clsMMKV.def(\n        \"getString\",\n        [](MMKV &kv, const string &key, const string &defaultValue) {\n            string result;\n            if (kv.getString(key, result)) {\n                return result;\n            }\n            return defaultValue;\n        },\n        \"decode an UTF-8 String/bytes value\", py::arg(\"key\"), py::arg(\"defaultValue\") = string());\n\n    clsMMKV.def(\n        \"getBytes\",\n        [](MMKV &kv, const string &key, const py::bytes &defaultValue) {\n            MMBuffer result = kv.getBytes(key);\n            if (result.length() > 0) {\n                return py::bytes((const char *) result.getPtr(), result.length());\n            }\n            return defaultValue;\n        },\n        \"decode a bytes value\", py::arg(\"key\"), py::arg(\"defaultValue\") = py::bytes());\n\n    clsMMKV.def(\"__contains__\", &MMKV::containsKey, py::arg(\"key\"));\n    clsMMKV.def(\"keys\", &MMKV::allKeys, py::arg(\"filterExpire\") = false);\n\n    clsMMKV.def(\"count\", &MMKV::count, py::arg(\"filterExpire\") = false);\n    clsMMKV.def(\"totalSize\", &MMKV::totalSize);\n    clsMMKV.def(\"actualSize\", &MMKV::actualSize);\n\n    clsMMKV.def(\"remove\", &MMKV::removeValueForKey, py::arg(\"key\"));\n    clsMMKV.def(\"remove\", &MMKV::removeValuesForKeys, py::arg(\"keys\"));\n    clsMMKV.def(\"clearAll\", &MMKV::clearAll, py::arg(\"keepSpace\") = false, \"remove all key-values\");\n    clsMMKV.def(\"trim\", &MMKV::trim, \"call this method after lots of removing if you care about disk usage\");\n    clsMMKV.def(\"importFrom\", &MMKV::importFrom, \"import all key-value items from others\");\n    clsMMKV.def(\"clearMemoryCache\", &MMKV::clearMemoryCache, \"call this method if you are facing memory-warning\",\n        py::arg(\"keepSpace\") = false);\n\n    clsMMKV.def(\"sync\", &MMKV::sync, py::arg(\"flag\") = MMKV_SYNC,\n                \"this call is not necessary unless you worry about unexpected shutdown of the machine (running out of \"\n                \"battery, etc.)\");\n\n    clsMMKV.def(\"enableAutoKeyExpire\", &MMKV::enableAutoKeyExpire, py::arg(\"expireDurationInSecond\"),\n                \"turn on auto key expiration, passing 0 means never expire\");\n    clsMMKV.def(\"disableAutoKeyExpire\", &MMKV::disableAutoKeyExpire, \"turn off auto key expiration\");\n\n    clsMMKV.def(\"enableCompareBeforeSet\", &MMKV::enableCompareBeforeSet, \"turn on compare before set/update\");\n    clsMMKV.def(\"disableCompareBeforeSet\", &MMKV::disableCompareBeforeSet, \"turn off compare before set/update\");\n\n    clsMMKV.def(\"lock\", &MMKV::lock, \"get exclusive access, won't return until the lock is obtained\");\n    clsMMKV.def(\"unlock\", &MMKV::unlock);\n    clsMMKV.def(\"try_lock\", &MMKV::try_lock, \"try to get exclusive access\");\n\n    clsMMKV.def(\"isMultiProcess\", &MMKV::isMultiProcess, \"check multi-process mode\");\n    clsMMKV.def(\"isReadOnly\", &MMKV::isReadOnly, \"check read-only mode\");\n\n    clsMMKV.def(\"close\", &MMKV::close, \"close the instance\");\n\n    clsMMKV.def_static(\"rootDir\", &MMKV::getRootDir, \"get the root directory of MMKV\");\n\n    // unified callback handler\n    clsMMKV.def(\"checkContentChanged\", &MMKV::checkContentChanged, \"check if content been changed by other process\");\n    clsMMKV.def_static(\n        \"registerHandler\",\n        [](decltype(g_logHandler) logHandler,\n           decltype(g_errorHandler) errorHandler,\n           decltype(g_contentHandler) contentChangeHandler,\n           decltype(g_contentLoadedHandler) contentLoadedHandler) {\n            g_logHandler = logHandler ? std::move(logHandler) : nullptr;\n            g_errorHandler = errorHandler ? std::move(errorHandler) : nullptr;\n            g_contentHandler = contentChangeHandler ? std::move(contentChangeHandler) : nullptr;\n            g_contentLoadedHandler = contentLoadedHandler ? std::move(contentLoadedHandler) : nullptr;\n            MMKV::registerHandler(&g_pyHandler);\n        },\n        \"register a unified callback handler for MMKV,\\n\"\n        \"must call MMKV.unRegisterHandler() or MMKV.onExit() before exit\\n\"\n        \"Parameters:\\n\"\n        \"  log_handler: (logLevel: mmkv.MMKVLogLevel, file: str, line: int, function: str, message: str) -> None\\n\"\n        \"  error_handler: (mmapID: str, errorType: mmkv.MMKVErrorType) -> mmkv.MMKVRecoverStrategic\\n\"\n        \"  content_change_handler: (mmapID: str) -> None\\n\"\n        \"  content_loaded_handler: (mmapID: str) -> None\",\n        py::arg(\"log_handler\") = nullptr,\n        py::arg(\"error_handler\") = nullptr,\n        py::arg(\"content_change_handler\") = nullptr,\n        py::arg(\"content_loaded_handler\") = nullptr);\n    clsMMKV.def_static(\n        \"unRegisterHandler\",\n        [] {\n            g_logHandler = nullptr;\n            g_errorHandler = nullptr;\n            g_contentHandler = nullptr;\n            g_contentLoadedHandler = nullptr;\n            MMKV::unRegisterHandler();\n        },\n        \"If you have registered a handler, you must call this method or MMKV.onExit() before exit. \"\n        \"Otherwise your app/script won't exit properly.\");\n\n    clsMMKV.def_static(\n        \"onExit\",\n        [] {\n            MMKV::onExit();\n            g_logHandler = nullptr;\n            g_errorHandler = nullptr;\n            g_contentHandler = nullptr;\n            g_contentLoadedHandler = nullptr;\n        },\n        \"call this method before exit, especially if you have registered any callback handlers\");\n\n    clsMMKV.def_static(\n        \"backupOneToDirectory\",\n        [](const string &mmapID, const MMKVPath_t &dstDir, const MMKVPath_t &srcDir) {\n            MMKVPath_t *srcDirPtr = (!srcDir.empty()) ? (MMKVPath_t *) &srcDir : nullptr;\n            return MMKV::backupOneToDirectory(mmapID, dstDir, srcDirPtr);\n        },\n        \"backup one MMKV instance from srcDir (default to the root dir of MMKV) to dstDir\", py::arg(\"mmapID\"),\n        py::arg(\"dstDir\"), py::arg(\"srcDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\n        \"restoreOneFromDirectory\",\n        [](const string &mmapID, const MMKVPath_t &srcDir, const MMKVPath_t &dstDir) {\n            MMKVPath_t *dstDirPtr = (!dstDir.empty()) ? (MMKVPath_t *) &dstDir : nullptr;\n            return MMKV::restoreOneFromDirectory(mmapID, srcDir, dstDirPtr);\n        },\n        \"restore one MMKV instance from srcDir to dstDir (default to the root dir of MMKV)\", py::arg(\"mmapID\"),\n        py::arg(\"srcDir\"), py::arg(\"dstDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\n        \"backupAllToDirectory\",\n        [](const MMKVPath_t &dstDir, const MMKVPath_t &srcDir) {\n            MMKVPath_t *srcDirPtr = (!srcDir.empty()) ? (MMKVPath_t *) &srcDir : nullptr;\n            return MMKV::backupAllToDirectory(dstDir, srcDirPtr);\n        },\n        \"backup all MMKV instance from srcDir (default to the root dir of MMKV) to dstDir\", py::arg(\"dstDir\"),\n        py::arg(\"srcDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\n        \"restoreAllFromDirectory\",\n        [](const MMKVPath_t &srcDir, const MMKVPath_t &dstDir) {\n            MMKVPath_t *dstDirPtr = (!dstDir.empty()) ? (MMKVPath_t *) &dstDir : nullptr;\n            return MMKV::restoreAllFromDirectory(srcDir, dstDirPtr);\n        },\n        \"restore all MMKV instance from srcDir to dstDir (default to the root dir of MMKV)\", py::arg(\"srcDir\"),\n        py::arg(\"dstDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\n        \"removeStorage\",\n        [](const string &mmapID, const MMKVPath_t &rootDir) {\n            MMKVPath_t *rootDirPtr = (!rootDir.empty()) ? (MMKVPath_t *) &rootDir : nullptr;\n            return MMKV::removeStorage(mmapID, rootDirPtr);\n        },\n        \"remove the storage of the MMKV, including the data file & meta file (.crc)\", py::arg(\"mmapID\"),\n        py::arg(\"rootDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\"isFileValid\",\n       [](const string &mmapID, const MMKVPath_t &rootDir) {\n           MMKVPath_t *rootDirPtr = (!rootDir.empty()) ? (MMKVPath_t *) &rootDir : nullptr;\n           return MMKV::isFileValid(mmapID, rootDirPtr);\n       },\n       \"detect if the MMKV file is valid or not\", py::arg(\"mmapID\"),\n       py::arg(\"rootDir\") = MMKVPath_t());\n\n    clsMMKV.def_static(\"checkExist\",\n                       [](const string &mmapID, const MMKVPath_t &rootDir) {\n                           MMKVPath_t *rootDirPtr = (!rootDir.empty()) ? (MMKVPath_t *) &rootDir : nullptr;\n                           return MMKV::checkExist(mmapID, rootDirPtr);\n                       },\n                       \"check if the MMKV file is exist or not\", py::arg(\"mmapID\"),\n                       py::arg(\"rootDir\") = MMKVPath_t());\n}\n"
  },
  {
    "path": "Python/setup.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport os\nimport re\nimport sys\nimport platform\nimport subprocess\n\nfrom setuptools import setup, Extension\nfrom setuptools.command.build_ext import build_ext\nfrom distutils.version import LooseVersion\n\n\nclass CMakeExtension(Extension):\n    def __init__(self, name, sourcedir=''):\n        Extension.__init__(self, name, sources=[])\n        self.sourcedir = os.path.abspath(sourcedir)\n\n\nclass CMakeBuild(build_ext):\n    def run(self):\n        try:\n            out = subprocess.check_output(['cmake', '--version'])\n        except OSError:\n            raise RuntimeError(\"CMake must be installed to build the following extensions: \" +\n                               \", \".join(e.name for e in self.extensions))\n\n        if platform.system() == \"Windows\":\n            cmake_version = LooseVersion(re.search(r'version\\s*([\\d.]+)', out.decode()).group(1))\n            if cmake_version < '3.1.0':\n                raise RuntimeError(\"CMake >= 3.1.0 is required on Windows\")\n\n        for ext in self.extensions:\n            self.build_extension(ext)\n\n    def build_extension(self, ext):\n        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))\n        # required for auto-detection of auxiliary \"native\" libs\n        if not extdir.endswith(os.path.sep):\n            extdir += os.path.sep\n\n        cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,\n                      '-DPYTHON_EXECUTABLE=' + sys.executable]\n\n        cfg = 'Debug' if self.debug else 'Release'\n        build_args = ['--config', cfg]\n\n        if platform.system() == \"Windows\":\n            cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]\n            if sys.maxsize > 2 ** 32:\n                cmake_args += ['-A', 'x64']\n            build_args += ['--', '/m']\n        else:\n            cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]\n            build_args += ['--', '-j8']\n\n        env = os.environ.copy()\n        env['CXXFLAGS'] = '{} -DVERSION_INFO=\\\\\"{}\\\\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version())\n        if not os.path.exists(self.build_temp):\n            os.makedirs(self.build_temp)\n        subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)\n        subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)\n\n\nsetup(\n    name='mmkv',\n    version='1.3.6',\n    author='Tencent',\n    description='An efficient, small key-value storage framework developed by WeChat Team.',\n    long_description='The MMKV, for Python. MMKV is an efficient, generic, easy-to-use key-value storage framework '\n                     'developed by the WeChat Team. It can be a replacement for SQLite.',\n    ext_modules=[CMakeExtension('mmkv')],\n    cmdclass=dict(build_ext=CMakeBuild),\n    zip_safe=False,\n)\n"
  },
  {
    "path": "Python/unit_test.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport sys\nimport mmkv\nimport tempfile\n\nKeyNotExist = 'KeyNotExist'\n\n\ndef test_bool(kv):\n    ret = kv.set(True, 'bool')\n    assert ret\n\n    value = kv.getBool('bool')\n    assert value\n\n    value = kv.getBool(KeyNotExist)\n    assert not value\n\n    value = kv.getBool(KeyNotExist, True)\n    assert value\n\n    print('test bool: passed')\n\n\ndef test_int(kv):\n    MinInt = -1 * (2 ** 31)\n    ret = kv.set(MinInt, 'Int')\n    assert ret\n\n    value = kv.getInt('Int')\n    assert value == MinInt\n\n    MaxInt = (2 ** 31) - 1\n    ret = kv.set(MaxInt, 'Int')\n    assert ret\n\n    value = kv.getInt('Int')\n    assert value == MaxInt\n\n    value = kv.getInt(KeyNotExist)\n    assert value == 0\n\n    value = kv.getInt(KeyNotExist, -1)\n    assert value == -1\n\n    print('test int: passed')\n\n\ndef test_uint(kv):\n    MaxUInt = (2 ** 32) - 1\n    ret = kv.set(MaxUInt, 'UInt')\n    assert ret\n\n    value = kv.getUInt('UInt')\n    assert value == MaxUInt\n\n    value = kv.getUInt(KeyNotExist)\n    assert value == 0\n\n    value = kv.getUInt(KeyNotExist, 1)\n    assert value == 1\n\n    print('test unsigned int: passed')\n\n\ndef test_long_int(kv):\n    MinLongInt = -1 * (2 ** 63)\n    ret = kv.set(MinLongInt, 'LongInt')\n    assert ret\n\n    value = kv.getLongInt('LongInt')\n    assert value == MinLongInt\n\n    MaxLongInt = (2 ** 63) - 1\n    ret = kv.set(MaxLongInt, 'LongInt')\n    assert ret\n\n    value = kv.getLongInt('LongInt')\n    assert value == MaxLongInt\n\n    value = kv.getLongInt(KeyNotExist)\n    assert value == 0\n\n    value = kv.getLongInt(KeyNotExist, -1)\n    assert value == -1\n\n    print('test long int: passed')\n\n\ndef test_long_uint(kv):\n    MaxLongUInt = (2 ** 64) - 1\n    ret = kv.set(MaxLongUInt, 'LongUInt')\n    assert ret\n\n    value = kv.getLongUInt('LongUInt')\n    assert value == MaxLongUInt\n\n    value = kv.getLongUInt(KeyNotExist)\n    assert value == 0\n\n    value = kv.getLongUInt(KeyNotExist, 1)\n    assert value == 1\n\n    print('test long unsigned int: passed')\n\n\ndef is_float_equal(a, b, accuracy=1e-09):\n    return abs(a - b) <= max(accuracy * max(abs(a), abs(b)), 0.0)\n\n\ndef test_float(kv):\n    MinFloat = sys.float_info.min\n    ret = kv.set(MinFloat, 'Float')\n    assert ret\n\n    value = kv.getFloat('Float')\n    assert is_float_equal(value, MinFloat)\n\n    MaxFloat = sys.float_info.max\n    ret = kv.set(MaxFloat, 'Float')\n    assert ret\n\n    value = kv.getFloat('Float')\n    assert is_float_equal(value, MaxFloat)\n\n    value = kv.getFloat(KeyNotExist)\n    assert is_float_equal(value, 0.0)\n\n    value = kv.getFloat(KeyNotExist, -1.0)\n    assert is_float_equal(value, -1.0)\n\n    print('test float: passed')\n\n\ndef test_string(kv):\n    OlympicString = 'Hello 2021 Olympic Games 奥运会'\n    ret = kv.set(OlympicString, 'String')\n    assert ret\n\n    value = kv.getString('String')\n    if sys.version_info >= (3, 0):\n        assert value == OlympicString\n    else:\n        assert value == OlympicString.decode('utf-8')\n\n    value = kv.getString(KeyNotExist)\n    assert (not value) or (len(value) == 0)\n\n    value = kv.getString(KeyNotExist, 'a')\n    assert value == 'a'\n\n    print('test string: passed')\n\n\ndef test_bytes(kv):\n    OlympicYears = [0, 4, 8, 12, 16, 21]\n    OlympicYearsBytes = bytes(OlympicYears)\n    ret = kv.set(OlympicYearsBytes, 'Bytes')\n    assert ret\n\n    value = kv.getBytes('Bytes')\n    assert value == OlympicYearsBytes\n    if sys.version_info >= (3, 0):\n        value = list(value)\n        assert value == OlympicYears\n\n    value = kv.getBytes(KeyNotExist)\n    assert (not value) or (len(value) == 0)\n\n    default_value = None\n    if sys.version_info >= (3, 0):\n        default_value = bytes('哦豁', 'utf8')\n    else:\n        default_value = bytes('a')\n    value = kv.getBytes(KeyNotExist, default_value)\n    assert value == default_value\n\n    print('test bytes: passed')\n\n\ndef test_equal(kv, mmap_id):\n    assert kv.mmapID() == mmap_id\n\n    another = mmkv.MMKV(mmap_id)\n    assert kv == another\n\n    kv_keys = sorted(kv.keys())\n    another_keys = sorted(another.keys())\n    assert kv_keys == another_keys\n\n    print('test equal: passed')\n\n\nif __name__ == '__main__':\n    temp_dir = tempfile.gettempdir()\n    root_dir = temp_dir + '/mmkv'\n    mmkv.MMKV.initializeMMKV(root_dir)\n    kv = mmkv.MMKV('unit_test_python')\n    test_bool(kv)\n    test_int(kv)\n    test_uint(kv)\n    test_long_int(kv)\n    test_long_uint(kv)\n    test_float(kv)\n    test_string(kv)\n    test_bytes(kv)\n    test_equal(kv, 'unit_test_python')\n"
  },
  {
    "path": "README.md",
    "content": "[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)\r\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)\r\n[![Release Version](https://img.shields.io/badge/release-2.4.0-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)\r\n[![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS%2FmacOS%20%7C%20Windows%20%7C%20POSIX%20%7C%20HarmonyOS%20NEXT-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)\r\n\r\n中文版本请参看[这里](./README_CN.md)\r\n\r\nMMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android**, **iOS/macOS**, **Windows**, **POSIX** and **HarmonyOS NEXT**.\r\n\r\n# MMKV for Android\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with files, and protobuf to encode/decode values, making the most of Android to achieve the best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics, and nothing more. It's really tidy.\r\n  * **About 50K in binary size**: MMKV adds about 50K per architecture on App size, and much less when zipped (APK).\r\n\r\n\r\n## Getting Started\r\n\r\n### Installation Via Maven\r\nAdd the following lines to `build.gradle` on your app module:\r\n\r\n```gradle\r\ndependencies {\r\n    implementation 'com.tencent:mmkv:2.4.0'\r\n    // replace \"2.4.0\" with any available version\r\n}\r\n```\r\n\r\nStarting from v2.0.0, MMKV **no longer supports 32-bit** arch and API level 22 or 21, if you want 32-bit or API level 21~22, use v1.3.x LTS series.  \r\n\r\nFor other installation options, see [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup).\r\n\r\n### Quick Tutorial\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.  \r\nSetup MMKV on App startup, say your `Application` class, add these lines:\r\n\r\n```Java\r\npublic void onCreate() {\r\n    super.onCreate();\r\n\r\n    String rootDir = MMKV.initialize(this);\r\n    System.out.println(\"mmkv root: \" + rootDir);\r\n    //……\r\n}\r\n```\r\n\r\nMMKV has a global instance, that can be used directly:\r\n\r\n```Java\r\nimport com.tencent.mmkv.MMKV;\r\n    \r\nMMKV kv = MMKV.defaultMMKV();\r\n\r\nkv.encode(\"bool\", true);\r\nboolean bValue = kv.decodeBool(\"bool\");\r\n\r\nkv.encode(\"int\", Integer.MIN_VALUE);\r\nint iValue = kv.decodeInt(\"int\");\r\n\r\nkv.encode(\"string\", \"Hello from mmkv\");\r\nString str = kv.decodeString(\"string\");\r\n```\r\n\r\nMMKV also supports **Multi-Process Access**. Full tutorials can be found here [Android Tutorial](https://github.com/Tencent/MMKV/wiki/android_tutorial).\r\n\r\n## Performance\r\nWriting random `int` for 1000 times, we get this chart:  \r\n![](https://github.com/Tencent/MMKV/wiki/assets/profile_android_mini.png)  \r\nFor more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/android_benchmark).\r\n\r\n# MMKV for iOS/macOS\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with files, and protobuf to encode/decode values, making the most of iOS/macOS to achieve the best performance.\r\n \r\n* **Easy-to-use**. You can use MMKV as you go, no configurations are needed. All changes are saved immediately, no `synchronize` calls are needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains encode/decode helpers and mmap logics and nothing more. It's really tidy.\r\n  * **Less than 30K in binary size**: MMKV adds less than 30K per architecture on App size, and much less when zipped (IPA).\r\n\r\n## Getting Started\r\n\r\n### Installation Via CocoaPods:\r\n  1. Install [CocoaPods](https://guides.CocoaPods.org/using/getting-started.html);\r\n  2. Open the terminal, `cd` to your project directory, run `pod repo update` to make CocoaPods aware of the latest available MMKV versions;\r\n  3. Edit your Podfile, add `pod 'MMKV'` to your app target;\r\n  4. Run `pod install`;\r\n  5. Open the `.xcworkspace` file generated by CocoaPods;\r\n  6. Add `#import <MMKV/MMKV.h>` to your source file and we are done.\r\n\r\nFor other installation options, see [iOS/macOS Setup](https://github.com/Tencent/MMKV/wiki/iOS_setup).\r\n\r\n### Quick Tutorial\r\nYou can use MMKV as you go, no configurations are needed. All changes are saved immediately, no `synchronize` calls are needed.\r\nSetup MMKV on App startup, in your `-[MyApp application: didFinishLaunchingWithOptions:]`, add these lines:\r\n\r\n```objective-c\r\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\r\n    // init MMKV in the main thread\r\n    [MMKV initializeMMKV:nil];\r\n\r\n    //...\r\n    return YES;\r\n}\r\n```\r\n\r\nMMKV has a global instance, that can be used directly:\r\n\r\n```objective-c\r\nMMKV *mmkv = [MMKV defaultMMKV];\r\n    \r\n[mmkv setBool:YES forKey:@\"bool\"];\r\nBOOL bValue = [mmkv getBoolForKey:@\"bool\"];\r\n    \r\n[mmkv setInt32:-1024 forKey:@\"int32\"];\r\nint32_t iValue = [mmkv getInt32ForKey:@\"int32\"];\r\n    \r\n[mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\r\nNSString *str = [mmkv getStringForKey:@\"string\"];\r\n```\r\n\r\nMMKV also supports **Multi-Process Access**. Full tutorials can be found [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial).\r\n\r\n## Performance\r\nWriting random `int` for 10000 times, we get this chart:  \r\n![](https://github.com/Tencent/MMKV/wiki/assets/profile_mini.png)  \r\nFor more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/iOS_benchmark).\r\n\r\n\r\n# MMKV for Windows\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with files, and protobuf to encode/decode values, making the most of Windows to achieve the best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls are needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics, and nothing more. It's really tidy.\r\n  * **About 10K in binary size**: MMKV adds about 10K on application size, and much less when zipped.\r\n\r\n\r\n## Getting Started\r\n\r\n### Installation Via Source\r\n1. Getting source code from git repository:\r\n  \r\n   ```\r\n   git clone https://github.com/Tencent/MMKV.git\r\n   ```\r\n  \r\n2. Add `Core/core.vcxproj` to your solution;\r\n3. Add `MMKV` project to your project's dependencies;\r\n4. Add `$(OutDir)include` to your project's `C/C++` -> `General` -> `Additional Include Directories`;\r\n5. Add `$(OutDir)` to your project's `Linker` -> `General` -> `Additional Library Directories`;\r\n6. Add `mmkv.lib` to your project's `Linker` -> `Input` -> `Additional Dependencies`;\r\n7. Add `#include <MMKV/MMKV.h>` to your source file and we are done.\r\n\r\n\r\nnote:  \r\n\r\n1. MMKV is compiled with `MT/MTd` runtime by default. If your project uses `MD/MDd`, you should change MMKV's setting to match your project's (`C/C++` -> `Code Generation` -> `Runtime Library`), or vice versa.\r\n2. MMKV is developed with Visual Studio 2017, change the `Platform Toolset` if you use a different version of Visual Studio.\r\n\r\nFor other installation options, see [Windows Setup](https://github.com/Tencent/MMKV/wiki/windows_setup).\r\n\r\n### Quick Tutorial\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.  \r\nSetup MMKV on App startup, say in your `main()`, add these lines:\r\n\r\n```C++\r\n#include <MMKV/MMKV.h>\r\n\r\nint main() {\r\n    std::wstring rootDir = getYourAppDocumentDir();\r\n    MMKV::initializeMMKV(rootDir);\r\n    //...\r\n}\r\n```\r\n\r\nMMKV has a global instance, that can be used directly:\r\n\r\n```C++\r\nauto mmkv = MMKV::defaultMMKV();\r\n\r\nmmkv->set(true, \"bool\");\r\nstd::cout << \"bool = \" << mmkv->getBool(\"bool\") << std::endl;\r\n\r\nmmkv->set(1024, \"int32\");\r\nstd::cout << \"int32 = \" << mmkv->getInt32(\"int32\") << std::endl;\r\n\r\nmmkv->set(\"Hello, MMKV for Windows\", \"string\");\r\nstd::string result;\r\nmmkv->getString(\"string\", result);\r\nstd::cout << \"string = \" << result << std::endl;\r\n```\r\n\r\nMMKV also supports **Multi-Process Access**. Full tutorials can be found here [Windows Tutorial](https://github.com/Tencent/MMKV/wiki/windows_tutorial).\r\n\r\n# MMKV for POSIX\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with files, and protobuf to encode/decode values, making the most of POSIX to achieve the best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls are needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics, and nothing more. It's really tidy.\r\n  * **About 7K in binary size**: MMKV adds about 7K on application size, and much less when zipped.\r\n\r\n\r\n## Getting Started\r\n\r\n### Installation Via CMake\r\n1. Getting source code from the git repository:\r\n  \r\n   ```\r\n   git clone https://github.com/Tencent/MMKV.git\r\n   ```\r\n2. Edit your `CMakeLists.txt`, add those lines:\r\n\r\n    ```cmake\r\n    add_subdirectory(mmkv/POSIX/src mmkv)\r\n    target_link_libraries(MyApp\r\n        mmkv)\r\n    ```\r\n3. Add `#include \"MMKV.h\"` to your source file and we are done.\r\n\r\nFor other installation options, see [POSIX Setup](https://github.com/Tencent/MMKV/wiki/posix_setup).\r\n\r\n### Quick Tutorial\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.  \r\nSetup MMKV on App startup, say in your `main()`, add these lines:\r\n\r\n```C++\r\n#include \"MMKV.h\"\r\n\r\nint main() {\r\n    std::string rootDir = getYourAppDocumentDir();\r\n    MMKV::initializeMMKV(rootDir);\r\n    //...\r\n}\r\n```\r\n\r\nMMKV has a global instance, that can be used directly:\r\n\r\n```C++\r\nauto mmkv = MMKV::defaultMMKV();\r\n\r\nmmkv->set(true, \"bool\");\r\nstd::cout << \"bool = \" << mmkv->getBool(\"bool\") << std::endl;\r\n\r\nmmkv->set(1024, \"int32\");\r\nstd::cout << \"int32 = \" << mmkv->getInt32(\"int32\") << std::endl;\r\n\r\nmmkv->set(\"Hello, MMKV for Windows\", \"string\");\r\nstd::string result;\r\nmmkv->getString(\"string\", result);\r\nstd::cout << \"string = \" << result << std::endl;\r\n```\r\n\r\nMMKV also supports **Multi-Process Access**. Full tutorials can be found here [POSIX Tutorial](https://github.com/Tencent/MMKV/wiki/posix_tutorial).\r\n\r\n# MMKV for HarmonyOS NEXT\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of native platform to achieve best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `flush` calls needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.\r\n  * **About 600K in binary size**: MMKV adds about 600K per architecture on App size, and much less when zipped (HAR/HAP).\r\n\r\n\r\n## Getting Started\r\n### Installation via OHPM:\r\n\r\n```bash\r\nohpm install @tencent/mmkv\r\n```\r\n### Quick Tutorial\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.  \r\nSetup MMKV on App startup, say your `EntryAbility.onCreate()` function, add these lines:\r\n\r\n```js\r\nimport { MMKV } from '@tencent/mmkv';\r\n\r\nexport default class EntryAbility extends UIAbility {\r\n  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {\r\n    let appCtx = this.context.getApplicationContext();\r\n    let mmkvRootDir = MMKV.initialize(appCtx);\r\n    console.info('mmkv rootDir: ', mmkvRootDir);\r\n    ……\r\n  }\r\n```\r\n\r\nMMKV has a global instance, that can be used directly:\r\n\r\n```js\r\nimport { MMKV } from '@tencent/mmkv';\r\n    \r\nlet mmkv = MMKV.defaultMMKV();\r\nmmkv.encodeBool('bool', true);\r\nconsole.info('bool = ', mmkv.decodeBool('bool'));\r\n    \r\nmmkv.encodeInt32('int32', Math.pow(2, 31) - 1);\r\nconsole.info('max int32 = ', mmkv.decodeInt32('int32'));\r\n    \r\nmmkv.encodeInt64('int', BigInt(2**63) - BigInt(1));\r\nconsole.info('max int64 = ', mmkv.decodeInt64('int'));\r\n    \r\nlet str: string = 'Hello OpenHarmony from MMKV';\r\nmmkv.encodeString('string', str);\r\nconsole.info('string = ', mmkv.decodeString('string'));\r\n\r\nlet arrayBuffer: ArrayBuffer = StringToArrayBuffer('Hello OpenHarmony from MMKV with bytes');\r\nmmkv.encodeBytes('bytes', arrayBuffer);\r\nlet bytes = mmkv.decodeBytes('bytes');\r\nconsole.info('bytes = ', ArrayBufferToString(bytes));\r\n```\r\n\r\nAs you can see, MMKV is quite easy to use.\r\nFor the full documentation, see [HarmonyOS NEXT Tutorial](https://github.com/Tencent/MMKV/wiki/ohos_setup).\r\n\r\n## License\r\nMMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](./LICENSE.TXT).\r\n\r\n## Change Log\r\nCheck out the [CHANGELOG.md](./CHANGELOG.md) for details of change history.\r\n\r\n## Contributing\r\n\r\nIf you are interested in contributing, check out the [CONTRIBUTING.md](./CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution). MMKV has officially joined the [Tencent Device-oriented Service Product Alliance](https://tds-union.qq.com/), working together with other alliance members to build an open and mutually beneficial frontend technology product ecosystem.\r\n\r\n\r\nTo give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](./CODE_OF_CONDUCT.md).\r\n\r\n## FAQ & Feedback\r\nCheck out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).\r\n\r\n## Personal Information Protection Rules\r\nUser privacy is taken very seriously: MMKV does not obtain, collect or upload any personal information. Please refer to the [MMKV SDK Personal Information Protection Rules](https://support.weixin.qq.com/cgi-bin/mmsupportacctnodeweb-bin/pages/aY5BAtRiO1BpoHxo) for details.\r\n"
  },
  {
    "path": "README_CN.md",
    "content": "# MMKV——基于 mmap 的高性能通用 key-value 组件\r\nMMKV 是基于 mmap 内存映射的 key-value 组件，底层序列化/反序列化使用 protobuf 实现，性能高，稳定性强。从 2015 年中至今在微信上使用，其性能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Windows / POSIX / HarmonyOS NEXT 等平台，一并开源。\r\n\r\n## MMKV 源起\r\n在微信客户端的日常运营中，时不时就会爆发特殊文字引起系统的 crash，[参考文章](https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286826&idx=1&sn=35601cb1156617aa235b7fd4b085bfc4)，文章里面设计的技术方案是在关键代码前后进行计数器的加减，通过检查计数器的异常，来发现引起闪退的异常文字。在会话列表、会话界面等有大量 cell 的地方，希望新加的计时器不会影响滑动性能；另外这些计数器还要永久存储下来——因为闪退随时可能发生。这就需要一个性能非常高的通用 key-value 存储组件，我们考察了 SharedPreferences、NSUserDefaults、SQLite 等常见组件，发现都没能满足如此苛刻的性能要求。考虑到这个防 crash 方案最主要的诉求还是实时写入，而 mmap 内存映射文件刚好满足这种需求，我们尝试通过它来实现一套 key-value 组件。\r\n\r\n## MMKV 原理\r\n* **内存准备**  \r\n通过 mmap 内存映射文件，提供一段可供随时写入的内存块，App 只管往里面写数据，由操作系统负责将内存回写到文件，不必担心 crash 导致数据丢失。\r\n* **数据组织**  \r\n数据序列化方面我们选用 protobuf 协议，pb 在性能和空间占用上都有不错的表现。\r\n* **写入优化**  \r\n考虑到主要使用场景是频繁地进行写入更新，我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后，append 到内存末尾。\r\n* **空间增长**  \r\n使用 append 实现增量更新带来了一个新的问题，就是不断 append 的话，文件大小会增长得不可控。我们需要在性能和空间上做个折中。\r\n\r\n更详细的设计原理参考 [MMKV 原理](https://github.com/Tencent/MMKV/wiki/design)。\r\n\r\n## Android 指南\r\n### 安装引入\r\n推荐使用 Maven：\r\n\r\n```gradle\r\ndependencies {\r\n    implementation 'com.tencent:mmkv:2.4.0'\r\n    // replace \"2.4.0\" with any available version\r\n}\r\n```\r\n从 v2.0.0 起, MMKV **去掉了 32-bit 架构的支持**、API level 22 及以下的支持, 如有这类需求，请使用 v1.3.x LTS 版本。  \r\n更多安装指引参考 [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup_cn)。\r\n\r\n### 快速上手\r\nMMKV 的使用非常简单，所有变更立马生效，无需调用 `sync`、`apply`。\r\n在 App 启动时初始化 MMKV，设定 MMKV 的根目录（files/mmkv/），例如在 `Application` 里：\r\n\r\n```Java\r\npublic void onCreate() {\r\n    super.onCreate();\r\n\r\n    String rootDir = MMKV.initialize(this);\r\n    System.out.println(\"mmkv root: \" + rootDir);\r\n    //……\r\n}\r\n```\r\n\r\nMMKV 提供一个全局的实例，可以直接使用：\r\n\r\n```Java\r\nimport com.tencent.mmkv.MMKV;\r\n//……\r\n\r\nMMKV kv = MMKV.defaultMMKV();\r\n\r\nkv.encode(\"bool\", true);\r\nboolean bValue = kv.decodeBool(\"bool\");\r\n\r\nkv.encode(\"int\", Integer.MIN_VALUE);\r\nint iValue = kv.decodeInt(\"int\");\r\n\r\nkv.encode(\"string\", \"Hello from mmkv\");\r\nString str = kv.decodeString(\"string\");\r\n```\r\nMMKV 支持**多进程访问**，更详细的用法参考 [Android Tutorial](https://github.com/Tencent/MMKV/wiki/android_tutorial_cn)。\r\n\r\n### 性能对比\r\n循环写入随机的`int` 1k 次，我们有如下性能对比：  \r\n![](https://github.com/Tencent/MMKV/wiki/assets/profile_android_mini.png)  \r\n更详细的性能对比参考 [Android Benchmark](https://github.com/Tencent/MMKV/wiki/android_benchmark_cn)。\r\n\r\n## iOS/macOS 指南\r\n### 安装引入\r\n推荐使用 CocoaPods：\r\n\r\n  1. 安装 [CocoaPods](https://guides.CocoaPods.org/using/getting-started.html)；\r\n  2. 打开命令行, `cd` 到你的项目工程目录, 输入 `pod repo update` 让 CocoaPods 感知最新的 MMKV 版本；\r\n  3. 打开 Podfile, 添加 `pod 'MMKV'` 到你的 app target 里面；\r\n  4. 在命令行输入 `pod install`；\r\n  5. 用 Xcode 打开由 CocoaPods 自动生成的 `.xcworkspace` 文件；\r\n  6. 添加头文件 `#import <MMKV/MMKV.h>`，就可以愉快地开始你的 MMKV 之旅了。\r\n\r\n更多安装指引参考 [iOS/macOS Setup](https://github.com/Tencent/MMKV/wiki/iOS_setup_cn)。\r\n\r\n### 快速上手\r\nMMKV 的使用非常简单，无需任何配置，所有变更立马生效，无需调用 `synchronize`。在 App 启动时初始化 MMKV（设定 MMKV 的根目录），例如在`-[MyApp application: didFinishLaunchingWithOptions:]`里：\r\n\r\n```objective-c\r\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\r\n    // init MMKV in the main thread\r\n    [MMKV initializeMMKV:nil];\r\n\r\n    //...\r\n    return YES;\r\n}\r\n```\r\n\r\nMMKV 提供一个全局的实例，可以直接使用：\r\n\r\n```objective-c\r\nMMKV *mmkv = [MMKV defaultMMKV];\r\n    \r\n[mmkv setBool:YES forKey:@\"bool\"];\r\nBOOL bValue = [mmkv getBoolForKey:@\"bool\"];\r\n    \r\n[mmkv setInt32:-1024 forKey:@\"int32\"];\r\nint32_t iValue = [mmkv getInt32ForKey:@\"int32\"];\r\n    \r\n[mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\r\nNSString *str = [mmkv getStringForKey:@\"string\"];\r\n```\r\n\r\nMMKV 支持**多进程访问**，更详细的用法参考 [iOS/macOS Tutorial](https://github.com/Tencent/MMKV/wiki/iOS_tutorial_cn)。\r\n\r\n### 性能对比\r\n循环写入随机的`int` 1w 次，我们有如下性能对比：  \r\n![](https://github.com/Tencent/MMKV/wiki/assets/profile_mini.png)  \r\n更详细的性能对比参考 [iOS/macOS Benchmark](https://github.com/Tencent/MMKV/wiki/iOS_benchmark_cn)。\r\n\r\n## Windows 指南\r\n### 安装引入\r\n推荐使用子工程：\r\n\r\n  1. 获取 MMKV 源码：\r\n  \r\n     ```\r\n     git clone https://github.com/Tencent/MMKV.git\r\n     ```\r\n  \r\n  2. 添加工程 `Core/core.vcxproj` 到你的项目里；\r\n  3. 设置你的主工程依赖于 `MMKV` 工程;\r\n  4. 添加目录 `$(OutDir)include` 到你主工程的 `C/C++` -> `常规` -> `附加包含目录`;\r\n  5. 添加目录 `$(OutDir)` 到你主工程的 `链接器` -> `常规` -> `附加库目录`;\r\n  6. 添加 `mmkv.lib` 到你主工程的 `链接器` -> `输入` -> `附加依赖项`;\r\n  7. 添加头文件 `#include <MMKV/MMKV.h>`，就可以愉快地开始你的 MMKV 之旅了。\r\n\r\n注意：\r\n\r\n1. MMKV 默认使用 `MT/MTd` 运行时库来编译，如果你发现主工程的配置不一样，请修改 MMKV 的配置再编译;\r\n2. MMKV 使用 Visual Studio 2017 开发，如果你在使用其他版本的 Visual Studio，请修改 MMKV 的`工具集`与主工程一致，再编译.\r\n\r\n更多安装指引参考 [Windows Setup](https://github.com/Tencent/MMKV/wiki/windows_setup_cn)。\r\n\r\n### 快速上手\r\nMMKV 的使用非常简单，所有变更立马生效，无需调用 `save`、`sync`。\r\n在 App 启动时初始化 MMKV，设定 MMKV 的根目录，例如在 `main()` 里：\r\n\r\n\r\n```C++\r\n#include <MMKV/MMKV.h>\r\n\r\nint main() {\r\n    std::wstring rootDir = getYourAppDocumentDir();\r\n    MMKV::initializeMMKV(rootDir);\r\n    //...\r\n}\r\n```\r\n\r\nMMKV 提供一个全局的实例，可以直接使用：\r\n\r\n```C++\r\nauto mmkv = MMKV::defaultMMKV();\r\n\r\nmmkv->set(true, \"bool\");\r\nstd::cout << \"bool = \" << mmkv->getBool(\"bool\") << std::endl;\r\n\r\nmmkv->set(1024, \"int32\");\r\nstd::cout << \"int32 = \" << mmkv->getInt32(\"int32\") << std::endl;\r\n\r\nmmkv->set(\"Hello, MMKV for Windows\", \"string\");\r\nstd::string result;\r\nmmkv->getString(\"string\", result);\r\nstd::cout << \"string = \" << result << std::endl;\r\n```\r\n\r\nMMKV 支持**多进程访问**，更详细的用法参考 [Windows Tutorial](https://github.com/Tencent/MMKV/wiki/windows_tutorial_cn)。\r\n\r\n## POSIX 指南\r\n### 安装引入\r\n推荐使用 CMake：\r\n\r\n  1. 获取 MMKV 源码：\r\n  \r\n     ```\r\n     git clone https://github.com/Tencent/MMKV.git\r\n     ```\r\n  \r\n  2. 打开你项目的 `CMakeLists.txt`, 添加这几行:\r\n\r\n    ```cmake\r\n    add_subdirectory(mmkv/POSIX/src mmkv)\r\n    target_link_libraries(MyApp\r\n        mmkv)\r\n    ```\r\n 3. 添加头文件 `#include \"MMKV.h\"`，就可以愉快地开始你的 MMKV 之旅了。\r\n\r\n更多安装指引参考 [POSIX Setup](https://github.com/Tencent/MMKV/wiki/posix_setup_cn)。\r\n\r\n### 快速上手\r\nMMKV 的使用非常简单，所有变更立马生效，无需调用 `save`、`sync`。\r\n在 App 启动时初始化 MMKV，设定 MMKV 的根目录，例如在 `main()` 里：\r\n\r\n\r\n```C++\r\n#include \"MMKV.h\"\r\n\r\nint main() {\r\n    std::string rootDir = getYourAppDocumentDir();\r\n    MMKV::initializeMMKV(rootDir);\r\n    //...\r\n}\r\n```\r\n\r\nMMKV 提供一个全局的实例，可以直接使用：\r\n\r\n```C++\r\nauto mmkv = MMKV::defaultMMKV();\r\n\r\nmmkv->set(true, \"bool\");\r\nstd::cout << \"bool = \" << mmkv->getBool(\"bool\") << std::endl;\r\n\r\nmmkv->set(1024, \"int32\");\r\nstd::cout << \"int32 = \" << mmkv->getInt32(\"int32\") << std::endl;\r\n\r\nmmkv->set(\"Hello, MMKV for Windows\", \"string\");\r\nstd::string result;\r\nmmkv->getString(\"string\", result);\r\nstd::cout << \"string = \" << result << std::endl;\r\n```\r\n\r\nMMKV 支持**多进程访问**，更详细的用法参考 [POSIX Tutorial](https://github.com/Tencent/MMKV/wiki/posix_tutorial_cn)。\r\n\r\n## HarmonyOS NEXT 指南\r\n### 安装引入\r\n推荐使用 OHPM：\r\n\r\n```bash\r\nohpm install @tencent/mmkv\r\n```\r\n\r\n更多安装指引参考 [HarmonyOS NEXT Tutorial](https://github.com/Tencent/MMKV/wiki/ohos_setup)。\r\n\r\n### 快速上手\r\nMMKV 的使用非常简单，所有变更立马生效，无需调用 `save`、`sync`。\r\n在 App 启动时初始化 MMKV，设定 MMKV 的根目录，例如在 `EntryAbility.onCreate()` 里：\r\n\r\n```js\r\nimport { MMKV } from '@tencent/mmkv';\r\n\r\nexport default class EntryAbility extends UIAbility {\r\n  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {\r\n    let appCtx = this.context.getApplicationContext();\r\n    let mmkvRootDir = MMKV.initialize(appCtx);\r\n    console.info('mmkv rootDir: ', mmkvRootDir);\r\n    ……\r\n  }\r\n```\r\n\r\nMMKV 提供一个全局的实例，可以直接使用：\r\n\r\n```js\r\nimport { MMKV } from '@tencent/mmkv';\r\n    \r\nlet mmkv = MMKV.defaultMMKV();\r\nmmkv.encodeBool('bool', true);\r\nconsole.info('bool = ', mmkv.decodeBool('bool'));\r\n    \r\nmmkv.encodeInt32('int32', Math.pow(2, 31) - 1);\r\nconsole.info('max int32 = ', mmkv.decodeInt32('int32'));\r\n    \r\nmmkv.encodeInt64('int', BigInt(2**63) - BigInt(1));\r\nconsole.info('max int64 = ', mmkv.decodeInt64('int'));\r\n    \r\nlet str: string = 'Hello OpenHarmony from MMKV';\r\nmmkv.encodeString('string', str);\r\nconsole.info('string = ', mmkv.decodeString('string'));\r\n\r\nlet arrayBuffer: ArrayBuffer = StringToArrayBuffer('Hello OpenHarmony from MMKV with bytes');\r\nmmkv.encodeBytes('bytes', arrayBuffer);\r\nlet bytes = mmkv.decodeBytes('bytes');\r\nconsole.info('bytes = ', ArrayBufferToString(bytes));\r\n```\r\n\r\nMMKV 更详细的用法参考 [HarmonyOS NEXT Tutorial](https://github.com/Tencent/MMKV/wiki/ohos_setup)。\r\n\r\n## License\r\nMMKV 以 BSD 3-Clause 证书开源，详情参见 [LICENSE.TXT](./LICENSE.TXT)。\r\n\r\n## 版本历史\r\n具体版本历史请参看 [CHANGELOG.md](./CHANGELOG.md)。\r\n\r\n## 参与贡献\r\n如果你有兴趣参与贡献，可以参考 [CONTRIBUTING.md](./CONTRIBUTING.md)。\r\n[腾讯开源激励计划](https://opensource.tencent.com/contribution) 鼓励开发者的参与和贡献，期待你的加入。\r\nMMKV 正式加入[TDS 腾讯端服务产品联盟](https://tds-union.qq.com/)，携手联盟其他成员，共同致力于构建开放共赢的大前端技术产品生态。\r\n\r\n为了明确我们对参与者的期望，MMKV 采用了被广泛使用的、由 Contributor Covenant 所定义的行为准则。我们认为它很好地阐明了我们的价值观。有关更多信息请查看 [Code of Conduct](./CODE_OF_CONDUCT.md)。\r\n\r\n## 问题 & 反馈\r\n常见问题参见 [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ_cn)，欢迎提 [issues](https://github.com/Tencent/MMKV/issues) 提问反馈。\r\n\r\n## 个人信息处理规则\r\nMMKV 不收集、获取或上传任何个人信息，详情参考[《MMKV SDK个人信息保护规则》](https://support.weixin.qq.com/cgi-bin/mmsupportacctnodeweb-bin/pages/aY5BAtRiO1BpoHxo)。\r\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nSecurity updates are currently being supported for these versions of MMKV.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 2.0.x   | :white_check_mark: |\n| 1.3.x   | :white_check_mark: |\n| < 1.3.9   | :x:                |\n\n## Reporting a Vulnerability\n\nSubmit an issue, or email me if it's sensitive, to report a vulnerability.\n"
  },
  {
    "path": "Script/dumpJavaSignature.py",
    "content": "import sys\nimport re\nimport ctypes\n\n# Mapping: Java Type -> (JNI Descriptor, Ctypes Type)\ntype_map = {\n    \"void\":    (\"V\", None),\n    \"boolean\": (\"Z\", ctypes.c_ubyte),  # jboolean is unsigned 8-bit\n    \"byte\":    (\"B\", ctypes.c_byte),\n    \"char\":    (\"C\", ctypes.c_uint16), # jchar is unsigned 16-bit\n    \"short\":   (\"S\", ctypes.c_short),\n    \"int\":     (\"I\", ctypes.c_int),\n    \"long\":    (\"J\", ctypes.c_int64),  # Java long is always 64-bit\n    \"float\":   (\"F\", ctypes.c_float),\n    \"double\":  (\"D\", ctypes.c_double),\n    \"String\":  (\"Ljava/lang/String;\", ctypes.c_void_p),\n    \"Object\":  (\"Ljava/lang/Object;\", ctypes.c_void_p),\n}\n\ndef get_jni_signature(return_type, params):\n    \"\"\"Generates the JNI encoding (e.g., '(II)V').\"\"\"\n    def get_type_sig(t_type):\n        if t_type.endswith(\"[]\"):\n            return \"[\" + get_type_sig(t_type[:-2])\n        if t_type in type_map:\n            return type_map[t_type][0]\n        # Fallback for custom classes: replace . with / and wrap in L...;\n        formatted_type = t_type.replace('.', '/')\n        return f\"L{formatted_type};\"\n\n    param_sigs = [get_type_sig(p_type) for p_type, _ in params]\n    return_sig = get_type_sig(return_type)\n    return f\"({''.join(param_sigs)}){return_sig}\"\n\ndef get_ctypes_signature(return_type, params):\n    \"\"\"Generates the ctypes CFUNCTYPE.\"\"\"\n    def get_ctype(t_type):\n        if t_type.endswith(\"[]\"):\n            return ctypes.c_void_p  # Arrays are objects (opaque pointers)\n        if t_type in type_map:\n            return type_map[t_type][1]\n        return ctypes.c_void_p      # Objects are void pointers\n\n    param_types = [get_ctype(p_type) for p_type, _ in params]\n\n    # Handle return type\n    ret_type = None if return_type == \"void\" else get_ctype(return_type)\n\n    # Note: The first two arguments in JNI are always (JNIEnv*, jobject/jclass)\n    # We set them as c_void_p here.\n    return ctypes.CFUNCTYPE(ret_type, ctypes.c_void_p, ctypes.c_void_p, *param_types)\n\ndef parse_method_declaration(declaration):\n    \"\"\"\n    Parses a Java method string regardless of modifier order.\n    Example: \"private native static int foo(String s)\"\n    \"\"\"\n    # 1. Separate Method Definition (left) from Parameters (right)\n    if '(' not in declaration or ')' not in declaration:\n        raise ValueError(\"Declaration must contain parenthesis '()'\")\n\n    def_part, params_part = declaration.split('(', 1)\n    params_part = params_part.rsplit(')', 1)[0] # remove trailing ')'\n\n    # 2. Tokenize the definition part (handling generics vaguely)\n    # We replace generic brackets with underscores temporarily to avoid splitting errors\n    # if users input \"List<String>\".\n    # (A real parser is better, but this suffices for single-line inputs)\n    clean_def = re.sub(r'<[^>]+>', '', def_part)\n    tokens = clean_def.split()\n\n    if len(tokens) < 2:\n        raise ValueError(\"Declaration too short. Needs at least ReturnType and MethodName.\")\n\n    # In Java, the last token before '(' is the Method Name\n    method_name = tokens[-1]\n    # The second to last is the Return Type\n    return_type = tokens[-2]\n    # Everything else are modifiers\n    modifiers = tokens[:-2]\n\n    # Check for 'native'\n    if \"native\" not in modifiers:\n        print(\"Warning: 'native' keyword missing. Proceeding anyway...\")\n\n    # Check for 'static'\n    is_static = \"static\" in modifiers\n\n    # 3. Parse Parameters\n    params = []\n    if params_part.strip():\n        # Split by comma\n        raw_params = params_part.split(',')\n        for raw_p in raw_params:\n            raw_p = raw_p.strip()\n            # Regex to grab type and name: \"String arg0\" or \"int[] numbers\"\n            # We ignore keywords like final or annotations for the signature\n            parts = raw_p.split()\n            if not parts: continue\n\n            # Usually the type is the second-to-last word if modifiers exist,\n            # or first word if simple.\n            # Heuristic: The Type is the word immediately preceding the Variable Name.\n            if len(parts) >= 2:\n                p_type = parts[-2]\n            else:\n                p_type = parts[0] # Just a type, no variable name provided\n\n            params.append((p_type, False))\n\n    return method_name, return_type, params, is_static\n\nif __name__ == \"__main__\":\n    if len(sys.argv) < 2:\n        print(\"Usage: python jni_sig.py \\\"<Java Declaration>\\\"\")\n        print(\"\\nExample Test:\")\n        test_decl = \"private native static int[] myMethod(int count, String msg)\"\n        print(f\"Input: {test_decl}\")\n\n        try:\n            name, ret, params, is_static = parse_method_declaration(test_decl)\n            jni = get_jni_signature(ret, params)\n            c_sig = get_ctypes_signature(ret, params)\n\n            print(f\"Method: {name}\")\n            print(f\"Is Static: {is_static}\")\n            print(f\"JNI Sig: {jni}\")\n            print(f\"ctypes: {c_sig}\")\n        except Exception as e:\n            print(e)\n        sys.exit(0)\n\n    decl = sys.argv[1]\n    try:\n        name, ret, params, is_static = parse_method_declaration(decl)\n        jni = get_jni_signature(ret, params)\n        c_sig = get_ctypes_signature(ret, params)\n\n        print(f\"Method Name: {name}\")\n        print(f\"Is Static: {is_static}\")\n        print(f\"JNI Signature: {jni}\")\n        print(f\"ctypes Signature: {c_sig}\")\n    except ValueError as e:\n        print(f\"Error: {e}\")"
  },
  {
    "path": "Script/formatCode.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport os\nimport os.path\nimport subprocess\npath = os.getcwd()\n\nexcluded_directories = [\"pybind11\", \"/openssl\", \"/zlib\", \"/build/generated/\", \"cmake-build-debug/\", \"cmake-build-release\", \"CMakeFiles\"]\nfor root, dirs, files in os.walk(path):\n    if any(excluded_dir in root for excluded_dir in excluded_directories):\n        continue\n    for name in files:\n        if name.endswith((\".h\", \".m\", \".mm\", \".hpp\", \".cpp\", \".java\")):\n            localpath = os.path.join(root, name)\n            print(localpath)\n            subprocess.run([\"clang-format\", \"-i\", localpath, \"-style=File\"], check=True)\n"
  },
  {
    "path": "Win32/.clang-format",
    "content": "---\nBasedOnStyle: LLVM\nIndentWidth: 4\nTabWidth: 4\nAlwaysBreakTemplateDeclarations: true\nAllowShortFunctionsOnASingleLine: Inline\nBreakAfterJavaFieldAnnotations: true\nBreakBeforeBraces: Linux\nSpaceAfterCStyleCast: true\nIndentCaseLabels: true\nAccessModifierOffset: -4\nBreakBeforeBraces: Custom\nBraceWrapping:\n    AfterNamespace: false\n    AfterClass: false\n    AfterFunction: false\n\nBreakConstructorInitializersBeforeComma: true\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nBinPackParameters: false\nReflowComments: false\n---\nLanguage: Cpp\nColumnLimit: 120\nIndentPPDirectives: AfterHash\n---\nLanguage: ObjC\nColumnLimit: 0\n#UseTab: ForIndentation\nObjCBlockIndentWidth: 4\n---\nLanguage: Java\nColumnLimit: 100\nAllowShortFunctionsOnASingleLine: None\nBreakBeforeBinaryOperators: NonAssignment\n"
  },
  {
    "path": "Win32/Win32.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.9.34714.143\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Win32Demo\", \"Win32Demo\\Win32Demo.vcxproj\", \"{532303C4-38D9-45D3-B925-18056C66CD01}\"\r\n\tProjectSection(ProjectDependencies) = postProject\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346} = {86D6C605-81F3-407B-A766-690EDF4CD346}\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5} = {32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Win32DemoProcess\", \"Win32DemoProcess\\Win32DemoProcess.vcxproj\", \"{86D6C605-81F3-407B-A766-690EDF4CD346}\"\r\n\tProjectSection(ProjectDependencies) = postProject\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5} = {32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"core\", \"..\\Core\\core.vcxproj\", \"{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Release|x64.Build.0 = Release|x64\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{532303C4-38D9-45D3-B925-18056C66CD01}.Release|x86.Build.0 = Release|Win32\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Release|x64.Build.0 = Release|x64\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{86D6C605-81F3-407B-A766-690EDF4CD346}.Release|x86.Build.0 = Release|Win32\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Release|x64.Build.0 = Release|x64\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{32CD39C9-37B5-3D38-A3D9-45E13F4AF9C5}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {EAE99F5F-91EC-4631-A8DA-E8223A366552}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Win32/Win32Demo/Win32Demo.cpp",
    "content": "﻿/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pch.h\"\n\n#include <MMKV/MMKV.h>\n#include <iostream>\n#include <string>\n#include <cassert>\n\nusing namespace std;\n\nwstring getAppDataRoaming(const wstring &company, const wstring &appName) {\n    wchar_t roaming[MAX_PATH] = {0};\n    auto size = GetEnvironmentVariable(L\"appdata\", roaming, MAX_PATH);\n    if (size >= MAX_PATH || size == 0) {\n        cout << \"fail to get %appdata%: \" << GetLastError() << endl;\n        return L\"\";\n    } else {\n        wstring result(roaming, size);\n        result += L\"\\\\\" + company;\n        result += L\"\\\\\" + appName;\n        return result;\n    }\n}\n\nstring to_string(const vector<string> &arr, const char *sp = \", \") {\n    string str;\n    for (const auto &element : arr) {\n        str += element;\n        str += sp;\n    }\n    if (!str.empty()) {\n        str.erase(str.length() - strlen(sp));\n    }\n    return str;\n}\n\nvoid functionalTest(MMKV *mmkv, bool decodeOnly) {\n    if (!decodeOnly) {\n        mmkv->set(true, \"bool\");\n    }\n    cout << \"bool = \" << mmkv->getBool(\"bool\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(1024, \"int32\");\n    }\n    cout << \"int32 = \" << mmkv->getInt32(\"int32\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<uint32_t>::max(), \"uint32\");\n    }\n    cout << \"uint32 = \" << mmkv->getUInt32(\"uint32\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<int64_t>::min(), \"int64\");\n    }\n    cout << \"int64 = \" << mmkv->getInt64(\"int64\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<uint64_t>::max(), \"uint64\");\n    }\n    cout << \"uint64 = \" << mmkv->getUInt64(\"uint64\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(3.14f, \"float\");\n    }\n    cout << \"float = \" << mmkv->getFloat(\"float\") << endl;\n\n    if (!decodeOnly) {\n        mmkv->set(numeric_limits<double>::max(), \"double\");\n    }\n    cout << \"double = \" << mmkv->getDouble(\"double\") << endl;\n    if (!decodeOnly) {\n\n        mmkv->set(\"Hello, MMKV-微信 for Win32\", \"string\");\n    }\n    string result;\n    mmkv->getString(\"string\", result);\n    cout << \"string = \" << result << endl;\n}\n\nconstexpr auto keyCount = 10000;\nconstexpr auto threadCount = 10;\nstatic const string MMKV_ID = \"thread_test\";\nvector<string> arrIntKeys;\nvector<string> arrStringKeys;\n\nDWORD WINAPI threadFunction(LPVOID lpParam) {\n    auto threadIndex = (size_t) lpParam;\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID);\n    mmkv->lock();\n    cout << \"thread \" << threadIndex << \" starts\" << endl;\n    mmkv->unlock();\n\n    auto segmentCount = keyCount / threadCount;\n    auto startIndex = segmentCount * threadIndex;\n    for (auto index = startIndex; index < startIndex + segmentCount; index++) {\n        mmkv->set(index, arrIntKeys[index]);\n        mmkv->set(\"str-\" + to_string(index), arrStringKeys[index]);\n    }\n\n    mmkv->lock();\n    cout << \"thread \" << threadIndex << \" ends\" << endl;\n    mmkv->unlock();\n    return 0;\n}\n\nvoid threadTest() {\n\n    HANDLE threadHandles[threadCount] = {0};\n    for (size_t index = 0; index < threadCount; index++) {\n        threadHandles[index] = CreateThread(nullptr, 0, threadFunction, (LPVOID) index, 0, nullptr);\n    }\n    WaitForMultipleObjects(threadCount, threadHandles, true, INFINITE);\n\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID);\n    cout << \"total count \" << mmkv->count() << endl;\n}\n\nvoid brutleTest() {\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID);\n    for (size_t i = 0; i < keyCount; i++) {\n        mmkv->set(i, arrIntKeys[i]);\n        mmkv->set(\"str-\" + to_string(i), arrStringKeys[i]);\n    }\n}\n\nvoid processTest() {\n    constexpr auto processCount = 2;\n    STARTUPINFO si[processCount] = {0};\n    PROCESS_INFORMATION pi[processCount] = {0};\n\n    for (auto index = 0; index < processCount; index++) {\n        si[index].cb = sizeof(si[0]);\n    }\n\n    wchar_t path[MAX_PATH] = {0};\n    GetModuleFileName(nullptr, path, MAX_PATH);\n    PathRemoveFileSpec(path);\n    PathAppend(path, L\"Win32DemoProcess.exe\");\n\n    HANDLE processHandles[processCount] = {0};\n    for (auto index = 0; index < processCount; index++) {\n        if (!CreateProcess(path, nullptr, nullptr, nullptr, false, 0, nullptr, nullptr, &si[index], &pi[index])) {\n            cout << \"CreateProcess failed: \" << GetLastError() << endl;\n            continue;\n        }\n        processHandles[index] = pi[index].hProcess;\n    }\n\n    WaitForMultipleObjects(processCount, processHandles, true, INFINITE);\n\n    for (auto index = 0; index < processCount; index++) {\n        CloseHandle(pi[index].hProcess);\n        CloseHandle(pi[index].hThread);\n    }\n\n    auto mmkv = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS);\n    cout << \"total count of process_test: \" << mmkv->count() << endl;\n}\n\nsize_t getpagesize(void) {\n    SYSTEM_INFO system_info;\n    GetSystemInfo(&system_info);\n    return system_info.dwPageSize;\n}\n\nvoid cornetSizeTest() {\n    auto cryptKey = string(\"aes\");\n    auto mmkv = MMKV::mmkvWithID(\"cornerSize\", MMKV_MULTI_PROCESS, &cryptKey);\n    mmkv->clearAll();\n    auto size = getpagesize() - 2;\n    size -= 4;\n    string key = \"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    mmkv::MMBuffer value(size);\n    mmkv->set(value, key);\n    mmkv->trim();\n}\n\nvoid fastRemoveCornetSizeTest() {\n    auto cryptKey = string(\"aes\");\n    auto mmkv = MMKV::mmkvWithID(\"fastRemoveCornerSize\", MMKV_MULTI_PROCESS, &cryptKey);\n    mmkv->clearAll();\n    int64_t size = getpagesize() - 4;\n    size -= 4;\n    string key = \"key\";\n    int64_t keySize = 3LL + 1LL;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    size -= (keySize + 1); // total size of fast remove\n    size /= 16;\n    mmkv::MMBuffer value(size);\n    auto ptr = (char *) value.getPtr();\n    for (size_t i = 0; i < value.length(); i++) {\n        ptr[i] = 'A';\n    }\n    for (int i = 0; i < 16; i++) {\n        mmkv->set(value, key); // when a full write back is occur, here's corruption happens\n        mmkv->removeValueForKey(key);\n    }\n\n}\nvoid testBackup() {\n    auto aesKey = string(\"cryptKey\");\n    auto mmapID = string(\"testEncrypt\");\n    auto mmkvRoot = MMKV::getRootDir();\n    auto pos = mmkvRoot.rfind(L\"\\\\\");\n    pos++;\n    auto rootDir = mmkvRoot.replace(pos, wstring::npos, L\"mmkv_backup\");\n    //wcout << \"backup dir: \" << mmkvRoot << endl;\n    MMKV *mmkv = nullptr;\n    //mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n    //mmkv->close();\n\n\tauto ret = MMKV::backupOneToDirectory(mmapID, rootDir);\n    printf(\"backup one return %d\\n\", ret);\n    if (ret) {\n        mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey, &rootDir);\n        cout << \"after backup allKeys: \" << ::to_string(mmkv->allKeys()) << endl;\n\n\t\t// otherwise it will fail in MMKV::backupAllToDirectory()\n        mmkv->close();\n    }\n\n    auto count = MMKV::backupAllToDirectory(rootDir);\n    printf(\"backup all count: %zu\\n\", count);\n    if (count > 0) {\n        mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey, &rootDir);\n        cout << \"check on backup [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n        \n        mmkv = MMKV::mmkvWithID(\"thread_test\", MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n        cout << \"check on backup [\" << mmkv->mmapID() << \"] allKeys count: \" << mmkv->count() << endl;\n\n        mmkv = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS, nullptr, &rootDir);\n        cout << \"check on backup [\" << mmkv->mmapID() << \"] allKeys count: \" << mmkv->count() << endl;\n    }\n}\n\nvoid testRestore() {\n    auto aesKey = string(\"cryptKey\");\n    auto mmapID = string(\"testEncrypt\");\n    auto mmkvRoot = MMKV::getRootDir();\n    auto pos = mmkvRoot.rfind(L\"\\\\\");\n    pos++;\n    auto rootDir = mmkvRoot.replace(pos, wstring::npos, L\"mmkv_backup\");\n    //wcout << \"restore dir: \" << mmkvRoot << endl;\n    auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n    mmkv->set((size_t)__LINE__, \"test_restore_key\");\n    cout << \"before restore [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n\n    auto ret = MMKV::restoreOneFromDirectory(mmapID, rootDir);\n    printf(\"restore one return %d\\n\", ret);\n    if (ret) {\n        cout << \"after restore [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n    }\n\n    auto count = MMKV::restoreAllFromDirectory(rootDir);\n    printf(\"restore all count: %zu\\n\", count);\n    if (count > 0) {\n        mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n        cout << \"check on restore [\" << mmkv->mmapID() << \"] allKeys: \" << ::to_string(mmkv->allKeys(), \",\\n\") << endl;\n\n        mmkv = MMKV::mmkvWithID(\"thread_test\");\n        cout << \"check on restore [\" << mmkv->mmapID() << \"] allKeys count: \" << mmkv->count() << endl;\n\n        mmkv = MMKV::mmkvWithID(\"process_test\", MMKV_MULTI_PROCESS);\n        cout << \"check on restore [\" << mmkv->mmapID() << \"] allKeys count: \" << mmkv->count() << endl;\n    }\n}\n\nvoid testAutoExpire() {\n    string mmapID = \"testAutoExpire\";\n    // disable auto expire by config\n    auto config = MMKVConfig();\n    config.enableKeyExpire = false;\n    config.recover = OnErrorRecover;\n    // config.itemSizeLimit = 1;\n    auto mmkv = MMKV::mmkvWithID(mmapID, config);\n    mmkv->clearAll();\n    mmkv->trim();\n    mmkv->disableAutoKeyExpire(); // this call become a no-op\n\n    mmkv->set(true, \"auto_expire_key_1\");\n\n    // enable auto expire by config\n    mmkv->close();\n    config.enableKeyExpire = true;\n    config.expiredInSeconds = 1;\n    mmkv = MMKV::mmkvWithID(mmapID, config);\n    mmkv->enableAutoKeyExpire(1); // this call become a no-op\n\n    mmkv->set(\"never_expire_key_1\", \"never_expire_key_1\", MMKV::ExpireNever);\n\n    Sleep(2 * 1000);\n    assert(mmkv->containsKey(\"auto_expire_key_1\") == false);\n    assert(mmkv->containsKey(\"never_expire_key_1\") == true);\n\n    mmkv->removeValueForKey(\"never_expire_key_1\");\n    mmkv->enableAutoKeyExpire(MMKV::ExpireNever);\n    mmkv->set(\"never_expire_key_1\", \"never_expire_key_1\");\n    mmkv->set(true, \"auto_expire_key_1\", 1);\n    Sleep(2 * 1000);\n    assert(mmkv->containsKey(\"never_expire_key_1\") == true);\n    assert(mmkv->containsKey(\"auto_expire_key_1\") == false);\n\n    auto count = mmkv->count(true);\n    cout << \"count all non expire keys: \" << count << endl;\n    auto allKeys = mmkv->allKeys(true);\n    cout << \"all non expire keys: \" << ::to_string(allKeys) << endl;\n}\n\nvoid testExpectedCapacity() {\n    int len = 10000;\n    std::string value(len, '0');\n    value = \"容量\" + value;\n    cout << \"value length = \" << value.size() << endl;\n    std::string key = \"key\";\n    // if you know exactly the sizes of key and value, set expectedCapacity for performance improvement\n    size_t expectedSize = key.size() + value.size();\n    auto mmkv4 = MMKV::mmkvWithID(\"testExpectedCapacity4\", MMKV_SINGLE_PROCESS, nullptr, nullptr, expectedSize);\n    // 0 times expand\n    mmkv4->set(value, key);\n\n    int count = 10;\n    expectedSize = (key.size() + value.size()) * count;\n    auto mmkv5 = MMKV::mmkvWithID(\"testExpectedCapacity5\", MMKV_SINGLE_PROCESS, nullptr, nullptr, expectedSize);\n    for (int i = 0; i < count; i++) {\n        key[0] = static_cast<char>('a' + i);\n        // 0 times expand\n        mmkv5->set(value, key);\n    }\n}\n\nvoid testRemoveStorage() {\n    string mmapID = \"test_remove\";\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_MULTI_PROCESS);\n        mmkv->set(true, \"bool\");\n    }\n    printf(\"check exist: %d\\n\", MMKV::checkExist(mmapID));\n    MMKV::removeStorage(mmapID);\n    printf(\"after remove, check exist: %d\\n\", MMKV::checkExist(mmapID));\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_MULTI_PROCESS);\n        if (mmkv->count() != 0) {\n            abort();\n        }\n    }\n\n    mmapID = \"test_remove/sg\";\n    auto rootDir = MMKV::getRootDir() + L\"_1\";\n    auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n    mmkv->set(true, \"bool\");\n    printf(\"check exist: %d\\n\", MMKV::checkExist(mmapID, &rootDir));\n    MMKV::removeStorage(mmapID, &rootDir);\n    printf(\"after remove, check exist: %d\\n\", MMKV::checkExist(mmapID, &rootDir));\n    mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, nullptr, &rootDir);\n    if (mmkv->count() != 0) {\n        abort();\n    }\n}\n\nvoid setReadOnly(const MMKVPath_t& path, bool readOnly) {\n    // Get the current file attributes\n    DWORD attributes = GetFileAttributes(path.c_str());\n    if (attributes == INVALID_FILE_ATTRIBUTES) {\n        // If the function fails, print an error message\n        DWORD error = GetLastError();\n        printf(\"Failed to get file attributes. Error code: %lu\\n\", error);\n        return;\n    }\n\n    // alter the read-only attribute\n    if (readOnly) {\n        attributes |= FILE_ATTRIBUTE_READONLY;\n    } else {\n        attributes &= ~FILE_ATTRIBUTE_READONLY;\n    }\n    // Set the file attributes to the new value\n    if (!SetFileAttributes(path.c_str(), attributes)) {\n        // If the function fails, print an error message\n        DWORD error = GetLastError();\n        printf(\"Failed to set file attributes. Error code: %lu\\n\", error);\n    }\n}\n\nvoid testReadOnly() {\n    string mmapID = \"testReadOnly\";\n    string aesKey = \"ReadOnly+Key\";\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, &aesKey);\n        functionalTest(mmkv, false);\n        mmkv->close();\n    }\n\n    auto path = MMKV::getRootDir() + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID);\n    setReadOnly(path, true);\n    auto crcPath = path + L\".crc\";\n    setReadOnly(crcPath, true);\n    {\n        auto mmkv = MMKV::mmkvWithID(mmapID, (MMKV_SINGLE_PROCESS | MMKV_READ_ONLY), &aesKey);\n        functionalTest(mmkv, true);\n\n        // also check if it tolerate update operations without crash\n        functionalTest(mmkv, false);\n\n        mmkv->close();\n    }\n    setReadOnly(path, false);\n    setReadOnly(crcPath, false);\n}\n\nvoid testNameSpace() {\n    wstring rootDir = getAppDataRoaming(L\"Tencent\", L\"微信-mmkv_namespace\");\n    // wstring rootDir = L\"D:\\\\mmkv\";\n    auto ns = MMKV::nameSpace(rootDir);\n    auto kv = ns.mmkvWithID(\"test_namespace\");\n    functionalTest(kv, false);\n}\n\nvoid testImport() {\n    string mmapID = \"test_import_src\";\n    auto src = MMKV::mmkvWithID(mmapID);\n    src->set(true, \"bool\");\n    src->set(std::numeric_limits<int32_t>::min(), \"int\");\n    src->set(std::numeric_limits<uint64_t>::max(), \"long\");\n    src->set(\"test import\", \"string\");\n\n    auto dst = MMKV::mmkvWithID(\"test_import_dst\");\n    dst->clearAll();\n    dst->enableAutoKeyExpire(1);\n    dst->set(true, \"bool\");\n    dst->set(-1, \"int\");\n    dst->set(0, \"long\");\n    dst->set(mmapID, \"string\");\n\n    auto count = dst->importFrom(src);\n    assert(count == 4 && dst->count() == 4);\n    assert(dst->getBool(\"bool\"));\n    assert(dst->getInt32(\"int\") == std::numeric_limits<int32_t>::min());\n    assert(dst->getUInt64(\"long\") == std::numeric_limits<uint64_t>::max());\n    string result;\n    dst->getString(\"string\", result);\n    assert(result == \"test import\");\n    Sleep(2 * 1000);\n    assert(dst->count(true) == 0);\n}\n\nMMKV* testMMKV(const string& mmapID, const string* cryptKey, bool aes256, bool decodeOnly, const wstring* rootPath) {\n    MMKV* kv = MMKV::mmkvWithID(mmapID, MMKV_SINGLE_PROCESS, cryptKey, rootPath, 0, aes256);\n    functionalTest(kv, decodeOnly);\n    return kv;\n}\n\nvoid testReKey() {\n    string mmapID = \"test/AES_reKey1\";\n    MMKV* kv = testMMKV(mmapID, nullptr, false, false, nullptr);\n\n    string cryptKey = \"Key_seq_1\";\n    kv->reKey(cryptKey);\n    kv->clearMemoryCache();\n    testMMKV(mmapID, &cryptKey, false, true, nullptr);\n\n    string cryptKey2 = \"Key_Seq_Very_Looooooooong\";\n    kv->reKey(cryptKey2, true);\n    kv->clearMemoryCache();\n    testMMKV(mmapID, &cryptKey2, true, true, nullptr);\n\n    kv->reKey(string());\n    kv->clearMemoryCache();\n    testMMKV(mmapID, nullptr, false, true, nullptr);\n}\n\n// Helper to write raw bytes or convert to WideChar based on destination\nvoid WriteUTF8ToStream(const char* utf8_str) {\n    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);\n    DWORD consoleMode;\n\n    if (GetConsoleMode(hOut, &consoleMode)) {\n        // --- CONSOLE: Convert to UTF-16 and write ---\n        int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);\n        if (wlen > 0) {\n            wchar_t* wbuf = (wchar_t*)malloc(wlen * sizeof(wchar_t));\n            if (wbuf) {\n                MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wbuf, wlen);\n                WriteConsoleW(hOut, wbuf, wlen - 1, NULL, NULL);\n                free(wbuf);\n            }\n        }\n    }\n    else {\n        // --- FILE/PIPE: Write raw UTF-8 bytes ---\n        DWORD bytesWritten;\n        WriteFile(hOut, utf8_str, (DWORD)strlen(utf8_str), &bytesWritten, NULL);\n    }\n}\n\n// Main VarArg Function\nvoid PrintUTF8(const char* format, ...) {\n    va_list args;\n\n    // 1. Calculate required length\n    // We pass NULL/0 to vsnprintf just to get the required size (excluding null terminator)\n    va_start(args, format);\n    int len = vsnprintf(NULL, 0, format, args);\n    va_end(args);\n\n    if (len < 0) return; // Encoding error or invalid format\n\n    // 2. Allocate buffer (len + 1 for null terminator)\n    // Using malloc ensures we don't overflow the stack with huge strings\n    char* buf = (char*)malloc(len + 1);\n    if (!buf) return; // Out of memory\n\n    // 3. Format the string into the buffer\n    va_start(args, format);\n    vsnprintf(buf, len + 1, format, args);\n    va_end(args);\n\n    // 4. Send to output helper\n    WriteUTF8ToStream(buf);\n\n    // 5. Cleanup\n    free(buf);\n}\n\nstatic void\nLogHandler(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) {\n\n    auto desc = [level] {\n        switch (level) {\n            case MMKVLogDebug:\n                return \"D\";\n            case MMKVLogInfo:\n                return \"I\";\n            case MMKVLogWarning:\n                return \"W\";\n            case MMKVLogError:\n                return \"E\";\n            default:\n                return \"N\";\n        }\n    }();\n    PrintUTF8(\"redirecting-[%s] <%s:%d::%s> %s\\n\", desc, file, line, function, message.c_str());\n}\n\nint main() {\n    // Get the original global locale\n    std::locale originalLocale = std::locale::global(std::locale());\n    std::cout << \"Original locale: \" << originalLocale.name() << std::endl;\n    //locale::global(locale(\"\"));\n    //locale::global(locale(\".UTF8\"));\n    //SetConsoleOutputCP(CP_UTF8);\n    //SetConsoleCP(CP_UTF8);\n\n    std::locale newLocale = std::locale();\n    std::cout << \"New locale: \" << newLocale.name() << std::endl;\n    // wcout.imbue(locale(\"\"));\n\n    srand((unsigned) GetTickCount64());\n\n    // test NameSpace before initializeMMKV()\n    testNameSpace();\n\n    wstring rootDir = getAppDataRoaming(L\"Tencent\", L\"微信-MMKV\");\n    MMKV::initializeMMKV(rootDir, MMKVLogInfo, LogHandler);\n    //MMKV::setLogLevel(MMKVLogNone);\n    //MMKV::registerLogHandler(LogHandler);\n\n    //auto mmkv = MMKV::defaultMMKV();\n    auto cryptKey = string(\"cryptKey\");\n    auto mmkv = MMKV::mmkvWithID(\"testEncrypt\", MMKV_SINGLE_PROCESS, &cryptKey);\n    functionalTest(mmkv, false);\n\n    for (size_t index = 0; index < keyCount; index++) {\n        arrIntKeys.push_back(\"int-\" + to_string(index));\n        arrStringKeys.push_back(\"string-\" + to_string(index));\n    }\n\n    //fastRemoveCornetSizeTest();\n    cornetSizeTest();\n    //brutleTest();\n    threadTest();\n    processTest();\n    testBackup();\n    testRestore();\n    testAutoExpire();\n    testExpectedCapacity();\n    testRemoveStorage();\n    testReadOnly();\n    testImport();\n    testReKey();\n}\n"
  },
  {
    "path": "Win32/Win32Demo/Win32Demo.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>15.0</VCProjectVersion>\r\n    <ProjectGuid>{532303C4-38D9-45D3-B925-18056C66CD01}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>Win32Demo</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>..\\$(IntDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n    <PostBuildEvent />\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>..\\$(IntDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n    <PostBuildEvent />\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"pch.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"pch.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Win32Demo.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Win32/Win32Demo/Win32Demo.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"pch.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"pch.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Win32Demo.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Win32/Win32Demo/pch.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pch.h\"\n\n// In general, ignore this file, but keep it around if you are using pre-compiled headers.\n"
  },
  {
    "path": "Win32/Win32Demo/pch.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef PCH_H\n#define PCH_H\n\n#define NOMINMAX // undefine max/min\n\n#include <Shlwapi.h>\n\n#endif //PCH_H\n"
  },
  {
    "path": "Win32/Win32DemoProcess/Win32DemoProcess.cpp",
    "content": "﻿/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pch.h\"\n\n#include <MMKV/MMKV.h>\n#include <chrono>\n#include <iostream>\n#include <string>\n\nusing namespace std;\n\nwstring getAppDataRoaming(const wstring &company, const wstring &appName) {\n    wchar_t roaming[MAX_PATH] = {0};\n    auto size = GetEnvironmentVariable(L\"appdata\", roaming, MAX_PATH);\n    if (size >= MAX_PATH || size == 0) {\n        cout << \"fail to get %appdata%: \" << GetLastError() << endl;\n        return L\"\";\n    } else {\n        wstring result(roaming, size);\n        result += L\"\\\\\" + company;\n        result += L\"\\\\\" + appName;\n        return result;\n    }\n}\n\nconstexpr auto keyCount = 1000;\nstatic const string MMKV_ID = \"process_test\";\nvector<string> arrIntKeys;\nvector<string> arrStringKeys;\n\nvoid brutleTest(DWORD processID) {\n    using hclock = chrono::high_resolution_clock;\n    auto start = hclock::now();\n\n    auto mmkv = MMKV::mmkvWithID(MMKV_ID, MMKV_MULTI_PROCESS);\n    for (size_t i = 0; i < keyCount; i++) {\n        // auto mmkv = MMKV::mmkvWithID(MMKV_ID + \".\" + std::to_string(i), MMKV_MULTI_PROCESS);\n        mmkv->set(i, arrIntKeys[i]);\n        mmkv->set(\"str-\" + to_string(i), arrStringKeys[i]);\n        mmkv->getInt32(arrIntKeys[i]);\n        string result;\n        mmkv->getString(arrStringKeys[i], result);\n    }\n\n    auto finish = hclock::now();\n    auto used = chrono::duration_cast<chrono::milliseconds>(finish - start).count();\n    mmkv->lock();\n    cout << endl << processID << \": \" << used << \" ms\\n\";\n    mmkv->unlock();\n}\n\nint main() {\n    //locale::global(locale(\".UTF8\"));\n    //SetConsoleOutputCP(CP_UTF8);\n    //SetConsoleCP(CP_UTF8);\n    //wcout.imbue(locale(\"\"));\n    srand(GetTickCount());\n\n    wstring rootDir = getAppDataRoaming(L\"Tencent\", L\"微信-MMKV\");\n    MMKV::initializeMMKV(rootDir);\n\n    auto processID = GetCurrentProcessId();\n    cout << processID << \": started\\n\";\n\n    for (size_t index = 0; index < keyCount; index++) {\n        arrIntKeys.push_back(\"int-\" + to_string(index));\n        arrStringKeys.push_back(\"string-\" + to_string(index));\n    }\n    brutleTest(processID);\n\n    cout << processID << \": ended\\n\";\n}\n"
  },
  {
    "path": "Win32/Win32DemoProcess/Win32DemoProcess.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>15.0</VCProjectVersion>\r\n    <ProjectGuid>{86D6C605-81F3-407B-A766-690EDF4CD346}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>Win32DemoProcess</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalDependencies>MMKV.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <AdditionalLibraryDirectories>..\\$(IntDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalDependencies>MMKV.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <AdditionalLibraryDirectories>..\\$(IntDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <AdditionalIncludeDirectories>$(OutDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <LanguageStandard>stdcpp17</LanguageStandard>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>\r\n      <AdditionalDependencies>MMKV.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"pch.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"pch.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Win32DemoProcess.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Win32/Win32DemoProcess/Win32DemoProcess.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"pch.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"pch.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Win32DemoProcess.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Win32/Win32DemoProcess/pch.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pch.h\"\n"
  },
  {
    "path": "Win32/Win32DemoProcess/pch.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef PCH_H\n#define PCH_H\n\n#endif //PCH_H\n"
  },
  {
    "path": "flutter/.gitignore",
    "content": ".DS_Store\n.dart_tool/\n\n.packages\n.pub/\n\nbuild/\ndoc/\n\n.flutter-plugins\n.flutter-plugins-dependencies"
  },
  {
    "path": "flutter/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 84f3d28555368a70270e9ac8390a9441df95e752\n  channel: stable\n\nproject_type: plugin\n"
  },
  {
    "path": "flutter/.vscode/launch.json",
    "content": "{\r\n    // Use IntelliSense to learn about possible attributes.\r\n    // Hover to view descriptions of existing attributes.\r\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\r\n    \"version\": \"0.2.0\",\r\n    \"configurations\": [\r\n        {\r\n            \"name\": \"mmkv\",\r\n            \"cwd\": \"mmkv\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv (profile mode)\",\r\n            \"cwd\": \"mmkv\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv (release mode)\",\r\n            \"cwd\": \"mmkv\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_android\",\r\n            \"cwd\": \"mmkv_android\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_android (profile mode)\",\r\n            \"cwd\": \"mmkv_android\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_android (release mode)\",\r\n            \"cwd\": \"mmkv_android\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ios\",\r\n            \"cwd\": \"mmkv_ios\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ios (profile mode)\",\r\n            \"cwd\": \"mmkv_ios\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ios (release mode)\",\r\n            \"cwd\": \"mmkv_ios\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ohos\",\r\n            \"cwd\": \"mmkv_ohos\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ohos (profile mode)\",\r\n            \"cwd\": \"mmkv_ohos\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_ohos (release mode)\",\r\n            \"cwd\": \"mmkv_ohos\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_platform_interface\",\r\n            \"cwd\": \"mmkv_platform_interface\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_platform_interface (profile mode)\",\r\n            \"cwd\": \"mmkv_platform_interface\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_platform_interface (release mode)\",\r\n            \"cwd\": \"mmkv_platform_interface\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_win32\",\r\n            \"cwd\": \"mmkv_win32\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_win32 (profile mode)\",\r\n            \"cwd\": \"mmkv_win32\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"mmkv_win32 (release mode)\",\r\n            \"cwd\": \"mmkv_win32\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"example\",\r\n            \"cwd\": \"mmkv\\\\example\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\"\r\n        },\r\n        {\r\n            \"name\": \"example (profile mode)\",\r\n            \"cwd\": \"mmkv\\\\example\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"profile\"\r\n        },\r\n        {\r\n            \"name\": \"example (release mode)\",\r\n            \"cwd\": \"mmkv\\\\example\",\r\n            \"request\": \"launch\",\r\n            \"type\": \"dart\",\r\n            \"flutterMode\": \"release\"\r\n        },\r\n        {\r\n            \"name\": \"Flutter (lib\\\\main.dart)\",\r\n            \"type\": \"dart\",\r\n            \"request\": \"launch\",\r\n            \"cwd\": \"mmkv\\\\example\",\r\n            \"program\": \"lib\\\\main.dart\"\r\n        },\r\n        {\r\n            \"name\": \"Flutter (lib/main.dart)\",\r\n            \"type\": \"dart\",\r\n            \"request\": \"launch\",\r\n            \"cwd\": \"mmkv/example\",\r\n            \"program\": \"lib/main.dart\"\r\n        }\r\n    ]\r\n}"
  },
  {
    "path": "flutter/.vscode/settings.json",
    "content": "{\r\n    \"files.associations\": {\r\n        \"xstring\": \"cpp\",\r\n        \"xlocale\": \"cpp\",\r\n        \"algorithm\": \"cpp\",\r\n        \"functional\": \"cpp\",\r\n        \"type_traits\": \"cpp\",\r\n        \"xtr1common\": \"cpp\",\r\n        \"xutility\": \"cpp\"\r\n    },\r\n    \"cmake.ignoreCMakeListsMissing\": true\r\n}"
  },
  {
    "path": "flutter/mmkv/CHANGELOG.md",
    "content": "# MMKV for Flutter Change Log\n## v2.4.0 / 2026-03-18\n* **Feature:** Refactored the callback system into a unified `MMKVHandler` interface. Added `onMMKVContentLoadSuccessfully` callback.\n* **Feature:** Added `MMKVConfig` for all-in-one instance configuration.\n* **Feature:** Added `defaultMMKV(config)` for creating the default instance with full configuration.\n* **Fix:** Robust check on encryption mode.\n* Android: Fix `fcntl()` OFD lock failure on ashmem.\n* iOS: Fixed a memory leak on getting `NameSpace` instance.\n\n## v2.3.0 / 2025-12-03\nThis release is a **breaking change** and introduces **AES-256 encryption** for enhanced security.\n* **Feature:** Added **AES-256 encryption** functionality. To upgrade an existing encrypted MMKV instance to AES-256, first load it using the old key. Then, call the `reKey()` method with the new key and set the `aes256` parameter to `true`, e.g., `reKey(newKey, aes256: true)`. After this, you should use the new key for all future loads of this instance.\n* **Fix:** Resolved a crash that occurred when loading an empty file in `ReadOnly` mode.\n* **Fix:** Added protection against the `weakly_canonical()` exception caused by an invalid file path.\n* **Fix:** Fixed an issue where the file size could change during multi-process loading.\n* **Fix:** Corrected a bug where a single key could be overridden incorrectly when upgrading from a v1.1.x version.\n\n## v2.2.4 / 2025-09-25\nThis is a hotfix release mainly for iOS/macOS CocoaPods users.\n\n* Improve the performance of MMBuffer a little bit in some cases.\n* iOS/macOS: Make MMKV and MMKVCore podspec define modules.\n\n## v2.2.3 / 2025-08-20\nThis is a feature release that brings **full desktop support to Flutter**.\n* **Added Full Desktop Support**: MMKV for Flutter now officially supports **Windows**, **macOS**, and **Linux**.\n* **Example App**: The example application has been updated to run on all new desktop platforms.\n\n## v2.2.2 / 2025-05-08\nThis is a hot fix version mainly **for Android platforms**. It’s highly recommended for v2.2.0~v2.2.1 users.\n* Improve file lock consistency for Mayfly FD MMKV instances.\n* Fix a potential Android ANR on multi-process mode MMKV init/creation.\n\n## v2.2.1 / 2025-4-25\n* Add `importFrom()`.\n* Fix Android initialize bug.\n\n## v2.2.0 / 2025-04-24\n* Add `checkExist()`.\n* Add `isFileValid()`.\n* Add `groupPath()` for iOS.\n* Reduce file descriptor usage by a half and more.\n* Improve multi-process access efficiency by about 20%.\n* Fix `checkContentChangedByOuterProcess()` not working bug.\n\n## v2.1.1 / 2025-03-06\nThis is a **critical hotfix** for v2.1.0. It's highly recommended to upgrade as soon as possible.\n* Fix a bug that MMKV instance from non-namespace fails to create.\n\n## v2.1.0 / 2025-02-18\n* **Breaking change**: Migrate legacy MMKV in a custom directory to normal MMKV. Historically Android/OHOS mistakenly use mmapKey as mmapID, which will be problematic with the `NameSpace` feature. Starting from v2.1.0, MMKV will try to migrate them back to normal when possible.  \n  It's highly recommended that you **upgrade to v2.0.2 first** with **forward support** of normal MMKV in a custom directory.\n* Improve inter-process locking by using `F_OFD_SETLK` instead of `F_SETLK` in Android/OHOS.\n* Improve directory creation on `ReadOnly` mode.\n* Add protection from bad disk records of MMKV files.\n* Fix FileLock not being unlocked on destruction.\n\n## v2.0.2 / 2024-12-27\n* Add version limitation of < v2.1.0 on MMKV platform plugins.\n\n## v2.0.1 / 2024-10-25\n* Fix breaking changes on platform interface package.\n\n## v1.3.10 / 2024-10-25\n* Rollback some breaking changes on platform interface package.\n\n## v2.0.0 / 2024-10-21\n* Support read-only mode.\n* Add add log/error/content-change callback for Flutter & ArtTS\n* Bump Android minSdkVersion to 23.\n* Drop 32-bit arch support.\n\n## v1.3.9 / 2024-07-26\nThis will be **the last LTS release of MMKV for Flutter**.\n* Modify the dependency of native lib in a way that no Dart package update is needed for any LTS release in the future.\n* Fix a data corruption bug on an encrypted MMKV with only one key value stored.\n* Make encryption more resilient from brute force cracking.\n* Fix a bug that pthread_mutex is not being destroyed correctly.\n* Android: Use an alternative way to get the process name to avoid potential App review issues.\n* Android: Upgrade to NDK 26.3.11579264.\n\n## v1.3.8 / 2024-07-12\n* Add support for **HarmonyOS NEXT**.\n\n## v1.3.7 / 2024-07-08\n**Sync with Latest Android Native Binary:**\n\nThis Long Term Support (LTS) release primarily reintroduces support for the ARMv7 architecture and lowers the minimum SDK version requirement to 21. Please note that only critical bug fixes will be applied to the 1.3.x series. New features will be introduced in version 2.0 and later, which will discontinue support for 32-bit architectures and raise the minimum SDK version requirement to 23.\n\n## v1.3.6 / 2024-07-05\n* Android: MMKV will try to load libmmkv.so before Dart code, to reduce the error of loading library in Android.\n* Android: Use the latest ashmem API if possible.\n* Android: Use the latest API to get the device API level.\n\n## v1.3.5 / 2024-04-24\n* Migrate to federated plugins to avoid the iOS rename headache. From now on, no more renaming from `mmkv` to `mmkvflutter` is needed.\n* Bump iOS Deployment Target to iOS 12.\n* Bump Android minSdkVersion to 23.\n\n## v1.3.4 / 2024-03-15\n* Make `trim()` more robust in multi-process mode.\n\n## v1.3.3 / 2024-01-25\n* Add `removeStorage()` static method to safely delete underlying files of an MMKV instance.\n* Add protection from a potential crash of a multi-process MMKV loading due to the MMKV file not being valid.\n* Add back the lazy load feature. It was first introduced in v1.2.16. But it was rollbacked in v1.3.0 of potential ANR & file corruption. Now it's clear that the bug was caused by something else, it's time to bring it back.\n* **Optimize loading speed** by using shared inter-process lock unless there's a need to truncate the file size, which is rare.\n* Make these two lately added features **more robust**: customizing the initial file size & optimizing write speed when there's only one key inside MMKV.\n* On the Xcode 15 build, an App will crash on iOS 14 and below. Previously we have recommended some workarounds (check the v1.3.2 release note for details). Now you can use Xcode 15.1 to fix this issue.\n\n## v1.3.2 / 2023-11-20\nAmong most of the features added in this version, the credit goes to @kaitian521.\n\n* Add the feature of customizing the **initial file size** of an MMKV instance.\n* **Optimize write speed** when there's only one key inside MMKV, the new key is the same as the old one, and MMKV is in `SINGLE_PROCESS_MODE`.\n* **Optimize write speed** by overriding from the beginning of the file instead of append in the back, when there's zero key inside MMKV, and MMKV is in `SINGLE_PROCESS_MODE`.\n* Add the feature of `clearAll()` with keeping file disk space unchanged, **reducing the need to expand file size** on later insert & update operations. This feature is off by default, you will have to call it with relative params or newly added methods. \n* Add the feature of **comparing values before setting/encoding** on the same key.\n* Fix a potential bug that the MMKV file will be invalid state after a successful expansion but a failure `zeroFill()`, will lead to a crash.\n* Fix a potential crash due to other module/static lib turn-off **RTTI**, which will cause MMKV to fail to catch `std::exception`.\n* Fix several potential crash due to the MMKV file not being valid.\n* Android: Use the `-O2` optimization level by default, which will **reduce native lib size** and improve read/write speed a little bit.\n* Android: Experimantal use `@fastNative` annotation on `enableCompareBeforeCompare()` to speed up JNI call.\n* Turn-off mlock() protection in background on iOS 13+. We have **verified it on WeChat** that the protection is no longer needed from at least iOS 13. Maybe iOS 12 or older is also not needed, but we don't have the chance to verify that because WeChat no longer supports iOS 12.\n\n#### Known Issue\n* On Xcode 15 build, App will crash on iOS 14 and below. The bug is introduced by Apple's new linker. The official solutions provided by Apple are either:\n  * Drop the support of iOS 14.\n  * Add `-Wl,-weak_reference_mismatches,weak` or `-Wl,-ld_classic` options to the `OTHER_LDFLAGS` build setting of Xcode 15. Note that these options are **not recognized** by older versions of Xcode.\n  * Use older versions of Xcode, or **wait for Xcode 15.2**.\n\n## v1.3.1 / 2023-8-11\nThis is a hotfix version. It's **highly recommended** that v1.2.16 & v1.3.0 users upgrade as soon as possible.\n* Fix a critical bug that might cause multi-process MMKV corrupt. This bug was introduced in v1.2.16.\n* Add the ability to filter expired keys on `count()` & `allKeys()` methods when auto key expiration is turn on.\n* Reduce the `msync()` call on newly created MMKV instances.\n\n## v1.3.0 / 2023-06-14\n* Add auto key expiration feature. Note that this is a breaking change, once upgrade to auto expiration, the MMKV file is not valid for older versions of MMKV (v1.2.16 and below) to correctly operate.\n* Roll back the lazy load optimization due to reported ANR issues. It was introduced in v1.2.16.\n* The version is now the same as the MMKV native library.\n* Starting from v1.3.0, Flutter for Android will use `com.tencent:mmkv`. Previously it's `com.tencent:mmkv-static`. It's the same as `com.tencent:mmkv` starting from v1.2.11.\n\n## v1.2.17 / 2023-04-20\n* Optimization: The actual file content is lazy loaded now, saving time on MMKV instance creation, and avoiding lock waiting when a lot of instances are created at the same time.\n* Fix a bug when restoring a loaded MMKV instance the meta file might mistakenly report corrupted.\n* Fix a crash on decoding an empty list.\n* Remove deprecated dependence.\n* Make the script more robust to fix the iOS Flutter plugin name.\n* Keep up with MMKV native lib v1.2.16.\n\n## v1.2.16 / 2023-01-12\n* Reduce the privacy info needed to obtain android sdkInt, avoid unnecessary risk on Android App Review.\n* Log handler now handles all logs from the very beginning, especially the logs in initialization.\n* Log handler register method is now deprecated. It's integrated with initialize().\n* Keep up with MMKV native lib v1.2.15.\n\n## v1.2.15 / 2022-08-10\n* Fix a bug that `MMKV.decodeXXX()` may return invalid results in multi-process mode.\n* Upgrade to Flutter 3.0.\n* Keep up with MMKV native lib v1.2.14.\n\n## v1.2.14 / 2022-03-30\n* Replace the deprecated `device_info` package with `device_info_plus`.\n* Keep up with MMKV native lib v1.2.13.\n\n## v1.2.13 / 2022-01-17\n* Fix a bug that a subsequential `clearAll()` call may fail to take effect in multi-process mode.\n* Hide some OpenSSL symbols to prevent link-time symbol conflict, when an App somehow also static linking OpenSSL.\n* Upgrade Android `compileSdkVersion` & `targetSdkVersion` from `30` to `31`.\n* Keep up with MMKV native lib v1.2.12.\n\n## v1.2.12 / 2021-10-26\n* Add backup & restore ability.\n* Keep up with MMKV native lib v1.2.11.\n\n## v1.2.11 / 2021-06-25\n* Bug Fixed: When building on iOS, occasionally it will fail on symbol conflict with other libs. We have renamed all public native methods to avoid potential conflict.\n* Keep up with MMKV native lib v1.2.10.\n\n## v1.2.10 / 2021-05-26\n* Bug Fixed: When calling `MMKV.encodeString()` with an empty string value on Android, `MMKV.decodeString()` will return `null`.\n* Bug Fixed: After upgrading from Flutter 1.20+ to 2.0+, calling `MMKV.defaultMMKV()` on Android might fail to load, you can try calling `MMKV.defaultMMKV(cryptKey: '\\u{2}U')` instead.\n* Keep up with MMKV native lib v1.2.9, which drops the **armeabi** arch on Android.\n\n## v1.2.9 / 2021-05-06\n* Support null-safety.\n* Upgrade to Flutter 2.0.\n* Keep up with MMKV native lib v1.2.8, which migrates the Android Native Lib to Maven Central Repository.\n* Fix `MMKV.encodeString()` crash on iOS with an empty string value.\n\n### Known Issue\n* When calling `MMKV.encodeString()` with an empty string value on Android, `MMKV.decodeString()` will return `null`. This bug will be fixed in the next version of Android Native Lib. iOS does not have such a bug.\n\n## v1.2.8 / 2020-12-25\n* Keep up with MMKV native lib v1.2.7, which fix the `MMKV.sync(false)` not being asynchronous bug.\n* Fix `MMKV.defaultMMKV()` crash on iOS simulator.\n* Fix `MMKV.defaultMMKV(cryptKey)` not encrypted as expected bug on iOS.\n\n## v1.2.7 / 2020-11-27\nFix iOS symbol not found bug.\n\n## v1.2.6 / 2020-11-27\nThe first official flutter plugin of MMKV. Most things actually work!\n"
  },
  {
    "path": "flutter/mmkv/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv/README.md",
    "content": "[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)\r\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)\r\n[![Release Version](https://img.shields.io/badge/release-2.4.0-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)\r\n[![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)\r\n\r\nMMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android** and **iOS**.\r\n\r\n# MMKV for Flutter\r\n\r\n## Features\r\n\r\n* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of native platform to achieve best performance.\r\n  * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.\r\n\r\n* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.\r\n\r\n* **Small**.\r\n  * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.\r\n  * **About 100K in binary size**: MMKV adds about 100K per architecture on App size, and much less when zipped (apk/ipa).\r\n\r\n\r\n## Getting Started\r\n\r\n### Installation\r\nAdd the following lines to `pubspec.yaml` on your app module. Then run `flutter pub get`.\r\n\r\n```yaml\r\ndependencies:\r\n  mmkv: \"^2.4.0\"\r\n```\r\n\r\nIf you already include MMKV native lib in your App, you need to upgrade to version newer than v2.0.0.  \r\n\r\n#### iOS  \r\nStarting from v1.3.5, there's **no need** to change the plugin name 'mmkv' to 'mmkvflutter'. You should remove the script (`fix_mmkv_plugin_name()`) previously added in your Podfile. \r\n\r\n#### Android  \r\nIf you previously use `com.tencent.mmkv-static` or `com.tencent.mmkv-shared` in your Android App, you should move to `com.tencent.mmkv`.\r\nAnd if your App depends on any 3rd SDK that embeds `com.tencent.mmkv-static` or `com.tencent.mmkv-shared`, you can add this lines to your `build.gradle` to avoid conflict:\r\n\r\n```gradle\r\n    dependencies {\r\n        ...\r\n\r\n        modules {\r\n            module(\"com.tencent:mmkv-static\") {\r\n                replacedBy(\"com.tencent:mmkv\", \"Using mmkv for flutter\")\r\n            }\r\n            module(\"com.tencent:mmkv-shared\") {\r\n                replacedBy(\"com.tencent:mmkv\", \"Using mmkv for flutter\")\r\n            }\r\n        }\r\n    }\r\n```\r\n\r\n### Setup\r\nYou can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.  \r\nSetup MMKV on App startup, say your `main()` function, add these lines:\r\n\r\n```dart\r\nimport 'package:mmkv/mmkv.dart';\r\n\r\nvoid main() async {\r\n\r\n  // must wait for MMKV to finish initialization\r\n  final rootDir = await MMKV.initialize();\r\n  print('MMKV for flutter with rootDir = $rootDir');\r\n\r\n  runApp(MyApp());\r\n}\r\n```\r\nNote that you have to **wait for MMKV to finish initialization** before accessing any MMKV instance.\r\n\r\n### CRUD Operations\r\n\r\n* MMKV has a global instance, that can be used directly:\r\n\r\n    ```dart\r\n    import 'package:mmkv/mmkv.dart';\r\n        \r\n    var mmkv = MMKV.defaultMMKV();\r\n    mmkv.encodeBool('bool', true);\r\n    print('bool = ${mmkv.decodeBool('bool')}');\r\n    \r\n    mmkv.encodeInt32('int32', (1<<31) - 1);\r\n    print('max int32 = ${mmkv.decodeInt32('int32')}');\r\n    \r\n    mmkv.encodeInt('int', (1<<63) - 1);\r\n    print('max int = ${mmkv.decodeInt('int')}');\r\n    \r\n    String str = 'Hello Flutter from MMKV';\r\n    mmkv.encodeString('string', str);\r\n    print('string = ${mmkv.decodeString('string')}');\r\n\r\n    str = 'Hello Flutter from MMKV with bytes';\r\n    var bytes = MMBuffer.fromList(Utf8Encoder().convert(str))!;\r\n    mmkv.encodeBytes('bytes', bytes);\r\n    bytes.destroy();\r\n\r\n    bytes = mmkv.decodeBytes('bytes')!;\r\n    print('bytes = ${Utf8Decoder().convert(bytes.asList()!)}');\r\n    bytes.destroy();\r\n    ```\r\n\r\n    As you can see, MMKV is quite easy to use.\r\n    \r\n    **Note**: If you come across to failing to load `defaultMMKV()` **on Android** after **upgrading** Flutter from 1.20+ to 2.0+, you can try passing this encryption key `'\\u{2}U'` instead.  \r\n\r\n   ```dart\r\n   var mmkv = MMKV.defaultMMKV(cryptKey: '\\u{2}U');\r\n   ```\r\n    \r\n* **Deleting & Querying**:\r\n\r\n    ```dart\r\n    var mmkv = MMKV.defaultMMKV();\r\n\r\n    mmkv.removeValue('bool');\r\n    print('contains \"bool\": ${mmkv.containsKey('bool')}');\r\n\r\n    mmkv.removeValues(['int32', 'int']);\r\n    print('all keys: ${mmkv.allKeys}');\r\n    ```\r\n\r\n* If different modules/logic need **isolated storage**, you can also create your own MMKV instance separately:\r\n\r\n    ```dart\r\n    var mmkv = MMKV(\"test\");\r\n    mmkv.encodeBool('bool', true);\r\n    print('bool = ${mmkv.decodeBool('bool')}');\r\n    ```\r\n\r\n* If **multi-process accessing** is needed，you can set `MMKV.MULTI_PROCESS_MODE` on MMKV initialization:\r\n\r\n    ```dart\r\n    var mmkv = MMKV(\"test-multi-process\", mode: MMKVMode.MULTI_PROCESS_MODE);\r\n    mmkv.encodeBool('bool', true);\r\n    print('bool = ${mmkv.decodeBool('bool')}');\r\n    ```\r\n\r\n### Supported Types\r\n* Primitive Types:\r\n  - `bool, int, double`\r\n\r\n* Classes & Collections:\r\n  - `String, List<int>, MMBuffer`\r\n\r\n### Log\r\n\r\n* By default, MMKV prints log to logcat/console, which is not convenient for diagnosing online issues. \r\nYou can setup MMKV **log redirecting** on App startup on the **native** interface of MMKV. \r\nCheckout how to do it on [Android](https://github.com/Tencent/MMKV/wiki/android_advance#log) / [iOS](https://github.com/Tencent/MMKV/wiki/ios_advance#log).\r\nDue to the current limitation of Flutter runtime, we can't redirect log on the Flutter side.\r\n\r\n* You can turn off MMKV's logging once and for all on initialization (which we strongly disrecommend).  \r\n\r\n    ```dart\r\n    final rootDir = await MMKV.initialize(logLevel: MMKVLogLevel.None);\r\n    ```\r\n### Encryption\r\n* By default MMKV stores all key-values in plain text on file, relying on Android's/iOS's sandbox to make sure the file is encrypted. Should you worry about information leaking, you can choose to encrypt MMKV.\r\n\r\n    ```dart\r\n    final encryptKey = 'MyEncryptKey';\r\n    var mmkv = MMKV(\"test-encryption\", cryptKey: encryptKey);\r\n    ```\r\n\r\n* You can change the encryption key later as you like. You can also change an existing MMKV instance from encrypted to unencrypted, or vice versa.\r\n\r\n    ```dart\r\n    // an unencrypted MMKV instance\r\n    var mmkv = MMKV(\"test-encryption\");\r\n\r\n    // change from unencrypted to encrypted with AES-128 key length\r\n    mmkv.reKey(\"Key_seq_1\");\r\n\r\n    // change encryption key with AES-256 key length\r\n    mmkv.reKey(\"Key_Seq_Very_Looooooooong\", aes256: true);\r\n\r\n    // change from encrypted to unencrypted\r\n    kmmkv.reKey(null);\r\n    ```\r\n \r\n### Customize location\r\n* By default, MMKV stores file inside `$(FilesDir)/mmkv/`. You can customize MMKV's **root directory** on App startup:\r\n\r\n    ```dart\r\n    final dir = await getApplicationSupportDirectory();\r\n    final rootDir = await MMKV.initialize(dir, rootDir: dir.path + '/mmkv_2');\r\n    print('MMKV for flutter with rootDir = $rootDir');\r\n    ```\r\n\r\n* You can even customize any MMKV instance's location:\r\n\r\n    ```dart\r\n    final dir = await getApplicationSupportDirectory();\r\n    var mmkv = MMKV('testCustomDir', rootDir: dir.path + '/mmkv_3');\r\n    ```\r\n  **Note:** It's recommended to store MMKV files **inside** your App's sandbox path. **DO NOT** store them on external storage(aka SD card). If you have to do it, you should  follow Android's [scoped storage](https://developer.android.com/preview/privacy/storage) enforcement.\r\n\r\n### Additional docs\r\nFor additional documents, checkout the [wiki](https://github.com/Tencent/MMKV/wiki/flutter_setup).\r\n\r\n## License\r\nMMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT).\r\n\r\n## Change Log\r\nCheck out the [CHANGELOG.md](https://github.com/Tencent/MMKV/blob/master/flutter/mmkv/CHANGELOG.md) for details of change history.\r\n\r\n## Contributing\r\n\r\nIf you are interested in contributing, check out the [CONTRIBUTING.md](https://github.com/Tencent/MMKV/blob/master/CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution).\r\n\r\nTo give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](https://github.com/Tencent/MMKV/blob/master/CODE_OF_CONDUCT.md).\r\n\r\n## FAQ & Feedback\r\nCheck out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).\r\n"
  },
  {
    "path": "flutter/mmkv/analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nanalyzer:\n  errors:\n    invalid_annotation_target: ignore\n    avoid_print: ignore\n  exclude:\n    - \"lib/assets.dart\"\n    - \"lib/injection.config.dart\"\n    - \"lib/**.g.dart\"\n    - \"lib/**.freeezed.dart\"\n    - lib/jsons.dart\n\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    prefer_double_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n    always_use_package_imports: true\n    always_declare_return_types: true\n    always_put_required_named_parameters_first: true\n    avoid_unused_constructor_parameters: true\n    prefer_final_fields: true\n    prefer_final_in_for_each: true\n    prefer_final_locals: true\n    constant_identifier_names: false\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "flutter/mmkv/example/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n.metadata\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n"
  },
  {
    "path": "flutter/mmkv/example/README.md",
    "content": "# mmkv_example\n\nDemonstrates how to use the mmkv plugin.\n\n## Getting Started\n\nThis project is a starting point for a Flutter application.\n\nA few resources to get you started if this is your first Flutter project:\n\n- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)\n- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)\n\nFor help getting started with Flutter, view our\n[online documentation](https://flutter.dev/docs), which offers tutorials,\nsamples, guidance on mobile development, and a full API reference.\n"
  },
  {
    "path": "flutter/mmkv/example/android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/build.gradle",
    "content": "plugins {\n    id \"com.android.application\"\n//    id \"kotlin-android\"\n    id \"dev.flutter.flutter-gradle-plugin\"\n}\n\ndef localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\nandroid {\n    namespace \"com.tencent.mmkv_example\"\n    compileSdkVersion 35\n\n    lintOptions {\n        disable 'InvalidPackage'\n    }\n\n    defaultConfig {\n        applicationId \"com.tencent.mmkv_example\"\n        minSdkVersion flutter.minSdkVersion\n        targetSdkVersion 35\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n    }\n\n    buildTypes {\n        release {\n            // TODO: Add your own signing config for the release build.\n            // Signing with the debug keys for now, so `flutter run --release` works.\n            signingConfig signingConfigs.debug\n\n            minifyEnabled true\n        }\n        debug {\n            minifyEnabled true\n        }\n    }\n\n    dependencies {\n        implementation('com.tencent:mmkv:2.4.0')\n\n        constraints {\n            implementation(\"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0\") {\n                because(\"kotlin-stdlib-jdk7 is now a part of kotlin-stdlib\")\n            }\n            implementation(\"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0\") {\n                because(\"kotlin-stdlib-jdk8 is now a part of kotlin-stdlib\")\n            }\n        }\n\n        modules {\n            module(\"com.tencent:mmkv-static\") {\n                replacedBy(\"com.tencent:mmkv\", \"Using mmkv for flutter\")\n            }\n            module(\"com.tencent:mmkv-shared\") {\n                replacedBy(\"com.tencent:mmkv\", \"Using mmkv for flutter\")\n            }\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- io.flutter.app.FlutterApplication is an android.app.Application that\n         calls FlutterMain.startInitialization(this); in its onCreate method.\n         In most cases you can leave this as-is, but you if you want to provide\n         additional functionality it is fine to subclass or reimplement\n         FlutterApplication and put your custom class here. -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <application\n        android:name=\"${applicationName}\"\n        android:label=\"mmkv_example\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:name=\".MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\"\n            android:exported=\"true\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/src/main/java/com/tencent/mmkv_example/MainActivity.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv_example;\n\nimport io.flutter.embedding.android.FlutterActivity;\n\npublic class MainActivity extends FlutterActivity {}\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">@android:color/white</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "flutter/mmkv/example/android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.tencent.mmkv_example\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "flutter/mmkv/example/android/build.gradle",
    "content": "//buildscript {\n//    repositories {\n//        google()\n//        mavenCentral()\n//    }\n//\n//    dependencies {\n//        classpath 'com.android.tools.build:gradle:7.4.1'\n//    }\n//}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "flutter/mmkv/example/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10.2-bin.zip\n"
  },
  {
    "path": "flutter/mmkv/example/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nandroid.enableR8=true\n"
  },
  {
    "path": "flutter/mmkv/example/android/settings.gradle",
    "content": "pluginManagement {\n    def flutterSdkPath = {\n        def properties = new Properties()\n        file(\"local.properties\").withInputStream { properties.load(it) }\n        def flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n        assert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\n        return flutterSdkPath\n    }()\n\n    includeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nplugins {\n    id \"dev.flutter.flutter-plugin-loader\" version \"1.0.0\"\n    id \"com.android.application\" version \"8.8.1\" apply false\n//    id \"org.jetbrains.kotlin.android\" version \"{kotlinVersion}\" apply false\n}\n\ninclude \":app\""
  },
  {
    "path": "flutter/mmkv/example/android/settings_aar.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "flutter/mmkv/example/ios/.gitignore",
    "content": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>$(DEVELOPMENT_LANGUAGE)</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>13.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Flutter/Debug.xcconfig",
    "content": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Flutter/Release.xcconfig",
    "content": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Flutter/ephemeral/flutter_lldb_helper.py",
    "content": "#\n# Generated file, do not edit.\n#\n\nimport lldb\n\ndef handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):\n    \"\"\"Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.\"\"\"\n    base = frame.register[\"x0\"].GetValueAsAddress()\n    page_len = frame.register[\"x1\"].GetValueAsUnsigned()\n\n    # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the\n    # first page to see if handled it correctly. This makes diagnosing\n    # misconfiguration (e.g. missing breakpoint) easier.\n    data = bytearray(page_len)\n    data[0:8] = b'IHELPED!'\n\n    error = lldb.SBError()\n    frame.GetThread().GetProcess().WriteMemory(base, data, error)\n    if not error.Success():\n        print(f'Failed to write into {base}[+{page_len}]', error)\n        return\n\ndef __lldb_init_module(debugger: lldb.SBDebugger, _):\n    target = debugger.GetDummyTarget()\n    # Caveat: must use BreakpointCreateByRegEx here and not\n    # BreakpointCreateByName. For some reasons callback function does not\n    # get carried over from dummy target for the later.\n    bp = target.BreakpointCreateByRegex(\"^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$\")\n    bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))\n    bp.SetAutoContinue(True)\n    print(\"-- LLDB integration loaded --\")\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Flutter/ephemeral/flutter_lldbinit",
    "content": "#\n# Generated file, do not edit.\n#\n\ncommand script import --relative-to-command-file flutter_lldb_helper.py\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\n# platform :ios, '13.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\nplatform :ios, '13.0'\n\n#source 'https://git.code.oa.com/guoling/PrivatePodSpec.git'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\n  # https://github.com/Tencent/MMKV/issues/669\n  # https://blog.cocoapods.org/CocoaPods-1.4.0/\n#   script_phase :name => 'Fix MMKV Plugin Name', :script => 'sed -i \"\" \"s/@import mmkv;/@import mmkvflutter;/g\" ${SRCROOT}/Runner/GeneratedPluginRegistrant.m', :execution_position=>:before_compile\n  # Specify the dependency from a specific branch\n    pod 'MMKVCore', :git => 'https://github.com/Tencent/MMKV.git', :branch => 'dev'\n    pod 'MMKV', :git => 'https://github.com/Tencent/MMKV.git', :branch => 'dev'\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/AppDelegate.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : FlutterAppDelegate\n\n@end\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/AppDelegate.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"AppDelegate.h\"\n#import \"GeneratedPluginRegistrant.h\"\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    [GeneratedPluginRegistrant registerWithRegistry:self];\n    // Override point for customization after application launch.\n    return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n\n@end\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images."
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"17156\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"17126\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"96\" y=\"-20\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>mmkv_example</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/Runner.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.tencent.mmkv</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner/main.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"AppDelegate.h\"\n#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n\nint main(int argc, char *argv[]) {\n    @autoreleasepool {\n        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n    }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t8B7B1942C9627442487AD17E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F18EA25B44B19F018EB8250 /* Pods_Runner.framework */; };\n\t\t978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };\n\t\t97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t23779DEF4DB9C913DFEF0BA5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t70DD1EE52EB3B4688CA4A26E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\t7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\t7F18EA25B44B19F018EB8250 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tB2C1AA8754382E0A720C38EA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tCB8E67DC2B627D4100C6E2ED /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8B7B1942C9627442487AD17E /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t1A5C66FA445CBC20042E1148 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tB2C1AA8754382E0A720C38EA /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t70DD1EE52EB3B4688CA4A26E /* Pods-Runner.release.xcconfig */,\n\t\t\t\t23779DEF4DB9C913DFEF0BA5 /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t5F9614CA0D18FEFEA68861FA /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F18EA25B44B19F018EB8250 /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\t1A5C66FA445CBC20042E1148 /* Pods */,\n\t\t\t\t5F9614CA0D18FEFEA68861FA /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB8E67DC2B627D4100C6E2ED /* Runner.entitlements */,\n\t\t\t\t7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,\n\t\t\t\t7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t97C146F11CF9000F007C117D /* Supporting Files */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F11CF9000F007C117D /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146F21CF9000F007C117D /* main.m */,\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t19FB7C13A6F154DB79BFE09E /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t\t5317A02409A8109AC00457A1 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t19FB7C13A6F154DB79BFE09E /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\",\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\\n\";\n\t\t};\n\t\t5317A02409A8109AC00457A1 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,\n\t\t\t\t97C146F31CF9000F007C117D /* main.m in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tARCHS = \"$(ARCHS_STANDARD)\";\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"COCOAPODS=1\",\n\t\t\t\t\tFORCE_POSIX,\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKVDemo;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tARCHS = \"$(ARCHS_STANDARD)\";\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"COCOAPODS=1\",\n\t\t\t\t\tFORCE_POSIX,\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKVDemo;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tARCHS = \"$(ARCHS_STANDARD)\";\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"COCOAPODS=1\",\n\t\t\t\t\tFORCE_POSIX,\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKVDemo;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      enableGPUValidationMode = \"1\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/lib/main.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:async\";\nimport \"dart:convert\";\nimport \"dart:io\";\nimport \"dart:typed_data\";\n\nimport \"package:flutter/material.dart\";\nimport \"package:mmkv/mmkv.dart\";\nimport \"package:path_provider_foundation/path_provider_foundation.dart\";\nimport \"package:path_provider/path_provider.dart\";\n// import \"package:posix/posix.dart\" show chmod;\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  String? groupDir;\n  if (Platform.isIOS) {\n    final PathProviderFoundation provider = PathProviderFoundation();\n    groupDir = await provider.getContainerPath(appGroupIdentifier: \"group.tencent.mmkv\");\n  } else if (Platform.isMacOS) {\n    final PathProviderFoundation provider = PathProviderFoundation();\n    final path = await provider.getApplicationDocumentsPath();\n    groupDir = \"$path/mmkv_group\";\n  }\n\n  // test NameSpace before MMKV.initialize()\n  await _testNameSpace();\n\n  // must wait for MMKV to finish initialization\n  final rootDir = await MMKV.initialize(groupDir: groupDir, handler: MyMMKVHandler());\n  print(\"MMKV for flutter with rootDir = $rootDir\");\n\n  runApp(const MyApp());\n}\n\nFuture<void> _testNameSpace() async {\n  final path = await getApplicationDocumentsDirectory();\n  final rootPath = \"${path.path}/mmkv_namespace\";\n  final ns = MMKV.nameSpace(rootPath);\n  final kv = ns.mmkv(\"test_namespace\");\n  _testMMKVImp(kv, false);\n}\n\nvoid _testMMKVImp(MMKV mmkv, bool decodeOnly) {\n  print(\"MMKV mmapID = ${mmkv.mmapID}\");\n  print('string = ${mmkv.decodeString('string')}');\n\n  String str = \"Hello dart from MMKV\";\n  if (!decodeOnly) {\n    mmkv.encodeString(\"string\", str);\n  }\n\n  if (!decodeOnly) {\n    mmkv.encodeBool(\"bool\", true);\n  }\n  print('bool = ${mmkv.decodeBool('bool')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeInt32(\"int32\", (1 << 31) - 1);\n  }\n  print('max int32 = ${mmkv.decodeInt32('int32')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeInt32(\"int32\", 0 - (1 << 31));\n  }\n  print('min int32 = ${mmkv.decodeInt32('int32')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeInt(\"int\", (1 << 63) - 1);\n  }\n  print('max int = ${mmkv.decodeInt('int')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeInt(\"int\", 0 - (1 << 63));\n  }\n  print('min int = ${mmkv.decodeInt('int')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeDouble(\"double\", double.maxFinite);\n  }\n  print('max double = ${mmkv.decodeDouble('double')}');\n\n  if (!decodeOnly) {\n    mmkv.encodeDouble(\"double\", double.minPositive);\n  }\n  print('min positive double = ${mmkv.decodeDouble('double')}');\n\n\n  str += \" with bytes\";\n  var bytes = MMBuffer.fromList(const Utf8Encoder().convert(str))!;\n  if (!decodeOnly) {\n    mmkv.encodeBytes(\"bytes\", bytes);\n  }\n  bytes.destroy();\n  bytes = mmkv.decodeBytes(\"bytes\")!;\n  print(\"bytes = ${const Utf8Decoder().convert(bytes.asList()!)}\");\n  bytes.destroy();\n\n  print('contains \"bool\": ${mmkv.containsKey('bool')}');\n  mmkv.removeValue(\"bool\");\n  print('after remove, contains \"bool\": ${mmkv.containsKey('bool')}');\n  mmkv.removeValues([\"int32\", \"int\"]);\n  print(\"all keys: ${mmkv.allKeys}\");\n}\n\nclass MyMMKVHandler extends MMKVHandler {\n  @override\n  bool wantLogRedirect() {\n    print(\"MyMMKVHandler.wantLogRedirect() is called\");\n    return true;\n  }\n\n  @override\n  void mmkvLog(MMKVLogLevel level, String file, int line, String function, String message) {\n    print(\"mmkv-redirect <$file:$line::$function> $message\");\n  }\n\n  @override\n  MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) {\n    print(\"onMMKVCRCCheckFail: $mmapID\");\n    return MMKVRecoverStrategic.OnErrorRecover;\n  }\n\n  @override\n  MMKVRecoverStrategic onMMKVFileLengthError(String mmapID) {\n    print(\"onMMKVFileLengthError: $mmapID\");\n    return MMKVRecoverStrategic.OnErrorRecover;\n  }\n\n  @override\n  bool wantContentChangeNotification() {\n    print(\"MyMMKVHandler.wantContentChangeNotification() is called\");\n    return true;\n  }\n\n  @override\n  void onContentChangedByOuterProcess(String mmapID) {\n    print(\"onContentChangedByOuterProcess: $mmapID\");\n  }\n}\n\nclass MyApp extends StatefulWidget {\n  const MyApp({super.key});\n\n  @override\n  _MyAppState createState() => _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  @override\n  void initState() {\n    super.initState();\n    initPlatformState();\n  }\n\n  // Platform messages are asynchronous, so we initialize in an async method.\n  Future<void> initPlatformState() async {\n    // If the widget was removed from the tree while the asynchronous platform\n    // message was in flight, we want to discard the reply rather than calling\n    // setState to update our non-existent appearance.\n    if (!mounted) return;\n\n    setState(() {});\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      home: Scaffold(\n        appBar: AppBar(\n          title: const Text(\"MMKV Plugin example app\"),\n        ),\n        body: Center(\n            child: Column(children: <Widget>[\n          Text(\"MMKV Version: ${MMKV.version}\\n\"),\n          TextButton(\n              onPressed: () {\n                functionalTest();\n                // testReadOnly();\n                testImport();\n              },\n              child: const Text(\"Functional Test\", style: TextStyle(fontSize: 18))),\n          TextButton(\n              onPressed: () {\n                testReKey();\n              },\n              child: const Text(\"Encryption Test\", style: TextStyle(fontSize: 18))),\n          TextButton(\n              onPressed: () {\n                testAutoExpire();\n              },\n              child: const Text(\"Auto Expiration Test\", style: TextStyle(fontSize: 18))),\n          TextButton(\n              onPressed: () {\n                testCompareBeforeSet();\n              },\n              child: const Text(\"Compare Before Insert/Update Test\", style: TextStyle(fontSize: 18))),\n          TextButton(\n              onPressed: () {\n                testBackup();\n                testRestore();\n              },\n              child: const Text(\"Backup & Restore Test\", style: TextStyle(fontSize: 18))),\n          TextButton(\n              onPressed: () {\n                testRemoveStorageAndCheckExist();\n              },\n              child: const Text(\"Remove Storage & Check Exist Test\", style: TextStyle(fontSize: 18))),\n        ])),\n      ),\n    );\n  }\n\n  void functionalTest() {\n    /* Note: If you come across to failing to load defaultMMKV() on Android after upgrading Flutter from 1.20+ to 2.0+,\n     * you can try passing this encryption key '\\u{2}U' instead.\n     * var mmkv = MMKV.defaultMMKV(cryptKey: '\\u{2}U');\n     */\n    final mmkv = MMKV.defaultMMKV();\n    mmkv.encodeBool(\"bool\", true);\n    print('bool = ${mmkv.decodeBool('bool')}');\n\n    mmkv.encodeInt32(\"int32\", (1 << 31) - 1);\n    print('max int32 = ${mmkv.decodeInt32('int32')}');\n\n    mmkv.encodeInt32(\"int32\", 0 - (1 << 31));\n    print('min int32 = ${mmkv.decodeInt32('int32')}');\n\n    mmkv.encodeInt(\"int\", (1 << 63) - 1);\n    print('max int = ${mmkv.decodeInt('int')}');\n\n    mmkv.encodeInt(\"int\", 0 - (1 << 63));\n    print('min int = ${mmkv.decodeInt('int')}');\n\n    mmkv.encodeDouble(\"double\", double.maxFinite);\n    print('max double = ${mmkv.decodeDouble('double')}');\n\n    mmkv.encodeDouble(\"double\", double.minPositive);\n    print('min positive double = ${mmkv.decodeDouble('double')}');\n\n    String str = \"Hello dart from MMKV\";\n    mmkv.encodeString(\"string\", str);\n    print('string = ${mmkv.decodeString('string')}');\n\n    mmkv.encodeString(\"string\", \"\");\n    print('empty string = ${mmkv.decodeString('string')}');\n    print('contains \"string\": ${mmkv.containsKey('string')}');\n\n    mmkv.encodeString(\"string\", null);\n    print('null string = ${mmkv.decodeString('string')}');\n    print('contains \"string\": ${mmkv.containsKey('string')}');\n\n    str += \" with bytes\";\n    var bytes = MMBuffer.fromList(const Utf8Encoder().convert(str))!;\n    mmkv.encodeBytes(\"bytes\", bytes);\n    bytes.destroy();\n    bytes = mmkv.decodeBytes(\"bytes\")!;\n    print(\"bytes = ${const Utf8Decoder().convert(bytes.asList()!)}\");\n    bytes.destroy();\n\n    // test empty bytes\n    {\n      final list = Uint8List(0);\n      final buffer = MMBuffer.fromList(list)!;\n\n      const key = \"empty_bytes\";\n      mmkv.encodeBytes(key, buffer);\n      buffer.destroy();\n\n      String bytesA;\n      final result = mmkv.decodeBytes(key);\n      if (result == null) {\n        bytesA = \"decodeBytes is null\";\n      } else {\n        // bytes = result.takeList().toString();\n        final listA = result.asList();\n        bytesA = listA.toString();\n        result.destroy();\n      }\n      print(\"$key = $bytesA\");\n    }\n\n    print('contains \"bool\": ${mmkv.containsKey('bool')}');\n    mmkv.removeValue(\"bool\");\n    print('after remove, contains \"bool\": ${mmkv.containsKey('bool')}');\n    mmkv.removeValues([\"int32\", \"int\"]);\n    print(\"all keys: ${mmkv.allKeys}\");\n\n    mmkv.trim();\n    mmkv.clearMemoryCache();\n    print(\"all keys: ${mmkv.allKeys}\");\n    mmkv.clearAll();\n    print(\"all keys: ${mmkv.allKeys}\");\n    // mmkv.sync(true);\n    // mmkv.close();\n    mmkv.checkContentChangedByOuterProcess();\n    print(\"is file valid: ${MMKV.isFileValid(mmkv.mmapID)}\");\n  }\n\n  MMKV testMMKV(String mmapID, String? cryptKey, bool aes256, bool decodeOnly, String? rootPath) {\n    final mmkv = MMKV(mmapID, cryptKey: cryptKey, aes256: aes256, rootDir: rootPath);\n    _testMMKVImp(mmkv, decodeOnly);\n    return mmkv;\n  }\n\n  void testReKey() {\n    const mmapID = \"testAES_reKey1\";\n    final MMKV kv = testMMKV(mmapID, null, false, false, null);\n\n    kv.reKey(\"Key_seq_1\");\n    kv.clearMemoryCache();\n    testMMKV(mmapID, \"Key_seq_1\", false, true, null);\n\n    kv.reKey(\"Key_Seq_Very_Looooooooong\", aes256: true);\n    kv.clearMemoryCache();\n    testMMKV(mmapID, \"Key_Seq_Very_Looooooooong\", true, true, null);\n\n    kv.reKey(null);\n    kv.clearMemoryCache();\n    testMMKV(mmapID, null, false, true, null);\n  }\n\n  void testBackup() {\n    final rootDir = FileSystemEntity.parentOf(MMKV.rootDir);\n    var backupRootDir = \"$rootDir/mmkv_backup_3\";\n    const String mmapID = \"test/AES\";\n    const String cryptKey = \"Tencent MMKV\";\n    final String otherDir = \"$rootDir/mmkv_3\";\n    testMMKV(mmapID, cryptKey, false, false, otherDir);\n\n    final ret = MMKV.backupOneToDirectory(mmapID, backupRootDir, rootDir: otherDir);\n    print(\"backup one [$mmapID] return: $ret\");\n\n    backupRootDir = \"$rootDir/mmkv_backup\";\n    final count = MMKV.backupAllToDirectory(backupRootDir);\n    print(\"backup all count: $count\");\n  }\n\n  void testRestore() {\n    final rootDir = FileSystemEntity.parentOf(MMKV.rootDir);\n    var backupRootDir = \"$rootDir/mmkv_backup_3\";\n    const String mmapID = \"test/AES\";\n    const String cryptKey = \"Tencent MMKV\";\n    final String otherDir = \"$rootDir/mmkv_3\";\n\n    final kv = MMKV(mmapID, cryptKey: cryptKey, rootDir: otherDir);\n    kv.encodeString(\"test_restore\", \"value before restore\");\n    print(\"before restore [${kv.mmapID}] allKeys: ${kv.allKeys}\");\n    final ret = MMKV.restoreOneMMKVFromDirectory(mmapID, backupRootDir, rootDir: otherDir);\n    print(\"restore one [${kv.mmapID}] ret = $ret\");\n    if (ret) {\n      print(\"after restore [${kv.mmapID}] allKeys: ${kv.allKeys}\");\n    }\n\n    backupRootDir = \"$rootDir/mmkv_backup\";\n    final count = MMKV.restoreAllFromDirectory(backupRootDir);\n    print(\"restore all count $count\");\n    if (count > 0) {\n      var mmkv = MMKV.defaultMMKV();\n      print(\"check on restore file[${mmkv.mmapID}] allKeys: ${mmkv.allKeys}\");\n\n      mmkv = MMKV(\"testAES_reKey1\");\n      print(\"check on restore file[${mmkv.mmapID}] allKeys: ${mmkv.allKeys}\");\n    }\n  }\n\n  void testAutoExpire() {\n    // test disable auto expire by constructor\n    var mmkv = MMKV(\"test_auto_expire\", enableKeyExpire: false);\n    // var mmkv = MMKV(\"test_auto_expire\", enableKeyExpire: false, itemSizeLimit: 1);\n    // mmkv.encodeBool(\"never_expire_key_1\", true, MMKV.ExpireNever);\n    mmkv.clearAll(keepSpace: true);\n    // mmkv.disableAutoKeyExpire();\n\n    // test enable auto expire by constructor\n    mmkv.close();\n    mmkv = MMKV(\"test_auto_expire\", enableKeyExpire: true, expiredInSeconds: 1);\n    // mmkv.enableAutoKeyExpire(1);\n    mmkv.encodeBool(\"auto_expire_key_1\", true);\n    mmkv.encodeInt32(\"auto_expire_key_2\", 1, 1);\n    mmkv.encodeInt(\"auto_expire_key_3\", 2, 1);\n    mmkv.encodeDouble(\"auto_expire_key_4\", 3.0, 1);\n    mmkv.encodeString(\"auto_expire_key_5\", \"hello auto expire\", 1);\n    {\n      final bytes = MMBuffer.fromList(const Utf8Encoder().convert(\"hello auto expire\"))!;\n      mmkv.encodeBytes(\"auto_expire_key_6\", bytes, 1);\n      bytes.destroy();\n    }\n    mmkv.encodeBool(\"never_expire_key_1\", true, MMKV.ExpireNever);\n\n    const duration = Duration(seconds: 2);\n    sleep(duration);\n\n    print(\"auto_expire_key_1: ${mmkv.containsKey(\"auto_expire_key_1\")}\");\n    print(\"auto_expire_key_2: ${mmkv.containsKey(\"auto_expire_key_2\")}\");\n    print(\"auto_expire_key_3: ${mmkv.containsKey(\"auto_expire_key_3\")}\");\n    print(\"auto_expire_key_4: ${mmkv.containsKey(\"auto_expire_key_4\")}\");\n    print(\"auto_expire_key_5: ${mmkv.containsKey(\"auto_expire_key_5\")}\");\n    print(\"auto_expire_key_6: ${mmkv.containsKey(\"auto_expire_key_6\")}\");\n    print(\"never_expire_key_1: ${mmkv.containsKey(\"never_expire_key_1\")}\");\n\n    print(\"count non-expired keys: ${mmkv.countNonExpiredKeys}\");\n    print(\"all non-expired keys: ${mmkv.allNonExpiredKeys}\");\n  }\n\n  void testCompareBeforeSet() {\n    final mmkv = MMKV(\"testCompareBeforeSet\", expectedCapacity: 4096 * 2);\n    mmkv.enableCompareBeforeSet();\n\n    mmkv.encodeString(\"key\", \"extra\");\n\n    {\n      const String key = \"int\";\n      const int v = 12345;\n      mmkv.encodeInt32(key, v);\n      final actualSize = mmkv.actualSize;\n      print(\"testCompareBeforeSet actualSize = $actualSize\");\n      print(\"testCompareBeforeSet v = ${mmkv.decodeInt(key)}\");\n      mmkv.encodeInt32(key, v);\n      final actualSize2 = mmkv.actualSize;\n      print(\"testCompareBeforeSet actualSize = $actualSize2\");\n      if (actualSize2 != actualSize) {\n        print(\"testCompareBeforeSet fail\");\n      }\n\n      mmkv.encodeInt32(key, v * 23);\n      print(\"testCompareBeforeSet actualSize = ${mmkv.actualSize}\");\n      print(\"testCompareBeforeSet v = ${mmkv.decodeInt32(key)}\");\n    }\n\n    {\n      const key = \"string\";\n      const v = \"w012A🏊🏻good\";\n      mmkv.encodeString(key, v);\n      final actualSize = mmkv.actualSize;\n      print(\"testCompareBeforeSet actualSize = $actualSize\");\n      print(\"testCompareBeforeSet v = ${mmkv.decodeString(key)}\");\n      mmkv.encodeString(key, v);\n      final actualSize2 = mmkv.actualSize;\n      print(\"testCompareBeforeSet actualSize = $actualSize2\");\n      if (actualSize2 != actualSize) {\n        print(\"testCompareBeforeSet fail\");\n      }\n\n      mmkv.encodeString(key, \"temp data 👩🏻‍🏫\");\n      print(\"testCompareBeforeSet actualSize = ${mmkv.actualSize}\");\n      print(\"testCompareBeforeSet v = ${mmkv.decodeString(key)}\");\n    }\n  }\n\n  void testRemoveStorageAndCheckExist() {\n    var mmapID = \"test_remove\";\n    {\n      final mmkv = MMKV(mmapID, mode: MMKVMode.MULTI_PROCESS_MODE);\n      mmkv.encodeBool(\"bool\", true);\n    }\n    var rootDir = _isDarwin() ? MMKV.groupPath() : null;\n    print(\"check exist = ${MMKV.checkExist(mmapID, rootDir: rootDir)}\");\n    MMKV.removeStorage(mmapID, rootDir: rootDir);\n    print(\"after remove, check exist = ${MMKV.checkExist(mmapID, rootDir: rootDir)}\");\n    {\n      final mmkv = MMKV(mmapID, mode: MMKVMode.MULTI_PROCESS_MODE);\n      if (mmkv.count != 0) {\n        print(\"storage not successfully removed\");\n      }\n    }\n\n    mmapID = \"test_remove/sg\";\n    rootDir = \"${MMKV.rootDir}_sg\";\n    var mmkv = MMKV(mmapID, rootDir: rootDir);\n    mmkv.encodeBool(\"bool\", true);\n    print(\"check exist = ${MMKV.checkExist(mmapID, rootDir: rootDir)}\");\n    MMKV.removeStorage(mmapID, rootDir: rootDir);\n    print(\"after remove, check exist = ${MMKV.checkExist(mmapID, rootDir: rootDir)}\");\n    mmkv = MMKV(mmapID, rootDir: rootDir);\n    if (mmkv.count != 0) {\n      print(\"storage not successfully removed\");\n    }\n  }\n\n  void testReadOnly() {\n    const name = \"testReadOnly\";\n    const key = \"ReadOnly+Key\";\n    {\n      final mmkv = MMKV(name, cryptKey: key);\n      _testMMKVImp(mmkv, false);\n      mmkv.close();\n    }\n\n    // posix.dart not working in Android or iOS, sigh..\n    /*final path = MMKV.rootDir + \"/\" + name;\n    chmod(path, \"444\");\n    final crcPath = path + \".crc\";\n    chmod(crcPath, \"444\");\n    */\n\n    final mmkv = MMKV(name, cryptKey: key, readOnly: true);\n    _testMMKVImp(mmkv, true);\n\n    // also check if it tolerate update operations without crash\n    _testMMKVImp(mmkv, false);\n\n    /*\n    chmod(path, \"666\");\n    chmod(crcPath, \"666\");\n    */\n  }\n\n  void testImport() {\n    const String mmapID = \"testImportSrc\";\n    final MMKV src = MMKV(mmapID);\n    src.encodeBool(\"bool\", true);\n    src.encodeInt32(\"int\", -2147483648); // Integer.MIN_VALUE in Dart\n    src.encodeInt(\"long\", 9223372036854775807); // Long.MAX_VALUE in Dart\n    src.encodeString(\"string\", \"test import\");\n\n    final MMKV dst = MMKV(\"testImportDst\");\n    dst.clearAll();\n    dst.enableAutoKeyExpire(1);\n    dst.encodeBool(\"bool\", false);\n    dst.encodeInt32(\"int\", -1);\n    dst.encodeInt(\"long\", 0);\n    dst.encodeString(\"string\", mmapID);\n\n    final int count = dst.importFrom(src);\n    if (count != 4 || dst.count != 4) {\n      print(\"MMKV: import check count fail\");\n    }\n    if (!dst.decodeBool(\"bool\")) {\n      print(\"MMKV: import check bool fail\");\n    }\n    if (dst.decodeInt32(\"int\") != -2147483648) {\n      print(\"MMKV: import check int fail\");\n    }\n    if (dst.decodeInt(\"long\") != 9223372036854775807) {\n      print(\"MMKV: import check long fail\");\n    }\n    if (dst.decodeString(\"string\") != \"test import\") {\n      print(\"MMKV: import check string fail\");\n    }\n\n    Future.delayed(const Duration(seconds: 2), () {\n      if (dst.countNonExpiredKeys != 0) {\n        print(\"MMKV: import check expire fail\");\n      }\n    });\n  }\n}\n\nbool _isDarwin() {\n  return Platform.isIOS || Platform.isMacOS;\n}\n"
  },
  {
    "path": "flutter/mmkv/example/linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "flutter/mmkv/example/linux/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.13)\nproject(runner LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"mmkv_example\")\n# The unique GTK application identifier for this application. See:\n# https://wiki.gnome.org/HowDoI/ChooseApplicationID\nset(APPLICATION_ID \"com.tencent.mmkv_example\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Load bundled libraries from the lib/ directory relative to the binary.\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Define build configuration options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\n# Application build; see runner/CMakeLists.txt.\nadd_subdirectory(\"runner\")\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nforeach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})\n  install(FILES \"${bundled_library}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendforeach(bundled_library)\n\n# Copy the native assets provided by the build.dart from all packages.\nset(NATIVE_ASSETS_DIR \"${PROJECT_BUILD_DIR}native_assets/linux/\")\ninstall(DIRECTORY \"${NATIVE_ASSETS_DIR}\"\n   DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n   COMPONENT Runtime)\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n"
  },
  {
    "path": "flutter/mmkv/example/linux/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "flutter/mmkv/example/linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <mmkv_linux/mmkv_linux_plugin.h>\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n  g_autoptr(FlPluginRegistrar) mmkv_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"MmkvLinuxPlugin\");\n  mmkv_linux_plugin_register_with_registrar(mmkv_linux_registrar);\n}\n"
  },
  {
    "path": "flutter/mmkv/example/linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "flutter/mmkv/example/linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  mmkv_linux\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "flutter/mmkv/example/linux/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.13)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name, change BINARY_NAME in the\n# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer\n# work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add preprocessor definitions for the application ID.\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Add dependency libraries. Add any application-specific dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\n\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\n"
  },
  {
    "path": "flutter/mmkv/example/linux/runner/main.cc",
    "content": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "flutter/mmkv/example/linux/runner/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"mmkv_example\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"mmkv_example\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_show(GTK_WIDGET(window));\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GApplication::startup.\nstatic void my_application_startup(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application startup.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->startup(application);\n}\n\n// Implements GApplication::shutdown.\nstatic void my_application_shutdown(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application shutdown.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_APPLICATION_CLASS(klass)->startup = my_application_startup;\n  G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  // Set the program name to the application ID, which helps various systems\n  // like GTK and desktop environments map this running application to its\n  // corresponding .desktop file. This ensures better integration by allowing\n  // the application to be recognized beyond its binary name.\n  g_set_prgname(APPLICATION_ID);\n\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     \"flags\", G_APPLICATION_NON_UNIQUE,\n                                     nullptr));\n}\n"
  },
  {
    "path": "flutter/mmkv/example/linux/runner/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "flutter/mmkv/example/macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport mmkv_ios\nimport path_provider_foundation\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  MMKVPlugin.register(with: registry.registrar(forPlugin: \"MMKVPlugin\"))\n  PathProviderPlugin.register(with: registry.registrar(forPlugin: \"PathProviderPlugin\"))\n}\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Podfile",
    "content": "platform :osx, '10.15'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\n\n  # Specify the dependency from a specific branch\n    pod 'MMKVCore', :git => 'https://github.com/Tencent/MMKV.git', :branch => 'dev'\n    pod 'MMKV', :git => 'https://github.com/Tencent/MMKV.git', :branch => 'dev'\n\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@main\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n\n  override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_1024.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = mmkv_example\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvExample\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2025 com.tencent. All rights reserved.\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t7416FD959E850BD215C2FB2D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59C1DE2B48A040A30A0A6838 /* Pods_RunnerTests.framework */; };\n\t\tE2DDB8A3EE9609EAE3D59879 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06F522889011B295251E80F2 /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t01C4C6089F85748C5A26ADC2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t06F522889011B295251E80F2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t1A934A22058F429F7FA2987F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* mmkv_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mmkv_example.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t59C1DE2B48A040A30A0A6838 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t6985E10173DB1D7F36165E3E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.debug.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t95DC042908D29FD04DFC5ED9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\tD44CE1E9B85D811B0DE0A753 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tF92D9C706DE1925749C26DF1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7416FD959E850BD215C2FB2D /* Pods_RunnerTests.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tE2DDB8A3EE9609EAE3D59879 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\tB15176455017E530C3B1A257 /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* mmkv_example.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB15176455017E530C3B1A257 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t01C4C6089F85748C5A26ADC2 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t1A934A22058F429F7FA2987F /* Pods-Runner.release.xcconfig */,\n\t\t\t\t95DC042908D29FD04DFC5ED9 /* Pods-Runner.profile.xcconfig */,\n\t\t\t\t6985E10173DB1D7F36165E3E /* Pods-RunnerTests.debug.xcconfig */,\n\t\t\t\tD44CE1E9B85D811B0DE0A753 /* Pods-RunnerTests.release.xcconfig */,\n\t\t\t\tF92D9C706DE1925749C26DF1 /* Pods-RunnerTests.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t06F522889011B295251E80F2 /* Pods_Runner.framework */,\n\t\t\t\t59C1DE2B48A040A30A0A6838 /* Pods_RunnerTests.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t94DE649AAAA0A22B1DE5A0FC /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t7C855DA06C93D0306C48B0CA /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\t3B1A56B4007F29133FC7677F /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* mmkv_example.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t331C80D4294CF70F00263BE5 /* RunnerTests */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\\n\";\n\t\t};\n\t\t3B1A56B4007F29133FC7677F /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t7C855DA06C93D0306C48B0CA /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t94DE649AAAA0A22B1DE5A0FC /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 6985E10173DB1D7F36165E3E /* Pods-RunnerTests.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvExample.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/mmkv_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mmkv_example\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = D44CE1E9B85D811B0DE0A753 /* Pods-RunnerTests.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvExample.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/mmkv_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mmkv_example\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F92D9C706DE1925749C26DF1 /* Pods-RunnerTests.profile.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvExample.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/mmkv_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mmkv_example\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"mmkv_example.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"mmkv_example.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      enableGPUValidationMode = \"1\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"mmkv_example.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"mmkv_example.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "flutter/mmkv/example/macos/RunnerTests/RunnerTests.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/.gitignore",
    "content": "/node_modules\n/oh_modules\n/local.properties\n/.idea\n**/build\n/.hvigor\n.cxx\n/.clangd\n/.clang-format\n/.clang-tidy\n**/.test\n*.har\n**/BuildProfile.ets\n**/oh-package-lock.json5\n\n**/src/main/resources/rawfile/flutter_assets/\n**/libs/arm64-v8a/libapp.so\n**/libs/arm64-v8a/libflutter.so\n**/libs/arm64-v8a/libvmservice_snapshot.so\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/AppScope/app.json5",
    "content": "{\n  \"app\": {\n    \"bundleName\": \"com.example.mmkv_fake_example\",\n    \"vendor\": \"example\",\n    \"versionCode\": 1,\n    \"versionName\": \"1.0.0\",\n    \"icon\": \"$media:app_icon\",\n    \"label\": \"$string:app_name\"\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/AppScope/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"app_name\",\n      \"value\": \"mmkv_fake_example\"\n    }\n  ]\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/build-profile.json5",
    "content": "{\n  \"app\": {\n    \"signingConfigs\": [\n      {\n        \"name\": \"default\",\n        \"type\": \"HarmonyOS\",\n        \"material\": {\n          \"certpath\": \"/Users/lingol/.ohos/config/default_ohos_1HD658c6NQUQuufw73R4pM9jk2_zRyU-MIvpwrj5Aow=.cer\",\n          \"storePassword\": \"0000001BD13BD4FD3E6F7A2DF0EE4400AF1E0891E9AFEBBFCE49942E0BC8314023B1974889CE7534DE4EBF\",\n          \"keyAlias\": \"debugKey\",\n          \"keyPassword\": \"0000001B248D9207C7D411E9D5F3BB20D8C4EF32B182D8F5FAAE82E4A67C0DBB2BAF55D946D3DC317BB333\",\n          \"profile\": \"/Users/lingol/.ohos/config/default_ohos_1HD658c6NQUQuufw73R4pM9jk2_zRyU-MIvpwrj5Aow=.p7b\",\n          \"signAlg\": \"SHA256withECDSA\",\n          \"storeFile\": \"/Users/lingol/.ohos/config/default_ohos_1HD658c6NQUQuufw73R4pM9jk2_zRyU-MIvpwrj5Aow=.p12\"\n        }\n      }\n    ],\n    \"products\": [\n      {\n        \"name\": \"default\",\n        \"signingConfig\": \"default\",\n        \"compatibleSdkVersion\": \"5.0.0(12)\",\n        \"runtimeOS\": \"HarmonyOS\"\n      }\n    ]\n  },\n  \"modules\": [\n    {\n      \"name\": \"entry\",\n      \"srcPath\": \"./entry\",\n      \"targets\": [\n        {\n          \"name\": \"default\",\n          \"applyToProducts\": [\n            \"default\"\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/.gitignore",
    "content": "\n/node_modules\n/oh_modules\n/.preview\n/build\n/.cxx\n/.test"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/build-profile.json5",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n{\n  \"apiType\": 'stageMode',\n  \"buildOption\": {\n    \"nativeLib\": {\n      \"debugSymbol\": {\n        \"strip\": false\n      }\n    }\n  },\n  \"targets\": [\n    {\n      \"name\": \"default\",\n      \"runtimeOS\": \"HarmonyOS\"\n    },\n    {\n      \"name\": \"ohosTest\",\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/hvigorfile.ts",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.\nexport { hapTasks } from '@ohos/hvigor-ohos-plugin';\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/oh-package.json5",
    "content": "{\n  \"name\": \"entry\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {\n    \"path_provider_ohos\": \"file:../har/path_provider_ohos.har\",\n    \"mmkv_ohos\": \"file:../har/mmkv_ohos.har\",\n//    \"@tencent/mmkv\": \"file:/Users/lingol/Developer/mmkv/OpenHarmony/MMKV/build/default/outputs/default/MMKV.har\"\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport { FlutterAbility, FlutterEngine, FlutterManager } from '@ohos/flutter_ohos';\nimport { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';\n// import { MMKV } from '@tencent/mmkv';\nimport AbilityConstant from '@ohos.app.ability.AbilityConstant';\nimport Want from '@ohos.app.ability.Want';\n\nexport default class EntryAbility extends FlutterAbility {\n  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {\n    // EntryAbility.instance = this\n    // MMKV.initialize(this.context.getApplicationContext())\n    // let mmkv = MMKV.defaultMMKV();\n    // mmkv.encodeInt32('Harmony', 12)\n    // mmkv.decodeNumberSet('a')\n    super.onCreate(want, launchParam)\n  }\n\n  configureFlutterEngine(flutterEngine: FlutterEngine) {\n    super.configureFlutterEngine(flutterEngine)\n    GeneratedPluginRegistrant.registerWith(flutterEngine)\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/ets/pages/Index.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport common from '@ohos.app.ability.common';\nimport { FlutterPage } from '@ohos/flutter_ohos'\n\nlet storage = LocalStorage.getShared()\nconst EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'\n\n@Entry(storage)\n@Component\nstruct Index {\n  private context = getContext(this) as common.UIAbilityContext\n  @LocalStorageLink('viewId') viewId: string = \"\";\n\n  build() {\n    Column() {\n      FlutterPage({ viewId: this.viewId })\n    }\n  }\n\n  onBackPress(): boolean {\n    this.context.eventHub.emit(EVENT_BACK_PRESS)\n    return true\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets",
    "content": "import { FlutterEngine, Log } from '@ohos/flutter_ohos';\nimport MMKVPlugin from 'mmkv_ohos';\nimport PathProviderPlugin from 'path_provider_ohos';\n\n/**\n * Generated file. Do not edit.\n * This file is generated by the Flutter tool based on the\n * plugins that support the Ohos platform.\n */\n\nconst TAG = \"GeneratedPluginRegistrant\";\n\nexport class GeneratedPluginRegistrant {\n\n  static registerWith(flutterEngine: FlutterEngine) {\n    try {\n      flutterEngine.getPlugins()?.add(new MMKVPlugin());\n      flutterEngine.getPlugins()?.add(new PathProviderPlugin());\n    } catch (e) {\n      Log.e(\n        TAG,\n        \"Tried to register plugins with FlutterEngine (\"\n          + flutterEngine\n          + \") failed.\");\n      Log.e(TAG, \"Received exception while registering\", e);\n    }\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/module.json5",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n{\n  \"module\": {\n    \"name\": \"entry\",\n    \"type\": \"entry\",\n    \"description\": \"$string:module_desc\",\n    \"mainElement\": \"EntryAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:main_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"EntryAbility\",\n        \"srcEntry\": \"./ets/entryability/EntryAbility.ets\",\n        \"description\": \"$string:EntryAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:EntryAbility_label\",\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"exported\": true,\n        \"skills\": [\n          {\n            \"entities\": [\n              \"entity.system.home\"\n            ],\n            \"actions\": [\n              \"action.system.home\"\n            ]\n          }\n        ]\n      }\n    ],\n    \"requestPermissions\": [\n      {\"name\" :  \"ohos.permission.INTERNET\"},\n    ]\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"mmkv_fake_example\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/resources/base/profile/main_pages.json",
    "content": "{\n  \"src\": [\n    \"pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/resources/en_US/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"mmkv_fake_example\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/main/resources/zh_CN/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"模块描述\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"mmkv_fake_example\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport hilog from '@ohos.hilog';\nimport { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'\n\nexport default function abilityTest() {\n  describe('ActsAbilityTest', function () {\n    // Defines a test suite. Two parameters are supported: test suite name and test suite function.\n    beforeAll(function () {\n      // Presets an action, which is performed only once before all test cases of the test suite start.\n      // This API supports only one parameter: preset action function.\n    })\n    beforeEach(function () {\n      // Presets an action, which is performed before each unit test case starts.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: preset action function.\n    })\n    afterEach(function () {\n      // Presets a clear action, which is performed after each unit test case ends.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: clear action function.\n    })\n    afterAll(function () {\n      // Presets a clear action, which is performed after all test cases of the test suite end.\n      // This API supports only one parameter: clear action function.\n    })\n    it('assertContain',0, function () {\n      // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n      hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');\n      let a = 'abc'\n      let b = 'b'\n      // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n      expect(a).assertContain(b)\n      expect(a).assertEqual(a)\n    })\n  })\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/ets/test/List.test.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport abilityTest from './Ability.test'\n\nexport default function testsuite() {\n  abilityTest()\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport UIAbility from '@ohos.app.ability.UIAbility';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\nimport hilog from '@ohos.hilog';\nimport { Hypium } from '@ohos/hypium';\nimport testsuite from '../test/List.test';\nimport window from '@ohos.window';\n\nexport default class TestAbility extends UIAbility {\n    onCreate(want, launchParam) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');\n        var abilityDelegator: any\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var abilityDelegatorArguments: any\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');\n        Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)\n    }\n\n    onDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');\n    }\n\n    onWindowStageCreate(windowStage: window.WindowStage) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');\n        windowStage.loadContent('testability/pages/Index', (err, data) => {\n            if (err.code) {\n                hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');\n                return;\n            }\n            hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',\n                JSON.stringify(data) ?? '');\n        });\n    }\n\n    onWindowStageDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');\n    }\n\n    onForeground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');\n    }\n\n    onBackground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');\n    }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport hilog from '@ohos.hilog';\n\n@Entry\n@Component\nstruct Index {\n  aboutToAppear() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');\n  }\n @State message: string = 'Hello World'\n   build() {\n         Row() {\n           Column() {\n             Text(this.message)\n               .fontSize(50)\n               .fontWeight(FontWeight.Bold)\n             Button() {\n               Text('next page')\n                 .fontSize(20)\n                 .fontWeight(FontWeight.Bold)\n             }.type(ButtonType.Capsule)\n             .margin({\n               top: 20\n             })\n             .backgroundColor('#0D9FFB')\n             .width('35%')\n             .height('5%')\n             .onClick(()=>{\n             })\n           }\n             .width('100%')\n         }\n             .height('100%')\n   }\n }"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport hilog from '@ohos.hilog';\nimport TestRunner from '@ohos.application.testRunner';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\n\nvar abilityDelegator = undefined\nvar abilityDelegatorArguments = undefined\n\nasync function onAbilityCreateCallback() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');\n}\n\nasync function addAbilityMonitorCallback(err: any) {\n    hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');\n}\n\nexport default class OpenHarmonyTestRunner implements TestRunner {\n    constructor() {\n    }\n\n    onPrepare() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');\n    }\n\n    async onRun() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'\n        let lMonitor = {\n            abilityName: testAbilityName,\n            onAbilityCreate: onAbilityCreateCallback,\n        };\n        abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)\n        var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName\n        var debug = abilityDelegatorArguments.parameters['-D']\n        if (debug == 'true')\n        {\n            cmd += ' -D'\n        }\n        hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);\n        abilityDelegator.executeShellCommand(cmd,\n            (err: any, d: any) => {\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');\n            })\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');\n    }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/module.json5",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n{\n  \"module\": {\n    \"name\": \"entry_test\",\n    \"type\": \"feature\",\n    \"description\": \"$string:module_test_desc\",\n    \"mainElement\": \"TestAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:test_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"TestAbility\",\n        \"srcEntry\": \"./ets/testability/TestAbility.ets\",\n        \"description\": \"$string:TestAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:TestAbility_label\",\n        \"exported\": true,\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"skills\": [\n          {\n            \"actions\": [\n              \"action.system.home\"\n            ],\n            \"entities\": [\n              \"entity.system.home\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_test_desc\",\n      \"value\": \"test ability description\"\n    },\n    {\n      \"name\": \"TestAbility_desc\",\n      \"value\": \"the test ability\"\n    },\n    {\n      \"name\": \"TestAbility_label\",\n      \"value\": \"test label\"\n    }\n  ]\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json",
    "content": "{\n  \"src\": [\n    \"testability/pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "flutter/mmkv/example/ohos/hvigor/hvigor-config.json5",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n{ \n  \"modelVersion\": \"5.0.0\",\n  \"dependencies\": {\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/hvigorfile.ts",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport { appTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: appTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}"
  },
  {
    "path": "flutter/mmkv/example/ohos/oh-package.json5",
    "content": "{\n  \"modelVersion\": \"5.0.0\",\n  \"name\": \"mmkv_fake_example\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\"\n  },\n  \"devDependencies\": {\n    \"@ohos/hypium\": \"1.0.6\"\n  },\n  \"overrides\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\",\n    \"@ohos/flutter_module\": \"file:./entry\",\n    \"path_provider_ohos\": \"file:./har/path_provider_ohos.har\",\n    \"mmkv_ohos\": \"file:./har/mmkv_ohos.har\"\n  }\n}"
  },
  {
    "path": "flutter/mmkv/example/pubspec.yaml",
    "content": "name: mmkv_example\ndescription: Demonstrates how to use the mmkv plugin.\nversion: 1.0.0+1\n\n# The following line prevents the package from being accidentally published to\n# pub.dev using `pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n\ndependencies:\n  flutter:\n    sdk: flutter\n  path_provider: \">=2.0.1\"\n\n  mmkv:\n    # When depending on this package from a real application you should use:\n    #   mmkv: ^x.y.z\n    # See https://dart.dev/tools/pub/dependencies#version-constraints\n    # The example app is bundled with the plugin so we use a path dependency on\n    # the parent directory to use the current plugin's version.\n    path: ../\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.0\n\n#  posix: ^6.0.1\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware.\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "flutter/mmkv/example/windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "flutter/mmkv/example/windows/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(mmkv_example LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"mmkv_example\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(VERSION 3.14...3.25)\n\n# Define build configuration option.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n# Define settings for the Profile build mode.\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build; see runner/CMakeLists.txt.\nadd_subdirectory(\"runner\")\n\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Copy the native assets provided by the build.dart from all packages.\nset(NATIVE_ASSETS_DIR \"${PROJECT_BUILD_DIR}native_assets/windows/\")\ninstall(DIRECTORY \"${NATIVE_ASSETS_DIR}\"\n   DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n   COMPONENT Runtime)\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n"
  },
  {
    "path": "flutter/mmkv/example/windows/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.14)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# Set fallback configurations for older versions of the flutter tool.\nif (NOT DEFINED FLUTTER_TARGET_PLATFORM)\n  set(FLUTTER_TARGET_PLATFORM \"windows-x64\")\nendif()\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      ${FLUTTER_TARGET_PLATFORM} $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "flutter/mmkv/example/windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <mmkv_win32/mmkv_win32_plugin.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  MmkvWin32PluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"MmkvWin32Plugin\"));\n}\n"
  },
  {
    "path": "flutter/mmkv/example/windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "flutter/mmkv/example/windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  mmkv_win32\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name, change BINARY_NAME in the\n# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer\n# work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add preprocessor definitions for the build version.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION=\\\"${FLUTTER_VERSION}\\\"\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}\")\n\n# Disable Windows macros that collide with C++ standard library functions.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\n\n# Add dependency libraries and include directories. Add any application-specific\n# dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_link_libraries(${BINARY_NAME} PRIVATE \"dwmapi.lib\")\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)\n#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD\n#else\n#define VERSION_AS_NUMBER 1,0,0,0\n#endif\n\n#if defined(FLUTTER_VERSION)\n#define VERSION_AS_STRING FLUTTER_VERSION\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"com.tencent\" \"\\0\"\n            VALUE \"FileDescription\", \"mmkv_example\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"mmkv_example\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2025 com.tencent. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"mmkv_example.exe\" \"\\0\"\n            VALUE \"ProductName\", \"mmkv_example\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n\n  flutter_controller_->engine()->SetNextFrameCallback([&]() {\n    this->Show();\n  });\n\n  // Flutter can complete the first frame before the \"show window\" callback is\n  // registered. The following call ensures a frame is pending to ensure the\n  // window is shown. It is a no-op if the first frame hasn't completed yet.\n  flutter_controller_->ForceRedraw();\n\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(1280, 720);\n  if (!window.Create(L\"mmkv_example\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(true);\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 and Windows 11 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  unsigned int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr)\n    -1; // remove the trailing null character\n  int input_length = (int)wcslen(utf16_string);\n  std::string utf8_string;\n  if (target_length == 0 || target_length > utf8_string.max_size()) {\n    return utf8_string;\n  }\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      input_length, utf8_string.data(), target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n\n#include <dwmapi.h>\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace {\n\n/// Window attribute that enables dark mode window decorations.\n///\n/// Redefined in case the developer's machine has a Windows SDK older than\n/// version 10.0.22000.0.\n/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute\n#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE\n#define DWMWA_USE_IMMERSIVE_DARK_MODE 20\n#endif\n\nconstexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n/// Registry key for app theme preference.\n///\n/// A value of 0 indicates apps should use dark mode. A non-zero or missing\n/// value indicates apps should use light mode.\nconstexpr const wchar_t kGetPreferredBrightnessRegKey[] =\n  L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Themes\\\\Personalize\";\nconstexpr const wchar_t kGetPreferredBrightnessRegValue[] = L\"AppsUseLightTheme\";\n\n// The number of Win32Window objects that currently exist.\nstatic int g_active_window_count = 0;\n\nusing EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n// Scale helper to convert logical scaler values to physical using passed in\n// scale factor\nint Scale(int source, double scale_factor) {\n  return static_cast<int>(source * scale_factor);\n}\n\n// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n// This API is only needed for PerMonitor V1 awareness mode.\nvoid EnableFullDpiSupportIfAvailable(HWND hwnd) {\n  HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n  if (!user32_module) {\n    return;\n  }\n  auto enable_non_client_dpi_scaling =\n      reinterpret_cast<EnableNonClientDpiScaling*>(\n          GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n  if (enable_non_client_dpi_scaling != nullptr) {\n    enable_non_client_dpi_scaling(hwnd);\n  }\n  FreeLibrary(user32_module);\n}\n\n}  // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar {\n public:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registrar instance.\n  static WindowClassRegistrar* GetInstance() {\n    if (!instance_) {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t* GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\n private:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar* instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t* WindowClassRegistrar::GetWindowClass() {\n  if (!class_registered_) {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass() {\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window() {\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window() {\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::Create(const std::wstring& title,\n                         const Point& origin,\n                         const Size& size) {\n  Destroy();\n\n  const wchar_t* window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window) {\n    return false;\n  }\n\n  UpdateTheme(window);\n\n  return OnCreate();\n}\n\nbool Win32Window::Show() {\n  return ShowWindow(window_handle_, SW_SHOWNORMAL);\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept {\n  if (message == WM_NCCREATE) {\n    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  } else if (Win32Window* that = GetThisFromHandle(window)) {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept {\n  switch (message) {\n    case WM_DESTROY:\n      window_handle_ = nullptr;\n      Destroy();\n      if (quit_on_close_) {\n        PostQuitMessage(0);\n      }\n      return 0;\n\n    case WM_DPICHANGED: {\n      auto newRectSize = reinterpret_cast<RECT*>(lparam);\n      LONG newWidth = newRectSize->right - newRectSize->left;\n      LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n      return 0;\n    }\n    case WM_SIZE: {\n      RECT rect = GetClientArea();\n      if (child_content_ != nullptr) {\n        // Size and position the child window.\n        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                   rect.bottom - rect.top, TRUE);\n      }\n      return 0;\n    }\n\n    case WM_ACTIVATE:\n      if (child_content_ != nullptr) {\n        SetFocus(child_content_);\n      }\n      return 0;\n\n    case WM_DWMCOLORIZATIONCOLORCHANGED:\n      UpdateTheme(hwnd);\n      return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy() {\n  OnDestroy();\n\n  if (window_handle_) {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0) {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {\n  return reinterpret_cast<Win32Window*>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content) {\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea() {\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle() {\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close) {\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate() {\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy() {\n  // No-op; provided for subclasses.\n}\n\nvoid Win32Window::UpdateTheme(HWND const window) {\n  DWORD light_mode;\n  DWORD light_mode_size = sizeof(light_mode);\n  LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,\n                               kGetPreferredBrightnessRegValue,\n                               RRF_RT_REG_DWORD, nullptr, &light_mode,\n                               &light_mode_size);\n\n  if (result == ERROR_SUCCESS) {\n    BOOL enable_dark_mode = light_mode == 0;\n    DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,\n                          &enable_dark_mode, sizeof(enable_dark_mode));\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv/example/windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window {\n public:\n  struct Point {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates a win32 window with |title| that is positioned and sized using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size this function will scale the inputted width and height as\n  // as appropriate for the default monitor. The window is invisible until\n  // |Show| is called. Returns true if the window was created successfully.\n  bool Create(const std::wstring& title, const Point& origin, const Size& size);\n\n  // Show the current window. Returns true if the window was successfully shown.\n  bool Show();\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\n protected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\n private:\n  friend class WindowClassRegistrar;\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window* GetThisFromHandle(HWND const window) noexcept;\n\n  // Update the window frame's theme to match the system theme.\n  static void UpdateTheme(HWND const window);\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif  // RUNNER_WIN32_WINDOW_H_\n"
  },
  {
    "path": "flutter/mmkv/lib/mmkv.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:async\";\nimport \"dart:convert\";\nimport \"dart:ffi\"; // For FFI\nimport \"dart:io\"; // For Platform.isX\n\nimport \"package:ffi/ffi.dart\";\nimport \"package:flutter/cupertino.dart\";\nimport \"package:flutter/material.dart\";\nimport \"package:flutter/services.dart\";\nimport \"package:mmkv_platform_interface/mmkv_platform_interface.dart\";\n\nexport \"package:mmkv_platform_interface/mmkv_platform_interface.dart\" show MMKVHandler, MMKVLogLevel, MMKVRecoverStrategic;\n\n/// Process mode for MMKV, default to [SINGLE_PROCESS_MODE].\nenum MMKVMode {\n  INVALID_MODE,\n  SINGLE_PROCESS_MODE,\n  MULTI_PROCESS_MODE,\n}\nconst int _READ_ONLY_MODE = 1 << 5;\n\n/// A native memory buffer, must call [MMBuffer.destroy()] after no longer use.\nclass MMBuffer {\n  int _length = 0;\n\n  /// The size of the memory buffer.\n  int get length => _length;\n\n  Pointer<Uint8>? _ptr;\n\n  /// The pointer of underlying memory buffer.\n  Pointer<Uint8>? get pointer => _ptr;\n\n  bool _isFromNative = false;\n\n  /// Whether this buffer is from native side.\n  bool get isFromNative => _isFromNative;\n\n  /// Create a memory buffer with size of [length].\n  MMBuffer(int length) {\n    _length = length;\n    if (length > 0) {\n      _ptr = malloc<Uint8>(length);\n    } else {\n      _ptr = nullptr;\n    }\n  }\n\n  /// Copy all data from [list].\n  static MMBuffer? fromList(List<int>? list) {\n    if (list == null) {\n      return null;\n    }\n\n    final buffer = MMBuffer(list.length);\n    if (list.isEmpty) {\n      buffer._ptr = malloc<Uint8>();\n    }\n    buffer.asList()!.setAll(0, list);\n    return buffer;\n  }\n\n  /// Create a wrapper of native pointer [ptr] with size [length].\n  /// DON'T [destroy()] the result because it's not a copy.\n  static MMBuffer _fromPointer(Pointer<Uint8> ptr, int length, {bool fromNative = true}) {\n    final buffer = MMBuffer(0);\n    buffer._length = length;\n    buffer._ptr = ptr;\n    buffer._isFromNative = fromNative;\n    return buffer;\n  }\n\n  /// Create a wrapper of native pointer [ptr] with size [length].\n  /// DO remember to [destroy()] the result because it's a COPY.\n  static MMBuffer _copyFromPointer(Pointer<Uint8> ptr, int length) {\n    final buffer = MMBuffer(length);\n    buffer._length = length;\n    if (length == 0 && ptr != nullptr) {\n      buffer._ptr = malloc<Uint8>();\n    }\n    _memcpy(buffer.pointer!.cast(), ptr.cast(), length);\n    return buffer;\n  }\n\n  /// Must call this after no longer use.\n  void destroy() {\n    if (_ptr != null && _ptr != nullptr) {\n      if (_isFromNative) {\n        _isFromNative = false;\n        _freePtr(_ptr!);\n      } else {\n        malloc.free(_ptr!);\n      }\n    }\n    _ptr = null;\n    _length = 0;\n  }\n\n  /// Get a **list view** of the underlying data.\n  /// Must call [destroy()] later after not longer use.\n  Uint8List? asList() {\n    if (_ptr != null && _ptr != nullptr) {\n      return _ptr!.asTypedList(_length);\n    }\n    return null;\n  }\n\n  /// Copy the underlying data as a list.\n  /// And [destroy()] itself at the same time.\n  Uint8List? takeList() {\n    if (_ptr != null && _ptr != nullptr) {\n      final list = Uint8List.fromList(asList()!);\n      destroy();\n      return list;\n    }\n    return null;\n  }\n}\n\n/// A facade wrapper for customize root path\nclass NameSpace {\n  late String _rootDir;\n\n  NameSpace(String dir) {\n    _rootDir = dir;\n  }\n\n  String get rootDir => _rootDir;\n\n  /// Get an MMKV instance with an unique ID [mmapID].\n  ///\n  /// * If you want a per-user mmkv, you could merge user-id within [mmapID].\n  /// * You can get a multi-process MMKV instance by passing [MMKVMode.MULTI_PROCESS_MODE].\n  /// * You can encrypt with [cryptKey], which limits to 16 bytes at most.\n  MMKV mmkv(String mmapID, {MMKVMode mode = MMKVMode.SINGLE_PROCESS_MODE, String? cryptKey, bool aes256 = false, int expectedCapacity = 0\n    , bool readOnly = false, bool? enableKeyExpire, int expiredInSeconds = 0, bool enableCompareBeforeSet = false, MMKVRecoverStrategic? recover\n    , int itemSizeLimit = 0}) {\n    final kv = MMKV._init(mmapID, mode: mode, cryptKey: cryptKey, aes256:aes256, rootDir: rootDir, expectedCapacity: expectedCapacity\n        , readOnly: readOnly, fromNameSpace: true, enableKeyExpire: enableKeyExpire, expiredInSeconds: expiredInSeconds\n        , enableCompareBeforeSet: enableCompareBeforeSet, recover: recover, itemSizeLimit: itemSizeLimit);\n    return kv;\n  }\n\n  /// backup one MMKV instance to [dstDir]\n  bool backupOneToDirectory(String mmapID, String dstDir) {\n    return MMKV.backupOneToDirectory(mmapID, dstDir,rootDir: _rootDir);\n  }\n\n  /// restore one MMKV instance from [srcDir]\n  bool restoreOneMMKVFromDirectory(String mmapID, String srcDir) {\n    return MMKV.restoreOneMMKVFromDirectory(mmapID, srcDir, rootDir: _rootDir);\n  }\n\n  /// Check whether the MMKV file is valid or not.\n  /// Note: Don't use this to check the existence of the instance, the result is undefined on nonexistent files.\n  bool isFileValid(String mmapID) {\n    return MMKV.isFileValid(mmapID, rootDir: _rootDir);\n  }\n\n  /// remove the storage of the MMKV, including the data file & meta file (.crc)\n  /// Note: the existing instance (if any) will be closed & destroyed\n  bool removeStorage(String mmapID) {\n    return MMKV.removeStorage(mmapID, rootDir: _rootDir);\n  }\n\n  /// check the existence of the MMKV\n  bool checkExist(String mmapID) {\n    return MMKV.checkExist(mmapID, rootDir: _rootDir);\n  }\n}\n\n/// An efficient, small mobile key-value storage framework developed by WeChat.\n/// Works on Android & iOS.\nclass MMKV {\n  Pointer<Void> _handle = nullptr;\n  static String _rootDir = \"\";\n\n  /// MMKV must be initialized before any usage.\n  ///\n  /// Generally speaking you should do this inside `main()`:\n  /// ```dart\n  /// void main() async {\n  ///   // must wait for MMKV to finish initialization\n  ///   final rootDir = await MMKV.initialize();\n  ///   print('MMKV for flutter with rootDir = $rootDir');\n  ///\n  ///   runApp(MyApp());\n  /// }\n  /// ```\n  /// Note that you must **wait for it** to finish before any usage.\n  /// * You can customize MMKV's root dir by passing [rootDir], `${Document}/mmkv` by default.\n  /// * You can customize MMKV's log level by passing [logLevel].\n  /// You can even turnoff logging by passing [MMKVLogLevel.None], which we don't recommend doing.\n  /// * If you want to use MMKV in multi-process on iOS, you should set group folder by passing [groupDir].\n  /// [groupDir] will be ignored on Android.\n  static Future<String> initialize({String? rootDir, String? groupDir, MMKVLogLevel logLevel = MMKVLogLevel.Info, MMKVHandler? handler}) async {\n    WidgetsFlutterBinding.ensureInitialized();\n\n    if (rootDir == null) {\n      final path = await _mmkvPlatform.getApplicationDocumentsPath();\n      rootDir = Platform.isWindows ? \"$path\\\\mmkv\" : \"$path/mmkv\";\n    }\n    _rootDir = rootDir;\n\n    _mmkvPlatform.theHandler = handler;\n    final logHandler = (handler != null && handler.wantLogRedirect()) ? Pointer.fromFunction<LogCallbackWrap>(_logRedirect) : nullptr;\n\n    final result = await _mmkvPlatform.initialize(rootDir, groupDir: groupDir, logLevel: logLevel.index, logHandler: logHandler);\n    if (handler != null) {\n      const ExceptionalReturn = -1;\n      final errorHandler = Pointer.fromFunction<ErrorCallbackWrap>(_errorHandler, ExceptionalReturn);\n      _registerErrorHandler(errorHandler);\n\n      if (handler.wantContentChangeNotification()) {\n        final contentHandler = Pointer.fromFunction<ContentCallbackWrap>(_contentChangeHandler);\n        _registerContentHandler(contentHandler);\n      }\n\n      final contentLoadedHandler = Pointer.fromFunction<ContentCallbackWrap>(_contentLoadedHandler);\n      _registerContentLoadedHandler(contentLoadedHandler);\n    }\n    return result;\n  }\n\n  /// create a NameSpace with custom root dir\n  static NameSpace nameSpace(String path) {\n    if (path.isNotEmpty) {\n      final rootDirPtr = _string2Pointer(path);\n      final ret = _getNameSpace(rootDirPtr);\n      calloc.free(rootDirPtr);\n      if (ret) {\n        final ns = NameSpace(path);\n        return ns;\n      }\n    }\n    throw StateError(\"Invalid rootPath $path\");\n  }\n\n  /// identical with the original MMKV with the global root dir\n  static NameSpace defaultNameSpace() {\n    if (rootDir.isEmpty) {\n      throw StateError(\"Invalid rootPath $rootDir\");\n    }\n    return NameSpace(rootDir);\n  }\n\n  /// The root directory of MMKV.\n  static String get rootDir => _rootDir;\n\n  /// A generic purpose instance in single-process mode.\n  ///\n  /// Note: If you come across to failing to load [defaultMMKV()] on Android after upgrading Flutter from 1.20+ to 2.0+,\n  /// you can try passing this [cryptKey] `'\\u{2}U'` instead.\n  /// ```dart\n  /// var mmkv = MMKV.defaultMMKV(cryptKey: '\\u{2}U');\n  /// ```\n  static MMKV defaultMMKV({String? cryptKey, bool aes256 = false, int expectedCapacity = 0, bool? enableKeyExpire, int expiredInSeconds = 0, bool enableCompareBeforeSet = false\n    , MMKVRecoverStrategic? recover, int itemSizeLimit = 0}) {\n    final mmkv = MMKV(\"\");\n    final cryptKeyPtr = _string2Pointer(cryptKey);\n    const mode = MMKVMode.SINGLE_PROCESS_MODE;\n    final int enableExpire = (enableKeyExpire == null) ? -1 : _bool2Int(enableKeyExpire);\n    final int recoverStrategy = (recover == null) ? -1 : (recover == MMKVRecoverStrategic.OnErrorDiscard ? 0 : 1);\n    mmkv._handle = _getDefaultMMKV(mode.index, cryptKeyPtr, _bool2Int(aes256), expectedCapacity, enableExpire, expiredInSeconds, _bool2Int(enableCompareBeforeSet)\n        , recoverStrategy, itemSizeLimit);\n    if (mmkv._handle == nullptr) {\n      throw StateError(\"Invalid state, forget initialize MMKV first?\");\n    }\n    calloc.free(cryptKeyPtr);\n    return mmkv;\n  }\n\n  /// Get an MMKV instance with an unique ID [mmapID].\n  ///\n  /// * If you want a per-user mmkv, you could merge user-id within [mmapID].\n  /// * You can get a multi-process MMKV instance by passing [MMKVMode.MULTI_PROCESS_MODE].\n  /// * You can encrypt with [cryptKey], which limits to 16 bytes at most.\n  /// * You can customize the [rootDir] of the file.\n  MMKV(String mmapID, {MMKVMode mode = MMKVMode.SINGLE_PROCESS_MODE, String? cryptKey, bool aes256 = false, String? rootDir, int expectedCapacity = 0\n    , bool readOnly = false, bool? enableKeyExpire, int expiredInSeconds = 0, bool enableCompareBeforeSet = false, MMKVRecoverStrategic? recover\n    , int itemSizeLimit = 0}) :\n    this._init(mmapID, mode: mode, cryptKey: cryptKey, aes256: aes256, rootDir: rootDir, expectedCapacity: expectedCapacity, readOnly: readOnly\n          , fromNameSpace: false, enableKeyExpire: enableKeyExpire, expiredInSeconds: expiredInSeconds, enableCompareBeforeSet: enableCompareBeforeSet\n          , recover: recover, itemSizeLimit: itemSizeLimit);\n\n  MMKV._init(String mmapID, {MMKVMode mode = MMKVMode.SINGLE_PROCESS_MODE, String? cryptKey, String? rootDir, int expectedCapacity = 0\n    , bool readOnly = false, bool fromNameSpace = false, bool aes256 = false, bool? enableKeyExpire, int expiredInSeconds = 0\n    , bool enableCompareBeforeSet = false, MMKVRecoverStrategic? recover, int itemSizeLimit = 0}) {\n    if (mmapID.isNotEmpty) {\n      final mmapIDPtr = _string2Pointer(mmapID);\n      final cryptKeyPtr = _string2Pointer(cryptKey);\n      final rootDirPtr = _string2Pointer(rootDir);\n\n      final realMode = readOnly ? (mode.index | _READ_ONLY_MODE) : mode.index;\n      final int enableExpire = (enableKeyExpire == null) ? -1 : _bool2Int(enableKeyExpire);\n      final int recoverStrategy = (recover == null) ? -1 : (recover == MMKVRecoverStrategic.OnErrorDiscard ? 0 : 1);\n      _handle = _getMMKVWithID(mmapIDPtr, realMode, cryptKeyPtr, rootDirPtr, expectedCapacity, _bool2Int(fromNameSpace), _bool2Int(aes256)\n      , enableExpire, expiredInSeconds, _bool2Int(enableCompareBeforeSet), recoverStrategy, itemSizeLimit);\n      if (_handle == nullptr) {\n        throw StateError(\"Invalid state, forget initialize MMKV first?\");\n      }\n\n      calloc.free(mmapIDPtr);\n      calloc.free(cryptKeyPtr);\n      calloc.free(rootDirPtr);\n    }\n  }\n\n  String get mmapID {\n    return _pointer2String(_mmapID(_handle))!;\n  }\n\n  static const int ExpireNever = 0;\n  static const int ExpireInMinute = 60;\n  static const int ExpireInHour = 60 * 60;\n  static const int ExpireInDay = 24 * 60 * 60;\n  static const int ExpireInMonth = 30 * 24 * 60 * 60;\n  static const int ExpireInYear = 365 * 30 * 24 * 60 * 60;\n\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeBool(String key, bool value, [int? expireDurationInSecond]) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = (expireDurationInSecond == null)\n        ? _encodeBool(_handle, keyPtr, _bool2Int(value))\n        : _encodeBoolV2(_handle, keyPtr, _bool2Int(value), expireDurationInSecond);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  bool decodeBool(String key, {bool defaultValue = false}) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _decodeBool(_handle, keyPtr, _bool2Int(defaultValue));\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  /// Use this when the [value] won't be larger than a normal int32.\n  /// It's more efficient & cost less space.\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeInt32(String key, int value, [int? expireDurationInSecond]) {\n    final keyPtr = key.toNativeUtf8();\n    final ret =\n        (expireDurationInSecond == null) ? _encodeInt32(_handle, keyPtr, value) : _encodeInt32V2(_handle, keyPtr, value, expireDurationInSecond);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  /// Use this when the value won't be larger than a normal int32.\n  /// It's more efficient & cost less space.\n  int decodeInt32(String key, {int defaultValue = 0}) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _decodeInt32(_handle, keyPtr, defaultValue);\n    calloc.free(keyPtr);\n    return ret;\n  }\n\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeInt(String key, int value, [int? expireDurationInSecond]) {\n    final keyPtr = key.toNativeUtf8();\n    final ret =\n        (expireDurationInSecond == null) ? _encodeInt64(_handle, keyPtr, value) : _encodeInt64V2(_handle, keyPtr, value, expireDurationInSecond);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  int decodeInt(String key, {int defaultValue = 0}) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _decodeInt64(_handle, keyPtr, defaultValue);\n    calloc.free(keyPtr);\n    return ret;\n  }\n\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeDouble(String key, double value, [int? expireDurationInSecond]) {\n    final keyPtr = key.toNativeUtf8();\n    final ret =\n        (expireDurationInSecond == null) ? _encodeDouble(_handle, keyPtr, value) : _encodeDoubleV2(_handle, keyPtr, value, expireDurationInSecond);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  double decodeDouble(String key, {double defaultValue = 0}) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _decodeDouble(_handle, keyPtr, defaultValue);\n    calloc.free(keyPtr);\n    return ret;\n  }\n\n  /// Encode an utf-8 string.\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeString(String key, String? value, [int? expireDurationInSecond]) {\n    if (value == null) {\n      removeValue(key);\n      return true;\n    }\n\n    final keyPtr = key.toNativeUtf8();\n    final bytes = MMBuffer.fromList(const Utf8Encoder().convert(value))!;\n\n    final ret = (expireDurationInSecond == null)\n        ? _encodeBytes(_handle, keyPtr, bytes.pointer!, bytes.length)\n        : _encodeBytesV2(_handle, keyPtr, bytes.pointer!, bytes.length, expireDurationInSecond);\n\n    calloc.free(keyPtr);\n    bytes.destroy();\n    return _int2Bool(ret);\n  }\n\n  /// Decode as an utf-8 string.\n  String? decodeString(String key) {\n    final keyPtr = key.toNativeUtf8();\n    final lengthPtr = calloc<Uint64>();\n\n    final ret = _decodeBytes(_handle, keyPtr, lengthPtr);\n    calloc.free(keyPtr);\n\n    if (ret != nullptr) {\n      final length = lengthPtr.value;\n      calloc.free(lengthPtr);\n      final result = _buffer2String(ret, length);\n      if (!_isDarwin() && length > 0) {\n        _freePtr(ret);\n      }\n      return result;\n    }\n    calloc.free(lengthPtr);\n    return null;\n  }\n\n  /// Encoding bytes.\n  ///\n  /// You can serialize an object into bytes, then store it inside MMKV.\n  /// ```dart\n  /// // assume using protobuf https://developers.google.com/protocol-buffers/docs/darttutorial\n  /// var object = MyClass();\n  /// final list = object.writeToBuffer();\n  /// final buffer = MMBuffer.fromList(list);\n  ///\n  /// mmkv.encodeBytes('bytes', buffer);\n  ///\n  /// buffer.destroy();\n  /// ```\n  /// [expireDurationInSecond] override the default duration setting from [enableAutoKeyExpire()].\n  /// * Passing [MMKV.ExpireNever] (aka 0) will never expire.\n  bool encodeBytes(String key, MMBuffer? value, [int? expireDurationInSecond]) {\n    if (value == null) {\n      removeValue(key);\n      return true;\n    }\n\n    final keyPtr = key.toNativeUtf8();\n    final ret = (expireDurationInSecond == null)\n        ? _encodeBytes(_handle, keyPtr, value.pointer!, value.length)\n        : _encodeBytesV2(_handle, keyPtr, value.pointer!, value.length, expireDurationInSecond);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  /// Decoding bytes.\n  ///\n  /// You can decode bytes from MMKV, then deserialize an object from the bytes.\n  /// ```dart\n  /// // assume using protobuf https://developers.google.com/protocol-buffers/docs/darttutorial\n  /// final bytes = mmkv.decodeBytes('bytes');\n  /// if (bytes != null) {\n  ///   final list = bytes.asList();\n  ///   final object = MyClass.fromBuffer(list);\n  ///\n  ///   // Must [destroy()] after no longer use.\n  ///   bytes.destroy();\n  /// }\n  /// ```\n  MMBuffer? decodeBytes(String key) {\n    final keyPtr = key.toNativeUtf8();\n    final lengthPtr = calloc<Uint64>();\n\n    final ret = _decodeBytes(_handle, keyPtr, lengthPtr);\n    calloc.free(keyPtr);\n\n    if (/*ret != null && */ ret != nullptr) {\n      final length = lengthPtr.value;\n      calloc.free(lengthPtr);\n      if (_isDarwin() || length == 0) {\n        return MMBuffer._copyFromPointer(ret, length);\n      } else {\n        return MMBuffer._fromPointer(ret, length);\n      }\n    }\n    calloc.free(lengthPtr);\n    return null;\n  }\n\n  /// Change encryption key for the MMKV instance.\n  ///\n  /// * The [cryptKey] is 16 bytes limited.\n  /// * You can transfer a plain-text MMKV into encrypted by setting an non-null, non-empty [cryptKey].\n  /// * Or vice versa by passing [cryptKey] with null.\n  /// See also [checkReSetCryptKey()].\n  bool reKey(String? cryptKey, {bool aes256 = false}) {\n    if (cryptKey != null && cryptKey.isNotEmpty) {\n      final bytes = MMBuffer.fromList(const Utf8Encoder().convert(cryptKey))!;\n      final ret = _reKey(_handle, bytes.pointer!, bytes.length, _bool2Int(aes256));\n      bytes.destroy();\n      return _int2Bool(ret);\n    } else {\n      final ret = _reKey(_handle, nullptr, 0, 0);\n      return _int2Bool(ret);\n    }\n  }\n\n  /// See also [reKey()].\n  String? get cryptKey {\n    final lengthPtr = calloc<Uint64>();\n    final ret = _cryptKey(_handle, lengthPtr);\n    if (/*ret != null && */ ret != nullptr) {\n      final length = lengthPtr.value;\n      calloc.free(lengthPtr);\n      final result = _buffer2String(ret, length);\n      _freePtr(ret);\n      return result;\n    }\n    return null;\n  }\n\n  /// Just reset the [cryptKey] (will not encrypt or decrypt anything).\n  /// Usually you should call this method after other process [reKey()] the multi-process mmkv.\n  void checkReSetCryptKey(String cryptKey, {bool aes256 = false}) {\n    final bytes = MMBuffer.fromList(const Utf8Encoder().convert(cryptKey))!;\n    _checkReSetCryptKey(_handle, bytes.pointer!, bytes.length, _bool2Int(aes256));\n    bytes.destroy();\n  }\n\n  /// Get the actual size consumption of the key's value.\n  /// Pass [actualSize] with true to get value's length.\n  int valueSize(String key, bool actualSize) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _valueSize(_handle, keyPtr, _bool2Int(actualSize));\n    calloc.free(keyPtr);\n    return ret;\n  }\n\n  /// Write the value to a pre-allocated native buffer.\n  ///\n  /// * Return size written into buffer.\n  /// * Return -1 on any error, such as [buffer] not large enough.\n  int writeValueToNativeBuffer(String key, MMBuffer buffer) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _writeValueToNB(_handle, keyPtr, buffer.pointer!.cast(), buffer.length);\n    calloc.free(keyPtr);\n    return ret;\n  }\n\n  /// Get all the keys (_unsorted_).\n  List<String> get allKeys {\n    return _allKeysImp(false);\n  }\n\n  /// Get all non-expired keys (_unsorted_). Note that this call has costs.\n  List<String> get allNonExpiredKeys {\n    return _allKeysImp(true);\n  }\n\n  List<String> _allKeysImp(bool filterExpire) {\n    final keyArrayPtr = calloc<Pointer<Pointer<Utf8>>>();\n    final sizeArrayPtr = calloc<Pointer<Uint32>>();\n    final List<String> keys = [];\n\n    final count = _allKeys(_handle, keyArrayPtr, sizeArrayPtr, _bool2Int(filterExpire));\n    if (count > 0) {\n      final keyArray = keyArrayPtr[0];\n      final sizeArray = sizeArrayPtr[0];\n      for (int index = 0; index < count; index++) {\n        final keyPtr = keyArray[index];\n        final size = sizeArray[index];\n        final key = _buffer2String(keyPtr.cast(), size);\n        if (key != null) {\n          keys.add(key);\n        }\n        if (!_isDarwin()) {\n          _freePtr(keyPtr);\n        }\n      }\n      _freePtr(keyArray);\n      _freePtr(sizeArray);\n    }\n\n    calloc.free(sizeArrayPtr);\n    calloc.free(keyArrayPtr);\n\n    return keys;\n  }\n\n  bool containsKey(String key) {\n    final keyPtr = key.toNativeUtf8();\n    final ret = _containsKey(_handle, keyPtr);\n    calloc.free(keyPtr);\n    return _int2Bool(ret);\n  }\n\n  int get count {\n    return _count(_handle, _bool2Int(false));\n  }\n\n  /// Get non-expired keys. Note that this call has costs.\n  int get countNonExpiredKeys {\n    return _count(_handle, _bool2Int(true));\n  }\n\n  /// Get the file size. See also [actualSize].\n  int get totalSize {\n    return _totalSize(_handle);\n  }\n\n  /// Get the actual used size. See also [totalSize].\n  int get actualSize {\n    return _actualSize(_handle);\n  }\n\n  bool get isMultiProcess {\n    return _isMultiProcess(_handle);\n  }\n\n  bool get isReadOnly {\n    return _isReadOnly(_handle);\n  }\n\n  void removeValue(String key) {\n    final keyPtr = key.toNativeUtf8();\n    _removeValueForKey(_handle, keyPtr);\n    calloc.free(keyPtr);\n  }\n\n  /// See also [trim()].\n  void removeValues(List<String> keys) {\n    if (keys.isEmpty) {\n      return;\n    }\n    final Pointer<Pointer<Utf8>> keyArray = calloc<Pointer<Utf8>>(keys.length);\n    final Pointer<Uint32> sizeArray = malloc<Uint32>(keys.length);\n    for (int index = 0; index < keys.length; index++) {\n      final key = keys[index];\n      final bytes = MMBuffer.fromList(const Utf8Encoder().convert(key))!;\n      sizeArray[index] = bytes.length;\n      keyArray[index] = bytes.pointer!.cast();\n    }\n\n    _removeValuesForKeys(_handle, keyArray, sizeArray, keys.length);\n\n    for (int index = 0; index < keys.length; index++) {\n      calloc.free(keyArray[index]);\n    }\n    calloc.free(keyArray);\n    calloc.free(sizeArray);\n  }\n\n  void clearAll({bool keepSpace = false}) {\n    _clearAll(_handle, _bool2Int(keepSpace));\n  }\n\n  /// Synchronize memory to file.\n  /// You don't need to call this, really, I mean it.\n  /// Unless you worry about running out of battery.\n  /// * Pass `true` to perform synchronous write.\n  /// * Pass `false` to perform asynchronous write, return immediately.\n  void sync(bool sync) {\n    _mmkvSync(_handle, _bool2Int(sync));\n  }\n\n  /// Clear all caches (on memory warning).\n  void clearMemoryCache() {\n    _clearMemoryCache(_handle);\n  }\n\n  /// Get memory page size.\n  static int get pageSize {\n    return _pageSize();\n  }\n\n  static String get version {\n    return _pointer2String(_version())!;\n  }\n\n  /// Trim the file size to minimal.\n  ///\n  /// * MMKV's size won't reduce after deleting key-values.\n  /// * Call this method after lots of deleting if you care about disk usage.\n  /// * Note that [clearAll()] has the similar effect.\n  void trim() {\n    _trim(_handle);\n  }\n\n  /// import all key-value items from src\n  ///\n  /// * Return count of items imported.\n  int importFrom(MMKV src) {\n    return _importFrom(_handle, src._handle);\n  }\n\n  /// Close the instance when it's no longer needed in the near future.\n  /// Any subsequent call to the instance is **undefined behavior**.\n  void close() {\n    _mmkvClose(_handle);\n  }\n\n  /// backup one MMKV instance to [dstDir]\n  ///\n  /// * [rootDir] the customize root path of the MMKV, if null then backup from the root dir of MMKV\n  static bool backupOneToDirectory(String mmapID, String dstDir, {String? rootDir}) {\n    final mmapIDPtr = mmapID.toNativeUtf8();\n    final dstDirPtr = dstDir.toNativeUtf8();\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _backupOne(mmapIDPtr, dstDirPtr, rootDirPtr);\n\n    calloc.free(mmapIDPtr);\n    calloc.free(dstDirPtr);\n    calloc.free(rootDirPtr);\n\n    return _int2Bool(ret);\n  }\n\n  /// restore one MMKV instance from [srcDir]\n  ///\n  /// * [rootDir] the customize root path of the MMKV, if null then restore to the root dir of MMKV\n  static bool restoreOneMMKVFromDirectory(String mmapID, String srcDir, {String? rootDir}) {\n    final mmapIDPtr = mmapID.toNativeUtf8();\n    final srcDirPtr = srcDir.toNativeUtf8();\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _restoreOne(mmapIDPtr, srcDirPtr, rootDirPtr);\n\n    calloc.free(mmapIDPtr);\n    calloc.free(srcDirPtr);\n    calloc.free(rootDirPtr);\n\n    return _int2Bool(ret);\n  }\n\n  /// backup all MMKV instance to [dstDir]\n  static int backupAllToDirectory(String dstDir) {\n    final dstDirPtr = dstDir.toNativeUtf8();\n\n    final ret = _backupAll(dstDirPtr);\n\n    calloc.free(dstDirPtr);\n\n    return ret;\n  }\n\n  /// restore all MMKV instance from [srcDir]\n  static int restoreAllFromDirectory(String srcDir) {\n    final srcDirPtr = srcDir.toNativeUtf8();\n\n    final ret = _restoreAll(srcDirPtr);\n\n    calloc.free(srcDirPtr);\n\n    return ret;\n  }\n\n  /// Enable auto key expiration. This is a upgrade operation, the file format will change.\n  /// And the file won't be accessed correctly by older version (v1.2.17) of MMKV.\n  /// [expireDurationInSecond] the expire duration for all keys, [MMKV.ExpireNever] (0) means no default duration (aka each key will have it's own expire date)\n  bool enableAutoKeyExpire(int expiredInSeconds) {\n    return _enableAutoExpire(_handle, expiredInSeconds);\n  }\n\n  /// Disable auto key expiration. This is a downgrade operation.\n  bool disableAutoKeyExpire() {\n    return _disableAutoExpire(_handle);\n  }\n\n  /// Enable compare value before update/insert.\n  bool enableCompareBeforeSet() {\n    return _enableCompareBeforeSet(_handle);\n  }\n\n  /// Disable compare value before update/insert.\n  bool disableCompareBeforeSet() {\n    return _disableCompareBeforeSet(_handle);\n  }\n\n  /// Check whether the MMKV file is valid or not.\n  /// Note: Don't use this to check the existence of the instance, the result is undefined on nonexistent files.\n  static bool isFileValid(String mmapID, {String? rootDir}) {\n    final mmapIDPtr = mmapID.toNativeUtf8();\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _isFileValid(mmapIDPtr, rootDirPtr);\n\n    calloc.free(mmapIDPtr);\n    calloc.free(rootDirPtr);\n\n    return _int2Bool(ret);\n  }\n\n  /// remove the storage of the MMKV, including the data file & meta file (.crc)\n  /// Note: the existing instance (if any) will be closed & destroyed\n  static bool removeStorage(String mmapID, {String? rootDir}) {\n    final mmapIDPtr = mmapID.toNativeUtf8();\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _removeStorage(mmapIDPtr, rootDirPtr);\n\n    calloc.free(mmapIDPtr);\n    calloc.free(rootDirPtr);\n\n    return _int2Bool(ret);\n  }\n\n  /// check the existence of the MMKV\n  static bool checkExist(String mmapID, {String? rootDir}) {\n    final mmapIDPtr = mmapID.toNativeUtf8();\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _checkExist(mmapIDPtr, rootDirPtr);\n\n    calloc.free(mmapIDPtr);\n    calloc.free(rootDirPtr);\n\n    return _int2Bool(ret);\n  }\n\n  /// return the iOS App Group root path for MMKV multi-process instances\n  static String? groupPath() {\n    return _isDarwin() ? _pointer2String(_groupPath()) : null;\n  }\n\n  /// check if the multi-process MMKV instance has been modified by other processes\n  void checkContentChangedByOuterProcess() {\n    _checkContentChanged(_handle);\n  }\n}\n\nbool _isDarwin() {\n  return Platform.isIOS || Platform.isMacOS;\n}\n\nvoid _logRedirect(int logLevel, Pointer<Utf8> file, int line, Pointer<Utf8> funcname, Pointer<Utf8> message) {\n  if (_mmkvPlatform.theHandler == null) {\n    return;\n  }\n\n  MMKVLogLevel level;\n  switch (logLevel) {\n    case 0:\n      level = MMKVLogLevel.Debug;\n      break;\n    case 1:\n      level = MMKVLogLevel.Info;\n      break;\n    case 2:\n      level = MMKVLogLevel.Warning;\n      break;\n    case 3:\n      level = MMKVLogLevel.Error;\n      break;\n    case 4:\n    default:\n      level = MMKVLogLevel.None;\n      break;\n  }\n\n  _mmkvPlatform.theHandler?.mmkvLog(level, _pointer2String(file)!, line, _pointer2String(funcname)!, _pointer2String(message)!);\n}\n\nenum _MMKVErrorType {\n  MMKVCRCCheckFail,\n  MMKVFileLength,\n}\n\nint _errorHandler(Pointer<Utf8> mmapIDPtr, int errorType) {\n  if (_mmkvPlatform.theHandler == null || mmapIDPtr == nullptr) {\n    return MMKVRecoverStrategic.OnErrorDiscard.index;\n  }\n\n  final mmapID = _pointer2String(mmapIDPtr);\n  MMKVRecoverStrategic strategic;\n  if (errorType == _MMKVErrorType.MMKVCRCCheckFail.index) {\n    strategic = _mmkvPlatform.theHandler!.onMMKVCRCCheckFail(mmapID!);\n  } else if (errorType == _MMKVErrorType.MMKVFileLength.index) {\n    strategic = _mmkvPlatform.theHandler!.onMMKVFileLengthError(mmapID!);\n  } else {\n    strategic = MMKVRecoverStrategic.OnErrorDiscard;\n  }\n\n  return strategic.index;\n}\n\nvoid _contentChangeHandler(Pointer<Utf8> mmapIDPtr) {\n  if (_mmkvPlatform.theHandler != null && mmapIDPtr != nullptr) {\n    final handler = _mmkvPlatform.theHandler!;\n    if (handler.wantContentChangeNotification()) {\n      final mmapID = _pointer2String(mmapIDPtr);\n      handler.onContentChangedByOuterProcess(mmapID!);\n    }\n  }\n}\n\nvoid _contentLoadedHandler(Pointer<Utf8> mmapIDPtr) {\n  if (_mmkvPlatform.theHandler != null && mmapIDPtr != nullptr) {\n    final mmapID = _pointer2String(mmapIDPtr);\n    _mmkvPlatform.theHandler!.onMMKVContentLoadSuccessfully(mmapID!);\n  }\n}\n\nint _bool2Int(bool value) {\n  return value ? 1 : 0;\n}\n\nbool _int2Bool(int value) {\n  return (value != 0) ? true : false;\n}\n\nPointer<Utf8> _string2Pointer(String? str) {\n  if (str != null) {\n    return str.toNativeUtf8();\n  }\n  return nullptr;\n}\n\nString? _pointer2String(Pointer<Utf8>? ptr) {\n  if (ptr != null && ptr != nullptr) {\n    return ptr.toDartString();\n  }\n  return null;\n}\n\nString? _buffer2String(Pointer<Uint8>? ptr, int length) {\n  if (ptr != null && ptr != nullptr) {\n    final listView = ptr.asTypedList(length);\n    return const Utf8Decoder().convert(listView);\n  }\n  return null;\n}\n\nfinal MMKVPluginPlatform _mmkvPlatform = MMKVPluginPlatform.instance!;\n\nfinal Pointer<Void> Function(Pointer<Utf8> mmapID, int, Pointer<Utf8> cryptKey, Pointer<Utf8> rootDir, int expectedCapacity, int isNameSpace, int aes256,\n    int enableKeyExpire, int expiredInSeconds, int enableCompareBeforeSet, int recover, int itemSizeLimit) _getMMKVWithID =\n_mmkvPlatform.getMMKVWithIDFunc();\n\nfinal Pointer<Void> Function(int, Pointer<Utf8> cryptKey, int aes256, int expectedCapacity, int enableKeyExpire, int expiredInSeconds, int enableCompareBeforeSet, int recover,\n    int itemSizeLimit) _getDefaultMMKV = _mmkvPlatform.getDefaultMMKVFunc();\n\nfinal Pointer<Utf8> Function(Pointer<Void>) _mmapID = _mmkvPlatform.mmapIDFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _encodeBool = _mmkvPlatform.encodeBoolFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int, int) _encodeBoolV2 = _mmkvPlatform.encodeBoolV2Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _decodeBool = _mmkvPlatform.decodeBoolFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _encodeInt32 = _mmkvPlatform.encodeInt32Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int, int) _encodeInt32V2 = _mmkvPlatform.encodeInt32V2Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _decodeInt32 = _mmkvPlatform.decodeInt32Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _encodeInt64 = _mmkvPlatform.encodeInt64Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int, int) _encodeInt64V2 = _mmkvPlatform.encodeInt64V2Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _decodeInt64 = _mmkvPlatform.decodeInt64Func();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, double) _encodeDouble = _mmkvPlatform.encodeDoubleFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, double, int) _encodeDoubleV2 = _mmkvPlatform.encodeDoubleV2Func();\n\nfinal double Function(Pointer<Void>, Pointer<Utf8>, double) _decodeDouble = _mmkvPlatform.decodeDoubleFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int) _encodeBytes =  _mmkvPlatform.encodeBytesFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int, int) _encodeBytesV2 =  _mmkvPlatform.encodeBytesV2Func();\n\nfinal Pointer<Uint8> Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint64>) _decodeBytes =  _mmkvPlatform.decodeBytesFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) _reKey = _mmkvPlatform.reKeyFunc();\n\nfinal Pointer<Uint8> Function(Pointer<Void>, Pointer<Uint64>) _cryptKey = _mmkvPlatform.cryptKeyFunc();\n\nfinal void Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) _checkReSetCryptKey = _mmkvPlatform.checkReSetCryptKeyFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, int) _valueSize = _mmkvPlatform.valueSizeFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Void>, int) _writeValueToNB =  _mmkvPlatform.writeValueToNBFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Pointer<Pointer<Utf8>>>, Pointer<Pointer<Uint32>>, int) _allKeys =  _mmkvPlatform.allKeysFunc();\nfinal int Function(Pointer<Void>, Pointer<Utf8>) _containsKey = _mmkvPlatform.containsKeyFunc();\n\nfinal int Function(Pointer<Void>, int) _count = _mmkvPlatform.countFunc();\n\nfinal int Function(Pointer<Void>) _totalSize = _mmkvPlatform.totalSizeFunc();\n\nfinal int Function(Pointer<Void>) _actualSize = _mmkvPlatform.actualSizeFunc();\n\nfinal void Function(Pointer<Void>, Pointer<Utf8>) _removeValueForKey = _mmkvPlatform.removeValueForKeyFunc();\n\nfinal void Function(Pointer<Void>, Pointer<Pointer<Utf8>>, Pointer<Uint32>, int) _removeValuesForKeys =  _mmkvPlatform.removeValuesForKeysFunc();\nfinal void Function(Pointer<Void>, int) _clearAll = _mmkvPlatform.clearAllFunc();\n\nfinal void Function(Pointer<Void>, int) _mmkvSync =  _mmkvPlatform.mmkvSyncFunc();\n\nfinal void Function(Pointer<Void>) _clearMemoryCache = _mmkvPlatform.clearMemoryCacheFunc();\n\nfinal int Function() _pageSize =  _mmkvPlatform.pageSizeFunc();\n\nfinal Pointer<Utf8> Function() _version =  _mmkvPlatform.versionFunc();\n\nfinal void Function(Pointer<Void>) _trim =  _mmkvPlatform.trimFunc();\n\nfinal void Function(Pointer<Void>) _mmkvClose =  _mmkvPlatform.mmkvCloseFunc();\n\nfinal void Function(Pointer<Void>, Pointer<Void>, int) _memcpy = _mmkvPlatform.memcpyFunc();\n\nfinal int Function(Pointer<Utf8> mmapID, Pointer<Utf8> dstDir, Pointer<Utf8> rootPath) _backupOne = _mmkvPlatform.backupOneFunc();\n\nfinal int Function(Pointer<Utf8> mmapID, Pointer<Utf8> srcDir, Pointer<Utf8> rootPath) _restoreOne = _mmkvPlatform.restoreOneFunc();\n\nfinal int Function(Pointer<Utf8> dstDir) _backupAll = _mmkvPlatform.backupAllFunc();\nfinal int Function(Pointer<Utf8> srcDir) _restoreAll = _mmkvPlatform.restoreAllFunc();\n\nfinal bool Function(Pointer<Void>, int) _enableAutoExpire = _mmkvPlatform.enableAutoExpireFunc();\n\nfinal bool Function(Pointer<Void>) _disableAutoExpire = _mmkvPlatform.disableAutoExpireFunc();\n\nfinal bool Function(Pointer<Void>) _enableCompareBeforeSet = _mmkvPlatform.enableCompareBeforeSetFunc();\n\nfinal bool Function(Pointer<Void>) _disableCompareBeforeSet = _mmkvPlatform.disableCompareBeforeSetFunc();\n\nfinal int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) _removeStorage = _mmkvPlatform.removeStorageFunc();\n\nfinal bool Function(Pointer<Void>) _isMultiProcess = _mmkvPlatform.isMultiProcessFunc();\n\nfinal bool Function(Pointer<Void>) _isReadOnly = _mmkvPlatform.isReadOnlyFunc();\n\nfinal ErrorCallbackRegister _registerErrorHandler = _mmkvPlatform.registerErrorHandlerFunc();\n\nfinal ContentCallbackRegister _registerContentHandler = _mmkvPlatform.registerContentHandlerFunc();\n\nfinal ContentCallbackRegister _registerContentLoadedHandler = _mmkvPlatform.registerContentLoadedHandlerFunc();\n\nfinal void Function(Pointer<Void>) _checkContentChanged = _mmkvPlatform.checkContentChangedFunc();\n\nfinal bool Function(Pointer<Utf8>) _getNameSpace = _mmkvPlatform.getNameSpaceFunc();\n\nfinal int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) _checkExist = _mmkvPlatform.checkExistFunc();\n\nfinal int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) _isFileValid = _mmkvPlatform.isFileValidFunc();\n\nfinal Pointer<Utf8> Function() _groupPath = _mmkvPlatform.groupPathFunc();\n\nfinal int Function(Pointer<Void>, Pointer<Void>) _importFrom = _mmkvPlatform.importFromFunc();\n\nfinal void Function(Pointer) _freePtr = _mmkvPlatform.freePtrFunc();"
  },
  {
    "path": "flutter/mmkv/mmkv.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/lib\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/test\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.idea\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build\" />\n    </content>\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" name=\"Dart SDK\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Flutter Plugins\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Dart Packages\" level=\"project\" />\n  </component>\n</module>\n"
  },
  {
    "path": "flutter/mmkv/pubspec.yaml",
    "content": "name: mmkv\ndescription: An efficient, small mobile key-value storage framework developed by WeChat. Works on Android & iOS.\nversion: 2.4.0\nhomepage: https://github.com/Tencent/mmkv\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.7.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  ffi: ^2.0.0\n  mmkv_ios:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_ios\n  mmkv_android:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_android\n  mmkv_ohos:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_ohos\n  mmkv_win32:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_win32\n  mmkv_linux:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_linux\n  mmkv_platform_interface:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_platform_interface\n\ndev_dependencies:\n  test:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' and Android 'package' identifiers should not ordinarily\n  # be modified. They are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    platforms:\n      android:\n        default_package: mmkv_android\n      ios:\n        default_package: mmkv_ios\n      ohos:\n        default_package: mmkv_ohos\n      macos:\n        default_package: mmkv_ios\n      windows:\n        default_package: mmkv_win32\n      linux:\n        default_package: mmkv_linux\n"
  },
  {
    "path": "flutter/mmkv/test/mmkv_test.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"package:mmkv/mmkv.dart\";\nimport \"package:test/test.dart\";\n\nvoid main() {\n  test(\"Int value should be written and read\", () {\n    final mmkv = MMKV.defaultMMKV();\n\n    mmkv.encodeInt(\"int\", 1024);\n\n    expect(mmkv.decodeInt(\"int\"), 1024);\n  });\n\n  test(\"String value should be written and read\", () {\n    final mmkv = MMKV.defaultMMKV();\n\n    mmkv.encodeString(\"string\", \"test\");\n\n    expect(mmkv.decodeInt(\"string\"), \"test\");\n  });\n\n  test(\"bool value should be written and read\", () {\n    final mmkv = MMKV.defaultMMKV();\n\n    mmkv.encodeBool(\"bool\", true);\n\n    expect(mmkv.decodeBool(\"bool\"), true);\n  });\n}\n"
  },
  {
    "path": "flutter/mmkv/tool/fix_mmkv_plugin_name.rb",
    "content": "def fix_mmkv_plugin_name(flutter_application_path)\n  return\nend\n\n"
  },
  {
    "path": "flutter/mmkv/tool/mmkvpodhelper.rb",
    "content": "#\n# Tencent is pleased to support the open source community by making\n# MMKV available.\n#\n# Copyright (C) 2020 THL A29 Limited, a Tencent company.\n# All rights reserved.\n#\n# Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n# this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#       https://opensource.org/licenses/BSD-3-Clause\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ndef mmkv_fix_plugin_name(flutter_application_path, is_module)\n  return\nend\n"
  },
  {
    "path": "flutter/mmkv.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/lib\" isTestSource=\"false\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.idea\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkv/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkv/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkv/build\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkvflutter/.dart_tool\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkvflutter/.pub\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/example/ios/.symlinks/plugins/mmkvflutter/build\" />\n    </content>\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" name=\"Dart SDK\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"Flutter Plugins\" level=\"project\" />\n  </component>\n</module>"
  },
  {
    "path": "flutter/mmkv_android/CHANGELOG.md",
    "content": "# MMKV Platform Android Change Log\n## v2.4.0 / 2026-03-18\nKeep up with native lib v2.4.0.\n\n## v2.3.0 / 2025-12-03\nKeep up with native lib v2.3.0.\n\n## v2.2.4 / 2025-09-25\nKeep up with native lib v2.2.4.\n\n## v2.2.3 / 2025-08-20\nKeep up with native lib v2.2.3.\n\n## v2.2.2 / 2025-05-08\nKeep up with native lib v2.2.2.\n\n## v2.2.1 / 2025-04-25\nKeep up with native lib v2.2.1.\n\n## v2.2.0 / 2025-04-24\nKeep up with native lib v2.2.0.\n\n## v2.1.0 / 2025-02-18\nKeep up with native lib v2.1.0.\n\n## v2.0.0 / 2024-10-25\nBump to 2.0 to setup a breaking change version.\n\n## v1.0.7 / 2024-10-24\nRollback native lib to v1.3.x.\n\n## v1.0.6 / 2024-10-21\nKeep up with native lib v2.0.0.\n\n## v1.0.5 / 2024-07-26\nKeep up with native lib v1.3.9.\n\n## v1.0.4 / 2024-07-12\nLower Dart to 2.17 to avoid OHOS conflict.\n\n## v1.0.3 / 2024-07-08\nKeep up with native lib v1.3.7.\n\n## v1.0.2 / 2024-07-05\nKeep up with native lib v1.3.6.\n\n## v1.0.1 / 2024-04-24\nKeep up with native lib v1.3.5.\n\n## v1.0.0 / 2024-04-19\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_android/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_android/README.md",
    "content": "# mmkv\\_android\n\nThe Android implementation detail of [`MMKV`][1].\n\n## Usage\n\nNever use it alone. It's NOT a complete set of MMKV functionality. It just provides the FFI implementation needed by MMKV.\n\nThis package is [endorsed][2], which means you can simply use `mmkv`\nnormally. This package will be automatically included in your app when you do,\nso you do not need to add it to your `pubspec.yaml`.\n\nHowever, if you `import` this package to use any of its APIs directly, you\nshould add it to your `pubspec.yaml` as usual.\n\n[1]: https://pub.dev/packages/mmkv\n[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin\n"
  },
  {
    "path": "flutter/mmkv_android/android/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": "flutter/mmkv_android/android/build.gradle",
    "content": "group 'com.tencent.mmkv'\nversion '1.0'\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.9.2'\n    }\n}\n\nrootProject.allprojects {\n    repositories {\n        google()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n\napply plugin: 'com.android.library'\n\nandroid {\n    namespace \"com.tencent.mmkvflutter\"\n    compileSdkVersion 35\n\n    defaultConfig {\n        minSdkVersion 23\n    }\n    lintOptions {\n        disable 'InvalidPackage'\n    }\n\n    dependencies {\n        implementation 'com.tencent:mmkv:[2.4.0, 2.5)'\n    }\n}\n"
  },
  {
    "path": "flutter/mmkv_android/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10.2-bin.zip\n"
  },
  {
    "path": "flutter/mmkv_android/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nandroid.enableR8=true\n"
  },
  {
    "path": "flutter/mmkv_android/android/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "flutter/mmkv_android/android/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "flutter/mmkv_android/android/settings.gradle",
    "content": "rootProject.name = 'mmkv'"
  },
  {
    "path": "flutter/mmkv_android/android/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"></manifest>"
  },
  {
    "path": "flutter/mmkv_android/android/src/main/java/com/tencent/mmkv/MMKVPlugin.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.tencent.mmkv;\n\nimport android.content.Context;\nimport androidx.annotation.NonNull;\nimport io.flutter.embedding.engine.plugins.FlutterPlugin;\nimport io.flutter.plugin.common.MethodCall;\nimport io.flutter.plugin.common.MethodChannel;\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler;\nimport io.flutter.plugin.common.MethodChannel.Result;\n\n/* MMKVPlugin */\npublic class MMKVPlugin implements FlutterPlugin, MethodCallHandler {\n    /// The MethodChannel that will the communication between Flutter and native Android\n    ///\n    /// This local reference serves to register the plugin with the Flutter Engine and unregister it\n    /// when the Flutter Engine is detached from the Activity\n    private MethodChannel channel;\n    private Context context;\n\n    @Override\n    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {\n        loadLibrary();\n\n        context = flutterPluginBinding.getApplicationContext();\n        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), \"mmkv\");\n        channel.setMethodCallHandler(this);\n    }\n\n    @Override\n    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {\n        switch (call.method) {\n            case \"initializeMMKV\":\n                final String rootDir = call.argument(\"rootDir\");\n                final Object logLevelObj = call.argument(\"logLevel\");\n                final int logLevelInt = logLevelObj instanceof Integer ? (int) logLevelObj : 1 /* LevelInfo */;\n                final MMKVLogLevel logLevel = MMKVLogLevel.values()[logLevelInt];\n                final String ret = MMKV.initialize(context, rootDir, logLevel);\n                result.success(ret);\n                break;\n            case \"getSdkVersion\":\n                result.success(android.os.Build.VERSION.SDK_INT);\n                break;\n            default:\n                result.notImplemented();\n                break;\n        }\n    }\n\n    @Override\n    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {\n        channel.setMethodCallHandler(null);\n    }\n\n    private static boolean isLibraryLoaded = false;\n\n    private static void loadLibrary() {\n        if (!isLibraryLoaded) {\n            System.loadLibrary(\"mmkv\");\n            isLibraryLoaded = true;\n        }\n    }\n}"
  },
  {
    "path": "flutter/mmkv_android/lib/mmkv_android.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:ffi\";\nimport \"package:ffi/ffi.dart\";\nimport \"package:flutter/services.dart\";\nimport \"package:mmkv_platform_interface/mmkv_platform_interface.dart\";\n\n// final\nclass MMKVPlatformAndroid extends MMKVPluginPlatformFFI {\n  // A default real implementation of the platform interface would go here.\n  static void registerWith() {\n    MMKVPluginPlatform.instance = MMKVPlatformAndroid();\n  }\n\n  static final _nativeLib = DynamicLibrary.open(\"libmmkv.so\");\n\n  @override\n  DynamicLibrary nativeLib() {\n    return _nativeLib;\n  }\n\n  static const MethodChannel _channel = MethodChannel(\"mmkv\");\n\n  static final Pointer<Utf8> Function(Pointer<Utf8> rootDir, Pointer<Utf8> cacheDir, int sdkInt, int logLevel, Pointer<NativeFunction<LogCallbackWrap>>)\n      _mmkvInitialize = _nativeLib\n          .lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>, Int32, Int32, Pointer<NativeFunction<LogCallbackWrap>>)>>(\n              \"mmkvInitialize_v2\")\n          .asFunction();\n\n  @override\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\n    final rootDirPtr = _string2Pointer(rootDir);\n    final sdkInt = await _channel.invokeMethod(\"getSdkVersion\") ?? 0;\n    final cacheDir = await getTemporaryPath();\n    final cacheDirPtr = _string2Pointer(cacheDir);\n\n    final ret = _mmkvInitialize(rootDirPtr, cacheDirPtr, sdkInt, logLevel, logHandler ?? nullptr);\n\n    calloc.free(rootDirPtr);\n    calloc.free(cacheDirPtr);\n\n    return _pointer2String(ret) ?? rootDir;\n  }\n}\n\nPointer<Utf8> _string2Pointer(String? str) {\n  if (str != null) {\n    return str.toNativeUtf8();\n  }\n  return nullptr;\n}\n\nString? _pointer2String(Pointer<Utf8>? ptr) {\n  if (ptr != null && ptr != nullptr) {\n    return ptr.toDartString();\n  }\n  return null;\n}\n"
  },
  {
    "path": "flutter/mmkv_android/pubspec.yaml",
    "content": "name: mmkv_android\ndescription: Android platform implementation of MMKV.\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_android\nversion: 2.4.0\nhomepage: https://github.com/Tencent/mmkv\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  ffi: ^2.0.0\n  mmkv_platform_interface:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_platform_interface\n\ndev_dependencies:\n  test:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' and Android 'package' identifiers should not ordinarily\n  # be modified. They are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    implements: mmkv\n    platforms:\n      android:\n        package: com.tencent.mmkv\n        pluginClass: MMKVPlugin\n        dartPluginClass: MMKVPlatformAndroid\n"
  },
  {
    "path": "flutter/mmkv_ios/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.build/\n.buildlog/\n.history\n.svn/\n.swiftpm/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\n/pubspec.lock\n**/doc/api/\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\nbuild/\n"
  },
  {
    "path": "flutter/mmkv_ios/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"edada7c56edf4a183c1735310e123c7f923584f1\"\n  channel: \"stable\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: edada7c56edf4a183c1735310e123c7f923584f1\n      base_revision: edada7c56edf4a183c1735310e123c7f923584f1\n    - platform: ios\n      create_revision: edada7c56edf4a183c1735310e123c7f923584f1\n      base_revision: edada7c56edf4a183c1735310e123c7f923584f1\n    - platform: macos\n      create_revision: edada7c56edf4a183c1735310e123c7f923584f1\n      base_revision: edada7c56edf4a183c1735310e123c7f923584f1\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "flutter/mmkv_ios/CHANGELOG.md",
    "content": "# MMKV Platform iOS Change Log\n## v2.4.0 / 2026-03-18\nKeep up with native lib v2.4.0.\n\n## v2.3.0 / 2025-12-03\nKeep up with native lib v2.3.0.\n\n## v2.2.4 / 2025-09-25\n* Keep up with native lib v2.2.4.\n\n## v2.2.3 / 2025-08-20\n* Support macOS.\n* Keep up with native lib v2.2.3.\n\n## v2.2.2 / 2025-05-08\nKeep up with native lib v2.2.2.\n\n## v2.2.1 / 2025-04-25\nKeep up with native lib v2.2.1.\n\n## v2.2.0 / 2025-04-24\nKeep up with native lib v2.2.0.\n\n## v2.1.0 / 2025-02-18\nKeep up with native lib v2.1.0.\n\n## v2.0.0 / 2024-10-25\nBump to 2.0 to setup a breaking change version.\n\n## v1.0.8 / 2024-10-24\nRollback native lib to v1.3.x.\n\n## v1.0.7 / 2024-10-21\nFix a compile error on setting wrong public headers.\n\n## v1.0.6 / 2024-10-21\nKeep up with native lib v2.0.0.\n\n## v1.0.5 / 2024-07-26\nKeep up with native lib v1.3.9.\n\n## v1.0.4 / 2024-07-12\nLower Dart to 2.17 to avoid OHOS conflict.\n\n## v1.0.3 / 2024-07-08\nKeep up with native lib v1.3.7.\n\n## v1.0.2 / 2024-07-05\nKeep up with native lib v1.3.6.\n\n## v1.0.1 / 2024-04-24\nKeep up with native lib v1.3.5.\n\n## v1.0.0 / 2024-04-19\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_ios/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_ios/README.md",
    "content": "# mmkv\\_ios\n\nThe iOS implementation detail of [`MMKV`][1].\n\n## Usage\n\nNever use it alone. It's NOT a complete set of MMKV functionality. It just provides the FFI implementation needed by MMKV.\n\nThis package is [endorsed][2], which means you can simply use `mmkv`\nnormally. This package will be automatically included in your app when you do,\nso you do not need to add it to your `pubspec.yaml`.\n\nHowever, if you `import` this package to use any of its APIs directly, you\nshould add it to your `pubspec.yaml` as usual.\n\n[1]: https://pub.dev/packages/mmkv\n[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin\n"
  },
  {
    "path": "flutter/mmkv_ios/darwin/.gitignore",
    "content": ".idea/\n.vagrant/\n.sconsign.dblite\n.svn/\n\n.DS_Store\n*.swp\nprofile\n\nDerivedData/\nbuild/\nGeneratedPluginRegistrant.h\nGeneratedPluginRegistrant.m\n\n.generated/\n\n*.pbxuser\n*.mode1v3\n*.mode2v3\n*.perspectivev3\n\n!default.pbxuser\n!default.mode1v3\n!default.mode2v3\n!default.perspectivev3\n\nxcuserdata\n\n*.moved-aside\n\n*.pyc\n*sync/\nIcon?\n.tags*\n\n/Flutter/Generated.xcconfig\n/Flutter/flutter_export_environment.sh"
  },
  {
    "path": "flutter/mmkv_ios/darwin/Classes/MMKVPlugin.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__\n#import <Flutter/Flutter.h>\n#else\n#import <FlutterMacOS/FlutterMacOS.h>\n#endif\n\n@interface MMKVPlugin : NSObject <FlutterPlugin>\n@end\n"
  },
  {
    "path": "flutter/mmkv_ios/darwin/Classes/MMKVPlugin.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"MMKVPlugin.h\"\n#import <MMKV/MMKV.h>\n#import \"flutter-bridge.h\"\n\n@implementation MMKVPlugin\n\n+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {\n    FlutterMethodChannel *channel = [FlutterMethodChannel\n        methodChannelWithName:@\"mmkv\"\n              binaryMessenger:[registrar messenger]];\n    MMKVPlugin *instance = [[MMKVPlugin alloc] init];\n    [registrar addMethodCallDelegate:instance channel:channel];\n}\n\n- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {\n    result(FlutterMethodNotImplemented);\n}\n\n@end\n"
  },
  {
    "path": "flutter/mmkv_ios/darwin/Classes/flutter-bridge.h",
    "content": "//\n//  flutter-bridge.h\n//  mmkv_ios\n//\n//  Created by lingol on 2024/9/12.\n//\n\n#ifndef flutter_bridge_h\n#define flutter_bridge_h\n\n#import <MMKV/MMKV.h>\n\nenum MMKVErrorType : int {\n    MMKVCRCCheckFail = 0,\n    MMKVFileLength,\n};\n\nusing ErrorCallback_t = uint32_t (*)(const char *mmapID, uint32_t errorType);\nusing ContenctChangeCallback_t = void (*)(const char *mmapID);\nusing LogCallback_t = void (*)(uint32_t level, const char *file, int32_t line, const char *funcname, const char *message);\n\n@interface MyMMKVHandler : NSObject<MMKVHandler>\n\n@property (atomic, assign) ErrorCallback_t errorCallback;\n@property (atomic, assign) LogCallback_t logCallback;\n@property (atomic, assign) ContenctChangeCallback_t contenctChangeCallback;\n@property (atomic, assign) ContenctChangeCallback_t contentLoadedCallback;\n\n+(MyMMKVHandler *) getHandler;\n\n@end\n\n#endif /* flutter_bridge_h */\n"
  },
  {
    "path": "flutter/mmkv_ios/darwin/Classes/flutter-bridge.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"flutter-bridge.h\"\n#import <stdint.h>\n#import <string>\n\n#define MMKV_EXPORT extern \"C\" __attribute__((visibility(\"default\"))) __attribute__((used))\n#define MMKV_FUNC(func) mmkv_ ## func\n\nusing namespace std;\n\nMMKV_EXPORT void *mmkvInitialize(const char *rootDir, const char *groupDir, int32_t logLevel, LogCallback_t logCallback) {\n    auto handler = [MyMMKVHandler getHandler];\n    handler.logCallback = logCallback;\n\n    NSString *root = [NSString stringWithUTF8String:rootDir];\n    NSString *ret = nil;\n    if (groupDir) {\n        NSString *group = [NSString stringWithUTF8String:groupDir];\n        ret = [MMKV initializeMMKV:root groupDir:group logLevel:(MMKVLogLevel) logLevel handler:handler];\n    } else {\n        ret = [MMKV initializeMMKV:root logLevel:(MMKVLogLevel) logLevel handler:handler];\n    }\n    return (void*) ret.UTF8String;\n}\n\nMMKV_EXPORT void *getMMKVWithID(const char *mmapID, uint32_t mode, const char *cryptKey, const char *rootPath,\n                                size_t expectedCapacity, bool isNameSpace, bool aes256, int32_t enableKeyExpire,\n                                int32_t expiredInSeconds, bool enableCompareBeforeSet, int32_t recover,\n                                uint32_t itemSizeLimit) {\n    MMKV *kv = nil;\n    if (!mmapID) {\n        return (__bridge void *) kv;\n    }\n    NSString *str = [NSString stringWithUTF8String:mmapID];\n\n    auto config = MMKVConfigDefault();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0) ? @YES : @NO;\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    bool done = false;\n    if (cryptKey) {\n        auto crypt = [NSString stringWithUTF8String:cryptKey];\n        if (crypt.length > 0) {\n            auto cryptKeyData = [crypt dataUsingEncoding:NSUTF8StringEncoding];\n            config.cryptKey = cryptKeyData;\n            if (rootPath) {\n                auto path = [NSString stringWithUTF8String:rootPath];\n                if (isNameSpace) {\n                    auto ns = [MMKV nameSpace:path];\n                    kv = [ns mmkvWithID:str config:config];\n                } else {\n                    config.rootPath = path;\n                    kv = [MMKV mmkvWithID:str config:config];\n                }\n            } else {\n                kv = [MMKV mmkvWithID:str config:config];\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath) {\n            auto path = [NSString stringWithUTF8String:rootPath];\n            if (isNameSpace) {\n                auto ns = [MMKV nameSpace:path];\n                kv = [ns mmkvWithID:str config:config];\n            } else {\n                config.rootPath = path;\n                kv = [MMKV mmkvWithID:str config:config];\n            }\n        } else {\n            kv = [MMKV mmkvWithID:str config:config];\n        }\n    }\n\n    return (__bridge void *) kv;\n}\n\nMMKV_EXPORT int64_t getDefaultMMKV(int /*mode*/, const char *cryptKey, bool aes256, size_t expectedCapacity,\n                                   int32_t enableKeyExpire, int32_t expiredInSeconds, bool enableCompareBeforeSet,\n                                   int32_t recover, uint32_t itemSizeLimit) {\n    MMKV *kv = nil;\n\n    auto config = MMKVConfigDefault();\n    // config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0) ? @YES : @NO;\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    if (cryptKey) {\n        auto crypt = [NSString stringWithUTF8String:cryptKey];\n        auto cryptKeyData = [crypt dataUsingEncoding:NSUTF8StringEncoding];\n        if (cryptKeyData.length > 0) {\n            config.cryptKey = cryptKeyData;\n            kv = [MMKV defaultMMKVWithConfig:config];\n        }\n    }\n    if (!kv) {\n        kv = [MMKV defaultMMKVWithConfig:config];\n    }\n\n    return (int64_t) kv;\n}\n\nMMKV_EXPORT const char *MMKV_FUNC(mmapID)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [[kv mmapID] UTF8String];\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeBool)(const void *handle, const char *oKey, bool value) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setBool:value forKey:key];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeBool_v2)(const void *handle, const char *oKey, bool value, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setBool:value forKey:key expireDuration:expiration];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(decodeBool)(const void *handle, const char *oKey, bool defaultValue) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv getBoolForKey:key defaultValue:defaultValue];\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeInt32)(const void *handle, const char *oKey, int32_t value) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setInt32:value forKey:key];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeInt32_v2)(const void *handle, const char *oKey, int32_t value, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setInt32:value forKey:key expireDuration:expiration];\n    }\n    return false;\n}\n\nMMKV_EXPORT int32_t MMKV_FUNC(decodeInt32)(const void *handle, const char *oKey, int32_t defaultValue) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv getInt32ForKey:key defaultValue:defaultValue];\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeInt64)(const void *handle, const char *oKey, int64_t value) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setInt64:value forKey:key];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeInt64_v2)(const void *handle, const char *oKey, int64_t value, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setInt64:value forKey:key expireDuration:expiration];\n    }\n    return false;\n}\n\nMMKV_EXPORT int64_t MMKV_FUNC(decodeInt64)(const void *handle, const char *oKey, int64_t defaultValue) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv getInt64ForKey:key defaultValue:defaultValue];\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeDouble)(const void *handle, const char *oKey, double value) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setDouble:value forKey:key];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeDouble_v2)(const void *handle, const char *oKey, double value, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv setDouble:value forKey:key expireDuration:expiration];\n    }\n    return false;\n}\n\nMMKV_EXPORT double MMKV_FUNC(decodeDouble)(const void *handle, const char *oKey, double defaultValue) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv getDoubleForKey:key defaultValue:defaultValue];\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeBytes)(const void *handle, const char *oKey, void *oValue, uint64_t length) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        if (oValue) {\n            auto value = [NSData dataWithBytesNoCopy:oValue length:static_cast<NSUInteger>(length) freeWhenDone:NO];\n            return [kv setData:value forKey:key];\n        } else {\n            [kv removeValueForKey:key];\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(encodeBytes_v2)(const void *handle, const char *oKey, void *oValue, uint64_t length, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        if (oValue) {\n            auto value = [NSData dataWithBytesNoCopy:oValue length:static_cast<NSUInteger>(length) freeWhenDone:NO];\n            return [kv setData:value forKey:key expireDuration:expiration];\n        } else {\n            [kv removeValueForKey:key];\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *MMKV_FUNC(decodeBytes)(const void *handle, const char *oKey, uint64_t *lengthPtr) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        auto value = [kv getDataForKey:key];\n        if (value) {\n            *lengthPtr = value.length;\n            if (value.bytes) {\n                return (void *) value.bytes;\n            } else {\n                return (__bridge void *) value;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(reKey)(const void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        if (oKey && length > 0) {\n            NSData *key = [NSData dataWithBytesNoCopy:oKey length:length freeWhenDone:NO];\n            return [kv reKey:key aes256:aes256];\n        } else {\n            return [kv reKey:nil aes256:aes256];\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *MMKV_FUNC(cryptKey)(const void *handle, uint64_t *lengthPtr) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && lengthPtr) {\n        auto cryptKey = [kv cryptKey];\n        if (cryptKey.length > 0) {\n            auto ptr = malloc(cryptKey.length);\n            if (ptr) {\n                memcpy(ptr, cryptKey.bytes, cryptKey.length);\n                *lengthPtr = cryptKey.length;\n                return ptr;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT void MMKV_FUNC(checkReSetCryptKey)(const void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        if (oKey && length > 0) {\n            NSData *key = [NSData dataWithBytesNoCopy:oKey length:length freeWhenDone:NO];\n            [kv checkReSetCryptKey:key aes256:aes256];\n        } else {\n            [kv checkReSetCryptKey:nil aes256:aes256];\n        }\n    }\n}\n\nMMKV_EXPORT uint32_t MMKV_FUNC(valueSize)(const void *handle, char *oKey, bool actualSize) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        auto ret = [kv getValueSizeForKey:key actualSize:actualSize];\n        return static_cast<uint32_t>(ret);\n    }\n    return 0;\n}\n\nMMKV_EXPORT int32_t MMKV_FUNC(writeValueToNB)(const void *handle, char *oKey, void *pointer, uint32_t size) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv writeValueForKey:key toBuffer:[NSMutableData dataWithBytesNoCopy:pointer length:size freeWhenDone:NO]];\n    }\n    return -1;\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(allKeys)(const void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        auto keys = filterExpire ? [kv allNonExpiredKeys] : [kv allKeys];\n        if (keys.count > 0) {\n            auto keyArray = (char **) malloc(keys.count * sizeof(void *));\n            auto sizeArray = (uint32_t *) malloc(keys.count * sizeof(uint32_t *));\n            if (!keyArray || !sizeArray) {\n                free(keyArray);\n                free(sizeArray);\n                return 0;\n            }\n            *keyArrayPtr = keyArray;\n            *sizeArrayPtr = sizeArray;\n\n            for (size_t index = 0; index < keys.count; index++) {\n                NSString *key = keys[index];\n                auto keyData = [key dataUsingEncoding:NSUTF8StringEncoding];\n                sizeArray[index] = static_cast<uint32_t>(keyData.length);\n                keyArray[index] = (char *) keyData.bytes;\n            }\n        }\n        return keys.count;\n    }\n    return 0;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(containsKey)(const void *handle, char *oKey) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        return [kv containsKey:key];\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(count)(const void *handle, bool filterExpire) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return filterExpire ? [kv countNonExpiredKeys] : [kv count];\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(totalSize)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv totalSize];\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(actualSize)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv actualSize];\n    }\n    return 0;\n}\n\nMMKV_EXPORT void MMKV_FUNC(removeValueForKey)(const void *handle, char *oKey) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && oKey) {\n        auto key = [NSString stringWithUTF8String:oKey];\n        [kv removeValueForKey:key];\n    }\n}\n\nMMKV_EXPORT void MMKV_FUNC(removeValuesForKeys)(const void *handle, char **keyArray, uint32_t *sizeArray, uint64_t count) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv && keyArray && sizeArray && count > 0) {\n        NSMutableArray *arrKeys = [NSMutableArray arrayWithCapacity:count];\n        for (uint64_t index = 0; index < count; index++) {\n            if (sizeArray[index] > 0 && keyArray[index]) {\n                auto keyData = [NSData dataWithBytesNoCopy:keyArray[index] length:sizeArray[index] freeWhenDone:NO];\n                NSString *key = [[NSString alloc] initWithData:keyData encoding:NSUTF8StringEncoding];\n                if (key.length > 0) {\n                    [arrKeys addObject:key];\n                }\n            }\n        }\n        if (arrKeys.count > 0) {\n            [kv removeValuesForKeys:arrKeys];\n        }\n    }\n}\n\nMMKV_EXPORT void MMKV_FUNC(clearAll)(const void *handle, bool keepSpace) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        if (keepSpace) {\n            [kv clearAllWithKeepingSpace];\n        } else {\n            [kv clearAll];\n        }\n    }\n}\n\nMMKV_EXPORT void mmkvSync(const void *handle, bool sync) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        if (sync) {\n            [kv sync];\n        } else {\n            [kv async];\n        }\n    }\n}\n\nMMKV_EXPORT void MMKV_FUNC(clearMemoryCache)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        [kv clearMemoryCache];\n    }\n}\n\nMMKV_EXPORT int32_t MMKV_FUNC(pageSize)() {\n    return static_cast<int32_t>([MMKV pageSize]);\n}\n\nMMKV_EXPORT const char *MMKV_FUNC(version)() {\n    return [MMKV version].UTF8String;\n}\n\nMMKV_EXPORT void MMKV_FUNC(trim)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        [kv trim];\n    }\n}\n\nMMKV_EXPORT void mmkvClose(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        [kv close];\n    }\n}\n\nMMKV_EXPORT void mmkvMemcpy(void *dst, const void *src, uint64_t size) {\n    memcpy(dst, src, size);\n}\n\nMMKV_EXPORT bool MMKV_FUNC(backupOne)(const char *mmapID, const char *dstDir, const char *rootPath) {\n    auto strID = [NSString stringWithUTF8String:mmapID];\n    auto strDstDir = [NSString stringWithUTF8String:dstDir];\n\n    if (rootPath) {\n        auto root = [NSString stringWithUTF8String:rootPath];\n        if (root.length > 0) {\n            return [MMKV backupOneMMKV:strID rootPath:root toDirectory:strDstDir];\n        }\n    }\n    return [MMKV backupOneMMKV:strID rootPath:nil toDirectory:strDstDir];\n}\n\nMMKV_EXPORT bool MMKV_FUNC(restoreOne)(const char *mmapID, const char *srcDir, const char *rootPath) {\n    auto strID = [NSString stringWithUTF8String:mmapID];\n    auto strSrcDir = [NSString stringWithUTF8String:srcDir];\n    if (rootPath) {\n        auto root = [NSString stringWithUTF8String:rootPath];\n        if (root.length > 0) {\n            return [MMKV restoreOneMMKV:strID rootPath:root fromDirectory:strSrcDir];\n        }\n    }\n    return [MMKV restoreOneMMKV:strID rootPath:nil fromDirectory:strSrcDir];\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(backupAll)(const char *dstDir/*, const char *rootPath*/) {\n    auto strDstDir = [NSString stringWithUTF8String:dstDir];\n    return [MMKV backupAll:nil toDirectory:strDstDir];\n}\n\nMMKV_EXPORT uint64_t MMKV_FUNC(restoreAll)(const char *srcDir/*, const char *rootPath*/) {\n    auto strSrcDir = [NSString stringWithUTF8String:srcDir];\n    return [MMKV restoreAll:nil fromDirectory:strSrcDir];\n}\n\nMMKV_EXPORT bool MMKV_FUNC(enableAutoExpire)(const void *handle, uint32_t expiration) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv enableAutoKeyExpire:expiration];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(disableAutoExpire)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv disableAutoKeyExpire];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(enableCompareBeforeSet)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv enableCompareBeforeSet];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(disableCompareBeforeSet)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv disableCompareBeforeSet];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(isFileValid)(const char *mmapID, const char *rootDir) {\n    auto strID = [NSString stringWithUTF8String:mmapID];\n\n    if (rootDir) {\n        auto root = [NSString stringWithUTF8String:rootDir];\n        if (root.length > 0) {\n            return [MMKV isFileValid:strID rootPath:root];\n        }\n    }\n    return [MMKV isFileValid:strID rootPath:nil];\n}\n\nMMKV_EXPORT bool MMKV_FUNC(removeStorage)(const char *mmapID, const char *rootDir) {\n    auto strID = [NSString stringWithUTF8String:mmapID];\n\n    if (rootDir) {\n        auto root = [NSString stringWithUTF8String:rootDir];\n        if (root.length > 0) {\n            return [MMKV removeStorage:strID rootPath:root];\n        }\n    }\n    return [MMKV removeStorage:strID rootPath:nil];\n}\n\nMMKV_EXPORT bool MMKV_FUNC(isMultiProcess)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv isMultiProcess];\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(isReadOnly)(const void *handle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    if (kv) {\n        return [kv isReadOnly];\n    }\n    return false;\n}\n\n#pragma mark - callback\n\n@implementation MyMMKVHandler\n\n+ (MyMMKVHandler *)getHandler {\n    static MyMMKVHandler *g_mmkvHandler = nullptr;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        g_mmkvHandler = [[MyMMKVHandler alloc] init];\n    });\n    return g_mmkvHandler;\n}\n\n- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID {\n    if (self.errorCallback) {\n        return (MMKVRecoverStrategic)self.errorCallback(mmapID.UTF8String, MMKVCRCCheckFail);\n    }\n    return MMKVOnErrorDiscard;\n}\n\n// by default MMKV will discard all data on file length mismatch\n// return `MMKVOnErrorRecover` to recover any data on the file\n- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID {\n    if (self.errorCallback) {\n        return (MMKVRecoverStrategic)self.errorCallback(mmapID.UTF8String, MMKVFileLength);\n    }\n    return MMKVOnErrorDiscard;\n}\n\n// by default MMKV will print log using NSLog\n// implement this method to redirect MMKV's log\n- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message {\n    if (self.logCallback) {\n        self.logCallback((uint32_t)level, file, line, funcname, message.UTF8String);\n    } else {\n        auto LogLevelDesc = [](MMKVLogLevel level) {\n            switch (level) {\n                case MMKVLogDebug:\n                    return \"D\";\n                case MMKVLogInfo:\n                    return \"I\";\n                case MMKVLogWarning:\n                    return \"W\";\n                case MMKVLogError:\n                    return \"E\";\n                default:\n                    return \"N\";\n            }\n        };\n        NSLog(@\"mmkv:[%s] <%s:%d::%s> %@\\n\", LogLevelDesc(level), file, line, funcname, message);\n    }\n}\n\n- (void)onMMKVContentChange:(NSString *)mmapID {\n    if (self.contenctChangeCallback) {\n        self.contenctChangeCallback(mmapID.UTF8String);\n    }\n}\n\n- (void)onMMKVContentLoadSuccessfully:(NSString *)mmapID {\n    if (self.contentLoadedCallback) {\n        self.contentLoadedCallback(mmapID.UTF8String);\n    }\n}\n\n@end\n\nMMKV_EXPORT void MMKV_FUNC(registerErrorHandler)(ErrorCallback_t callback) {\n    [MyMMKVHandler getHandler].errorCallback = callback;\n}\n\nMMKV_EXPORT void MMKV_FUNC(registerContentChangeNotify)(ContenctChangeCallback_t callback) {\n    [MyMMKVHandler getHandler].contenctChangeCallback = callback;\n}\n\nMMKV_EXPORT void MMKV_FUNC(registerContentLoadedNotify)(ContenctChangeCallback_t callback) {\n    [MyMMKVHandler getHandler].contentLoadedCallback = callback;\n}\n\nMMKV_EXPORT void MMKV_FUNC(checkContentChanged)(const void *handle) {\n    MMKV *kv = (__bridge MMKV*)handle;\n    if (kv) {\n        [kv checkContentChanged];\n    }\n}\n\nMMKV_EXPORT bool MMKV_FUNC(getNameSpace)(const char *rootPath) {\n    if (rootPath) {\n        auto root = [NSString stringWithUTF8String:rootPath];\n        if (root.length > 0) {\n            [MMKV nameSpace:root];\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool MMKV_FUNC(checkExist)(const char *mmapID, const char *rootDir) {\n    auto strID = [NSString stringWithUTF8String:mmapID];\n\n    if (rootDir) {\n        auto root = [NSString stringWithUTF8String:rootDir];\n        if (root.length > 0) {\n            return [MMKV checkExist:strID rootPath:root];\n        }\n    }\n    return [MMKV checkExist:strID rootPath:nil];\n}\n\nMMKV_EXPORT const char *MMKV_FUNC(groupPath)() {\n    auto groupPath = [MMKV mmkvGroupPath];\n    return groupPath ? [groupPath UTF8String] : nullptr;\n}\n\nMMKV_EXPORT size_t MMKV_FUNC(importFrom)(const void *handle, const void *srcHandle) {\n    MMKV *kv = (__bridge MMKV *) handle;\n    MMKV *kvSrc = (__bridge MMKV *) srcHandle;\n    if (kv && kvSrc) {\n        return [kv importFrom:kvSrc];\n    }\n    return 0;\n}\n"
  },
  {
    "path": "flutter/mmkv_ios/darwin/mmkv_ios.podspec",
    "content": "#\n# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.\n# Run `pod lib lint mmkv.podspec' to validate before publishing.\n#\n\nPod::Spec.new do |s|\n  s.name             = 'mmkv_ios'\n  s.version          = '2.4.0'\n  s.summary          = 'MMKV is a cross-platform key-value storage framework developed by WeChat.'\n  s.description      = <<-DESC\n  The MMKV, for Flutter.\n  MMKV is an efficient, complete, easy-to-use mobile key-value storage framework used in the WeChat application.\n  It can be a replacement for NSUserDefaults & SharedPreferences.\n                       DESC\n  s.homepage     = \"https://github.com/Tencent/MMKV\"\n  s.license          = { :file => '../LICENSE' }\n  s.author           = { 'Tencent' => 'guoling@tencent.com' }\n  s.source           = { :path => '.' }\n  s.source_files = 'Classes/**/*'\n  s.public_header_files = 'Classes/**/MMKVPlugin.h'\n  s.ios.dependency 'Flutter'\n  s.osx.dependency 'FlutterMacOS'\n  s.ios.deployment_target = '13.0'\n  s.osx.deployment_target = '10.15'\n  s.dependency 'MMKV', '>= 2.4.0', '< 2.5'\n  s.osx.framework = 'FlutterMacOS'\n\n# Flutter.framework does not contain a i386 slice.\n  s.pod_target_xcconfig = {\n    'DEFINES_MODULE' => 'YES',\n    'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',\n#\"GCC_PREPROCESSOR_DEFINITIONS\" => \"FORCE_POSIX\",\n  }\n\nend\n"
  },
  {
    "path": "flutter/mmkv_ios/lib/mmkv_ios.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport 'dart:ffi';\nimport 'package:ffi/ffi.dart';\nimport 'package:flutter/services.dart';\nimport 'package:mmkv_platform_interface/mmkv_platform_interface.dart';\n\n// final\nclass MMKVPlatformIOS extends MMKVPluginPlatformFFI {\n  static void registerWith() {\n    MMKVPluginPlatform.instance = MMKVPlatformIOS();\n  }\n\n  @override\n  String nativeFuncName(String name) {\n    return \"mmkv_$name\";\n  }\n\n  static late final _nativeLib = DynamicLibrary.process();\n\n  @override\n  DynamicLibrary nativeLib() {\n    return _nativeLib;\n  }\n\n  static const MethodChannel _channel = MethodChannel(\"mmkv\");\n\n  static final Pointer<Utf8> Function(Pointer<Utf8> rootDir, Pointer<Utf8> groupDir, int logLevel, Pointer<NativeFunction<LogCallbackWrap>>)\n      _mmkvInitialize = _nativeLib\n          .lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>, Int32, Pointer<NativeFunction<LogCallbackWrap>>)>>(\n              \"mmkvInitialize\")\n          .asFunction();\n\n  @override\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\n    final rootDirPtr = _string2Pointer(rootDir);\n    final groupDirPtr = groupDir != null ? _string2Pointer(groupDir) : nullptr;\n\n    final ret = _mmkvInitialize(rootDirPtr, groupDirPtr, logLevel, logHandler ?? nullptr);\n\n    calloc.free(rootDirPtr);\n    calloc.free(groupDirPtr);\n\n    return _pointer2String(ret) ?? rootDir;\n  }\n}\n\nPointer<Utf8> _string2Pointer(String? str) {\n  if (str != null) {\n    return str.toNativeUtf8();\n  }\n  return nullptr;\n}\n\nString? _pointer2String(Pointer<Utf8>? ptr) {\n  if (ptr != null && ptr != nullptr) {\n    return ptr.toDartString();\n  }\n  return null;\n}\n"
  },
  {
    "path": "flutter/mmkv_ios/pubspec.yaml",
    "content": "name: mmkv_ios\ndescription: iOS platform implementation of MMKV.\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_ios\nversion: 2.4.0\nhomepage: https://github.com/Tencent/mmkv\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  ffi: ^2.0.0\n  mmkv_platform_interface:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_platform_interface\n\ndev_dependencies:\n  test:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' and Android 'package' identifiers should not ordinarily\n  # be modified. They are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    implements: mmkv\n    platforms:\n      ios:\n        pluginClass: MMKVPlugin\n        dartPluginClass: MMKVPlatformIOS\n        sharedDarwinSource: true\n      macos:\n        pluginClass: MMKVPlugin\n        dartPluginClass: MMKVPlatformIOS\n        sharedDarwinSource: true\n"
  },
  {
    "path": "flutter/mmkv_linux/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.build/\n.buildlog/\n.history\n.svn/\n.swiftpm/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\n/pubspec.lock\n**/doc/api/\n.dart_tool/\n.flutter-plugins-dependencies\n/build/\n/coverage/\n"
  },
  {
    "path": "flutter/mmkv_linux/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"20f82749394e68bcfbbeee96bad384abaae09c13\"\n  channel: \"stable\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13\n      base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13\n    - platform: linux\n      create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13\n      base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "flutter/mmkv_linux/CHANGELOG.md",
    "content": "# MMKV Platform Linux Change Log\n## v2.4.0 / 2026-03-18\nKeep up with native lib v2.4.0.\n\n## v2.3.0 / 2025-12-03\nKeep up with native lib v2.3.0.\n\n## v2.2.4 / 2025-09-25\n* Keep up with native lib v2.2.4.\n\n## v2.2.3 / 2025-08-20\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_linux/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_linux/README.md",
    "content": "# mmkv\\_linux\n\nThe Linux implementation detail of [`MMKV`][1].\n\n## Usage\n\nNever use it alone. It's NOT a complete set of MMKV functionality. It just provides the FFI implementation needed by MMKV.\n\nThis package is [endorsed][2], which means you can simply use `mmkv`\nnormally. This package will be automatically included in your app when you do,\nso you do not need to add it to your `pubspec.yaml`.\n\nHowever, if you `import` this package to use any of its APIs directly, you\nshould add it to your `pubspec.yaml` as usual.\n\n[1]: https://pub.dev/packages/mmkv\n[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin\n"
  },
  {
    "path": "flutter/mmkv_linux/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "flutter/mmkv_linux/lib/mmkv_linux.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport 'dart:ffi';\nimport 'package:ffi/ffi.dart';\n// import 'package:flutter/services.dart';\nimport 'package:mmkv_platform_interface/mmkv_platform_interface.dart';\n\n// final\nclass MMKVPlatformLinux extends MMKVPluginPlatformFFI {\n  static void registerWith() {\n    MMKVPluginPlatform.instance = MMKVPlatformLinux();\n  }\n\n  static final _nativeLib = DynamicLibrary.open(\"libmmkv_linux_plugin.so\");\n\n  @override\n  DynamicLibrary nativeLib() {\n    return _nativeLib;\n  }\n\n  static final Pointer<Utf8> Function(Pointer<Utf8> rootDir, int logLevel, Pointer<NativeFunction<LogCallbackWrap>>)\n      _mmkvInitialize = _nativeLib\n          .lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<NativeFunction<LogCallbackWrap>>)>>(\n              \"mmkvInitialize\")\n          .asFunction();\n\n  @override\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\n    final rootDirPtr = _string2Pointer(rootDir);\n\n    final ret = _mmkvInitialize(rootDirPtr, logLevel, logHandler ?? nullptr);\n\n    calloc.free(rootDirPtr);\n\n    return _pointer2String(ret) ?? rootDir;\n  }\n}\n\nPointer<Utf8> _string2Pointer(String? str) {\n  if (str != null) {\n    return str.toNativeUtf8();\n  }\n  return nullptr;\n}\n\nString? _pointer2String(Pointer<Utf8>? ptr) {\n  if (ptr != null && ptr != nullptr) {\n    return ptr.toDartString();\n  }\n  return null;\n}\n"
  },
  {
    "path": "flutter/mmkv_linux/linux/CMakeLists.txt",
    "content": "# The Flutter tooling requires that developers have CMake 3.10 or later\n# installed. You should not increase this version, as doing so will cause\n# the plugin to fail to compile for some customers of the plugin.\ncmake_minimum_required(VERSION 3.10)\n\n# Project-level configuration.\nset(PROJECT_NAME \"mmkv_linux\")\nproject(${PROJECT_NAME} LANGUAGES CXX)\n\n# Set the version of MMKV to use. This should match the version used in the\n# mmkv_flutter plugin.\nset(MMKV_VERSION \"v2.4.0\")\n\ninclude(FetchContent)\nFetchContent_Declare(mmkv\n  GIT_REPOSITORY \"https://github.com/Tencent/MMKV.git\"\n  GIT_TAG ${MMKV_VERSION}\n  SOURCE_SUBDIR \"Core\"\n)\nFetchContent_MakeAvailable(mmkv)\n\n# This value is used when generating builds using this plugin, so it must\n# not be changed.\nset(PLUGIN_NAME \"${PROJECT_NAME}_plugin\")\n\n# Define the plugin library target. Its name must not be changed (see comment\n# on PLUGIN_NAME above).\nadd_library(${PLUGIN_NAME} SHARED\n  \"mmkv_linux_plugin.cc\"\n  \"flutter-bridge.cpp\"\n)\n\n# Apply a standard set of build settings that are configured in the\n# application-level CMakeLists.txt. This can be removed for plugins that want\n# full control over build settings.\n# apply_standard_settings(${PLUGIN_NAME})\n\n# Symbols are hidden by default to reduce the chance of accidental conflicts\n# between plugins. This should not be removed; any symbols that should be\n# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.\nset_target_properties(${PLUGIN_NAME} PROPERTIES\n  CXX_STANDARD 20\n  CXX_EXTENSIONS OFF\n  POSITION_INDEPENDENT_CODE ON\n  CXX_VISIBILITY_PRESET hidden\n)\ntarget_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)\n\n# Source include directories and library dependencies. Add any plugin-specific\n# dependencies here.\ntarget_include_directories(${PLUGIN_NAME} INTERFACE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE flutter)\n# target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE core)\n\n# List of absolute paths to libraries that should be bundled with the plugin.\n# This list could contain prebuilt libraries, or libraries created by an\n# external build triggered from this build file.\nset(mmkv_linux_bundled_libraries\n  \"\"\n  PARENT_SCOPE\n)\n"
  },
  {
    "path": "flutter/mmkv_linux/linux/flutter-bridge.cpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <MMKV/MMKVPredef.h>\n\n#ifndef MMKV_DISABLE_FLUTTER\n\n#    include <MMKV/MMKV.h>\n// #    include <MMKV/MMKVLog.h>\n#    include <cstdint>\n#    include <string>\n\nusing namespace mmkv;\nusing namespace std;\n\n#    ifdef MMKV_EXPORT\n#       undef MMKV_EXPORT\n#    endif\n#    define MMKV_EXPORT extern \"C\" __attribute__((visibility(\"default\"))) __attribute__((used))\n\nusing LogCallback_t = void (*)(uint32_t level, const char *file, int32_t line, const char *funcname, const char *message);\nusing ErrorCallback_t = int (*)(const char *mmapID, int32_t errorType);\nusing ContenctChangeCallback_t = void (*)(const char *mmapID);\n\nclass FlutterMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    LogCallback_t logCallback = nullptr;\n    ErrorCallback_t errorCallback = nullptr;\n    ContenctChangeCallback_t contentChangeCallback = nullptr;\n    ContenctChangeCallback_t contentLoadedCallback = nullptr;\n\n    void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, const std::string &message) override {\n        if (logCallback) {\n            logCallback(level, file, line, function, message.c_str());\n        }\n    }\n\n    MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVCRCCheckFail);\n        }\n        return OnErrorDiscard;\n    }\n\n    MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if (errorCallback) {\n            return (MMKVRecoverStrategic) errorCallback(mmapID.c_str(), MMKVFileLength);\n        }\n        return OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if (contentChangeCallback) {\n            contentChangeCallback(mmapID.c_str());\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if (contentLoadedCallback) {\n            contentLoadedCallback(mmapID.c_str());\n        }\n    }\n};\n\nstatic FlutterMMKVHandler g_flutterHandler;\n\nMMKV_EXPORT void *mmkvInitialize(const char *rootDir, int32_t logLevel, LogCallback_t callback) {\n    if (!rootDir) {\n        return nullptr;\n    }\n    auto root = string(rootDir);\n\n    if (callback) {\n        g_flutterHandler.logCallback = callback;\n        MMKV::initializeMMKV(root, (MMKVLogLevel) logLevel, &g_flutterHandler);\n    } else {\n        MMKV::initializeMMKV(root, (MMKVLogLevel) logLevel);\n    }\n    return (void *) MMKV::getRootDir().c_str();\n}\n\nMMKV_EXPORT void *getMMKVWithID(const char *mmapID, int32_t mode, const char *cryptKey, const char *rootPath,\n                                uint64_t expectedCapacity, bool fromNameSpace, bool aes256, int32_t enableKeyExpire,\n                                int32_t expiredInSeconds, bool enableCompareBeforeSet, int32_t recover,\n                                uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n    if (!mmapID) {\n        return kv;\n    }\n    auto str = string(mmapID);\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    bool done = false;\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            if (rootPath) {\n                auto path = string(rootPath);\n                if (fromNameSpace) {\n                    auto ns = MMKV::nameSpace(path);\n                    config.rootPath = &ns.getRootDir();\n                    kv = ns.mmkvWithID(str, config);\n                } else {\n                    config.rootPath = &path;\n                    kv = MMKV::mmkvWithID(str, config);\n                }\n            } else {\n                kv = MMKV::mmkvWithID(str, config);\n            }\n            done = true;\n        }\n    }\n    if (!done) {\n        if (rootPath) {\n            auto path = string(rootPath);\n            if (fromNameSpace) {\n                auto ns = MMKV::nameSpace(path);\n                config.rootPath = &ns.getRootDir();\n                kv = ns.mmkvWithID(str, config);\n            } else {\n                config.rootPath = &path;\n                kv = MMKV::mmkvWithID(str, config);\n            }\n        } else {\n            kv = MMKV::mmkvWithID(str, config);\n        }\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT void *getDefaultMMKV(int32_t mode, const char *cryptKey, bool aes256, size_t expectedCapacity,\n                                 int32_t enableKeyExpire, int32_t expiredInSeconds, bool enableCompareBeforeSet,\n                                 int32_t recover, uint32_t itemSizeLimit) {\n    MMKV *kv = nullptr;\n\n    auto config = MMKVConfig();\n    config.mode = (MMKVMode) mode;\n    config.aes256 = aes256;\n    config.expectedCapacity = expectedCapacity;\n    if (enableKeyExpire >= 0) {\n        config.enableKeyExpire = (enableKeyExpire != 0);\n    }\n    config.expiredInSeconds = expiredInSeconds;\n    config.enableCompareBeforeSet = enableCompareBeforeSet;\n    if (recover >= 0) {\n        config.recover = static_cast<MMKVRecoverStrategic>(recover);\n    }\n    config.itemSizeLimit = itemSizeLimit;\n\n    if (cryptKey) {\n        string crypt = cryptKey;\n        if (crypt.length() > 0) {\n            config.cryptKey = &crypt;\n            kv = MMKV::defaultMMKV(config);\n        }\n    }\n    if (!kv) {\n        kv = MMKV::defaultMMKV(config);\n    }\n\n    return kv;\n}\n\nMMKV_EXPORT const char *mmapID(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->mmapID().c_str();\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT bool encodeBool(void *handle, const char *oKey, bool value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBool_v2(void *handle, const char *oKey, bool value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((bool) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool decodeBool(void *handle, const char *oKey, bool defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getBool(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt32(void *handle, const char *oKey, int32_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt32_v2(void *handle, const char *oKey, int32_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int32_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int32_t decodeInt32(void *handle, const char *oKey, int32_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt32(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeInt64(void *handle, const char *oKey, int64_t value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeInt64_v2(void *handle, const char *oKey, int64_t value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((int64_t) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT int64_t decodeInt64(void *handle, const char *oKey, int64_t defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getInt64(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeDouble(void *handle, const char *oKey, double value) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeDouble_v2(void *handle, const char *oKey, double value, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->set((double) value, key, expiration);\n    }\n    return false;\n}\n\nMMKV_EXPORT double decodeDouble(void *handle, const char *oKey, double defaultValue) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        return kv->getDouble(key, defaultValue);\n    }\n    return defaultValue;\n}\n\nMMKV_EXPORT bool encodeBytes(void *handle, const char *oKey, void *oValue, uint64_t length) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool encodeBytes_v2(void *handle, const char *oKey, void *oValue, uint64_t length, uint32_t expiration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        if (oValue) {\n            auto value = MMBuffer(oValue, static_cast<size_t>(length), MMBufferNoCopy);\n            return kv->set(value, key, expiration);\n        } else {\n            kv->removeValueForKey(key);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *decodeBytes(void *handle, const char *oKey, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        auto key = string(oKey);\n        mmkv::MMBuffer value;\n        auto hasValue = kv->getBytes(key, value);\n        if (hasValue) {\n            if (value.length() > 0) {\n                if (value.isStoredOnStack()) {\n                    auto result = malloc(value.length());\n                    if (result) {\n                        memcpy(result, value.getPtr(), value.length());\n                        *lengthPtr = value.length();\n                    }\n                    return result;\n                }\n                void *result = value.getPtr();\n                *lengthPtr = value.length();\n                value.detach();\n                return result;\n            }\n            *lengthPtr = 0;\n            // this ptr is intended for checking existence of the value\n            // don't free this ptr\n            return value.getPtr();\n        }\n    }\n    return nullptr;\n}\n\n#    ifndef MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT bool reKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            return kv->reKey(key, aes256);\n        } else {\n            return kv->reKey(string(), aes256);\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT void *cryptKey(void *handle, uint64_t *lengthPtr) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && lengthPtr) {\n        auto cryptKey = kv->cryptKey();\n        if (cryptKey.length() > 0) {\n            auto ptr = malloc(cryptKey.length());\n            if (ptr) {\n                memcpy(ptr, cryptKey.data(), cryptKey.length());\n                *lengthPtr = cryptKey.length();\n                return ptr;\n            }\n        }\n    }\n    return nullptr;\n}\n\nMMKV_EXPORT void checkReSetCryptKey(void *handle, char *oKey, uint64_t length, bool aes256) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        if (oKey && length > 0) {\n            string key(oKey, length);\n            kv->checkReSetCryptKey(&key, aes256);\n        } else {\n            kv->checkReSetCryptKey(nullptr, aes256);\n        }\n    }\n}\n\n#    endif // MMKV_DISABLE_CRYPT\n\nMMKV_EXPORT uint32_t valueSize(void *handle, char *oKey, bool actualSize) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        auto ret = kv->getValueSize(key, actualSize);\n        return static_cast<uint32_t>(ret);\n    }\n    return 0;\n}\n\nMMKV_EXPORT int32_t writeValueToNB(void *handle, char *oKey, void *pointer, uint32_t size) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->writeValueToBuffer(key, pointer, size);\n    }\n    return -1;\n}\n\nMMKV_EXPORT uint64_t allKeys(void *handle, char ***keyArrayPtr, uint32_t **sizeArrayPtr, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        auto keys = kv->allKeys(filterExpire);\n        if (!keys.empty()) {\n            auto keyArray = (char **) malloc(keys.size() * sizeof(void *));\n            auto sizeArray = (uint32_t *) malloc(keys.size() * sizeof(uint32_t *));\n            if (!keyArray || !sizeArray) {\n                free(keyArray);\n                free(sizeArray);\n                return 0;\n            }\n            *keyArrayPtr = keyArray;\n            *sizeArrayPtr = sizeArray;\n\n            for (size_t index = 0; index < keys.size(); index++) {\n                auto &key = keys[index];\n                sizeArray[index] = static_cast<uint32_t>(key.length());\n                keyArray[index] = (char *) malloc(key.length());\n                if (keyArray[index]) {\n                    memcpy(keyArray[index], key.data(), key.length());\n                }\n            }\n        }\n        return keys.size();\n    }\n    return 0;\n}\n\nMMKV_EXPORT bool containsKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        return kv->containsKey(key);\n    }\n    return false;\n}\n\nMMKV_EXPORT uint64_t count(void *handle, bool filterExpire) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->count(filterExpire);\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t totalSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->totalSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT uint64_t actualSize(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->actualSize();\n    }\n    return 0;\n}\n\nMMKV_EXPORT void removeValueForKey(void *handle, char *oKey) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && oKey) {\n        string key(oKey);\n        kv->removeValueForKey(key);\n    }\n}\n\nMMKV_EXPORT void removeValuesForKeys(void *handle, char **keyArray, uint32_t *sizeArray, uint64_t count) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv && keyArray && sizeArray && count > 0) {\n        vector<string> arrKeys;\n        arrKeys.reserve(count);\n        for (uint64_t index = 0; index < count; index++) {\n            if (sizeArray[index] > 0 && keyArray[index]) {\n                arrKeys.emplace_back(keyArray[index], sizeArray[index]);\n            }\n        }\n        if (!arrKeys.empty()) {\n            kv->removeValuesForKeys(arrKeys);\n        }\n    }\n}\n\nMMKV_EXPORT void clearAll(void *handle, bool keepSpace) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearAll(keepSpace);\n    }\n}\n\nMMKV_EXPORT void mmkvSync(void *handle, bool sync) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->sync((SyncFlag) sync);\n    }\n}\n\nMMKV_EXPORT void clearMemoryCache(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->clearMemoryCache();\n    }\n}\n\nMMKV_EXPORT int32_t pageSize() {\n    return static_cast<int32_t>(DEFAULT_MMAP_SIZE);\n}\n\nMMKV_EXPORT const char *version() {\n    return MMKV_VERSION;\n}\n\nMMKV_EXPORT void trim(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->trim();\n    }\n}\n\nMMKV_EXPORT void mmkvClose(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->close();\n    }\n}\n\nMMKV_EXPORT void mmkvMemcpy(void *dst, const void *src, uint64_t size) {\n    memcpy(dst, src, size);\n}\n\nMMKV_EXPORT bool backupOne(const char *mmapID, const char *dstDir, const char *rootPath) {\n    auto dst = string(dstDir);\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupOneToDirectory(mmapID, dst, &root);\n        }\n    }\n    return MMKV::backupOneToDirectory(mmapID, dst);\n}\n\nMMKV_EXPORT bool restoreOne(const char *mmapID, const char *srcDir, const char *rootPath) {\n    auto src = string(srcDir);\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreOneFromDirectory(mmapID, src, &root);\n        }\n    }\n    return MMKV::restoreOneFromDirectory(mmapID, src);\n}\n\nMMKV_EXPORT uint64_t backupAll(const char *dstDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::backupAllToDirectory(dstDir, &root);\n        }\n    }*/\n    auto dst = string(dstDir);\n    return MMKV::backupAllToDirectory(dst);\n}\n\nMMKV_EXPORT uint64_t restoreAll(const char *srcDir/*, const char *rootPath*/) {\n    // historically Android mistakenly use mmapKey as mmapID\n    // makes everything tricky with customize root\n    /*if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::restoreAllFromDirectory(srcDir, &root);\n        }\n    }*/\n   auto src = string(srcDir);\n    return MMKV::restoreAllFromDirectory(src);\n}\n\nMMKV_EXPORT bool enableAutoExpire(void *handle, uint32_t expireDuration) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableAutoKeyExpire(expireDuration);\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableAutoExpire(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableAutoKeyExpire();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool enableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->enableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool disableCompareBeforeSet(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->disableCompareBeforeSet();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isFileValid(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::isFileValid(mmapID, &root);\n        }\n    }\n    return MMKV::isFileValid(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool removeStorage(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::removeStorage(mmapID, &root);\n        }\n    }\n    return MMKV::removeStorage(mmapID, nullptr);\n}\n\nMMKV_EXPORT bool isMultiProcess(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isMultiProcess();\n    }\n    return false;\n}\n\nMMKV_EXPORT bool isReadOnly(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        return kv->isReadOnly();\n    }\n    return false;\n}\n\nMMKV_EXPORT void registerErrorHandler(ErrorCallback_t callback) {\n    g_flutterHandler.errorCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.contentChangeCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentChangeNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentChangeCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentLoadedCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void registerContentLoadedNotify(ContenctChangeCallback_t callback) {\n    g_flutterHandler.contentLoadedCallback = callback;\n    if (callback || g_flutterHandler.logCallback || g_flutterHandler.errorCallback || g_flutterHandler.contentChangeCallback) {\n        MMKV::registerHandler(&g_flutterHandler);\n    }\n}\n\nMMKV_EXPORT void checkContentChanged(void *handle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    if (kv) {\n        kv->checkContentChanged();\n    }\n}\n\nMMKV_EXPORT bool getNameSpace(const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (!root.empty()) {\n            MMKV::nameSpace(root);\n            return true;\n        }\n    }\n    return false;\n}\n\nMMKV_EXPORT bool checkExist(const char *mmapID, const char *rootPath) {\n    if (rootPath) {\n        auto root = string(rootPath);\n        if (root.length() > 0) {\n            return MMKV::checkExist(mmapID, &root);\n        }\n    }\n    return MMKV::checkExist(mmapID, nullptr);\n}\n\nMMKV_EXPORT size_t importFrom(void *handle, void *srcHandle) {\n    MMKV *kv = static_cast<MMKV *>(handle);\n    MMKV *kvSrc = static_cast<MMKV *>(srcHandle);\n    if (kv && kvSrc) {\n        return kv->importFrom(kvSrc);\n    }\n    return 0;\n}\n\nMMKV_EXPORT void freePtr(void *ptr) {\n    if (ptr) {\n        free(ptr);\n    }\n}\n\n#endif // MMKV_DISABLE_FLUTTER\n"
  },
  {
    "path": "flutter/mmkv_linux/linux/include/mmkv_linux/mmkv_linux_plugin.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n #ifndef FLUTTER_PLUGIN_MMKV_LINUX_PLUGIN_H_\n#define FLUTTER_PLUGIN_MMKV_LINUX_PLUGIN_H_\n\n#include <flutter_linux/flutter_linux.h>\n\nG_BEGIN_DECLS\n\n#ifdef FLUTTER_PLUGIN_IMPL\n#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define FLUTTER_PLUGIN_EXPORT\n#endif\n\n// typedef struct _MmkvLinuxPlugin MmkvLinuxPlugin;\n// typedef struct {\n//   GObjectClass parent_class;\n// } MmkvLinuxPluginClass;\n\n// FLUTTER_PLUGIN_EXPORT GType mmkv_linux_plugin_get_type();\n\nFLUTTER_PLUGIN_EXPORT void mmkv_linux_plugin_register_with_registrar(\n    FlPluginRegistrar* registrar);\n\nG_END_DECLS\n\n#endif  // FLUTTER_PLUGIN_MMKV_LINUX_PLUGIN_H_\n"
  },
  {
    "path": "flutter/mmkv_linux/linux/mmkv_linux_plugin.cc",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n #include \"include/mmkv_linux/mmkv_linux_plugin.h\"\n\n// #include <flutter_linux/flutter_linux.h>\n// #include <gtk/gtk.h>\n// #include <sys/utsname.h>\n\n// #include <cstring>\n\n// // Handles the getPlatformVersion method call.\n// FlMethodResponse *get_platform_version();\n\n// #define MMKV_LINUX_PLUGIN(obj) \\\n//   (G_TYPE_CHECK_INSTANCE_CAST((obj), mmkv_linux_plugin_get_type(), \\\n//                               MmkvLinuxPlugin))\n\n// struct _MmkvLinuxPlugin {\n//   GObject parent_instance;\n// };\n\n// G_DEFINE_TYPE(MmkvLinuxPlugin, mmkv_linux_plugin, g_object_get_type())\n\n// // Called when a method call is received from Flutter.\n// static void mmkv_linux_plugin_handle_method_call(\n//     MmkvLinuxPlugin* self,\n//     FlMethodCall* method_call) {\n//   g_autoptr(FlMethodResponse) response = nullptr;\n\n//   const gchar* method = fl_method_call_get_name(method_call);\n\n//   if (strcmp(method, \"getPlatformVersion\") == 0) {\n//     response = get_platform_version();\n//   } else {\n//     response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());\n//   }\n\n//   fl_method_call_respond(method_call, response, nullptr);\n// }\n\n// FlMethodResponse* get_platform_version() {\n//   struct utsname uname_data = {};\n//   uname(&uname_data);\n//   g_autofree gchar *version = g_strdup_printf(\"Linux %s\", uname_data.version);\n//   g_autoptr(FlValue) result = fl_value_new_string(version);\n//   return FL_METHOD_RESPONSE(fl_method_success_response_new(result));\n// }\n\n// static void mmkv_linux_plugin_dispose(GObject* object) {\n//   G_OBJECT_CLASS(mmkv_linux_plugin_parent_class)->dispose(object);\n// }\n\n// static void mmkv_linux_plugin_class_init(MmkvLinuxPluginClass* klass) {\n//   G_OBJECT_CLASS(klass)->dispose = mmkv_linux_plugin_dispose;\n// }\n\n// static void mmkv_linux_plugin_init(MmkvLinuxPlugin* self) {}\n\n// static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,\n//                            gpointer user_data) {\n//   MmkvLinuxPlugin* plugin = MMKV_LINUX_PLUGIN(user_data);\n//   mmkv_linux_plugin_handle_method_call(plugin, method_call);\n// }\n\nvoid mmkv_linux_plugin_register_with_registrar(FlPluginRegistrar* registrar) {\n  // MmkvLinuxPlugin* plugin = MMKV_LINUX_PLUGIN(\n  //     g_object_new(mmkv_linux_plugin_get_type(), nullptr));\n\n  // g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();\n  // g_autoptr(FlMethodChannel) channel =\n  //     fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),\n  //                           \"mmkv_linux\",\n  //                           FL_METHOD_CODEC(codec));\n  // fl_method_channel_set_method_call_handler(channel, method_call_cb,\n  //                                           g_object_ref(plugin),\n  //                                           g_object_unref);\n\n  // g_object_unref(plugin);\n}\n"
  },
  {
    "path": "flutter/mmkv_linux/pubspec.yaml",
    "content": "name: mmkv_linux\ndescription: Linux platform implementation of MMKV.\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_linux\nversion: 2.4.0\nhomepage: https://github.com/Tencent/mmkv\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  ffi: ^2.0.0\n  mmkv_platform_interface:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_platform_interface\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^5.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)\n  # which should be registered in the plugin registry. This is required for\n  # using method channels.\n  # The Android 'package' specifies package in which the registered class is.\n  # This is required for using method channels on Android.\n  # The 'ffiPlugin' specifies that native code should be built and bundled.\n  # This is required for using `dart:ffi`.\n  # All these are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    implements: mmkv\n    platforms:\n      linux:\n        # we don't really need a pluginClass for Linux, \n        # just to make the flutter build process happy.\n        pluginClass: MmkvLinuxPlugin\n        dartPluginClass: MMKVPlatformLinux\n        ffiPlugin: true"
  },
  {
    "path": "flutter/mmkv_ohos/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\n/pubspec.lock\n**/doc/api/\n.dart_tool/\n.packages\nbuild/\n"
  },
  {
    "path": "flutter/mmkv_ohos/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"beb3674133f4d44fee3be8c1f7f8561321959017\"\n  channel: \"[user-branch]\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: beb3674133f4d44fee3be8c1f7f8561321959017\n      base_revision: beb3674133f4d44fee3be8c1f7f8561321959017\n    - platform: ohos\n      create_revision: beb3674133f4d44fee3be8c1f7f8561321959017\n      base_revision: beb3674133f4d44fee3be8c1f7f8561321959017\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "flutter/mmkv_ohos/CHANGELOG.md",
    "content": "# MMKV Platform OHOS Change Log\n## v2.4.0 / 2026-03-18\nKeep up with native lib v2.4.0.\n\n## v2.3.0 / 2025-12-03\nKeep up with native lib v2.3.0.\n\n## v2.2.4 / 2025-09-25\n* Keep up with native lib v2.2.4.\n\n## v2.2.3 / 2025-08-20\nKeep up with native lib v2.2.3.\n\n## v2.2.2 / 2025-05-08\nKeep up with native lib v2.2.2.\n\n## v2.2.1 / 2025-04-25\nKeep up with native lib v2.2.1.\n\n## v2.2.0 / 2025-04-24\nKeep up with native lib v2.2.0.\n\n## v2.1.0 / 2025-02-18\nKeep up with native lib v2.1.0.\n\n## v2.0.2 / 2024-12-27\nKeep up with native lib v2.0.2. And add limitation of < v2.1.\n\n## v2.0.1 / 2024-10-25\nKeep up with native lib v2.0.1.\n\n## v2.0.0 / 2024-10-25\nBump to 2.0 to setup a breaking change version.\n\n## v1.0.3 / 2024-10-24\nRollback native lib to v1.3.x.\n\n## v1.0.2 / 2024-10-21\nKeep up with native lib v2.0.0.\n\n## v1.0.1 / 2024-07-26\nKeep up with native lib v1.3.9.\n\n## v1.0.0 / 2024-07-12\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_ohos/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_ohos/README.md",
    "content": "# mmkv\\_ohos\n\nThe OHOS implementation detail of [`MMKV`][1].\n\n## Usage\n\nNever use it alone. It's NOT a complete set of MMKV functionality. It just provides the FFI implementation needed by MMKV.\n\nThis package is [endorsed][2], which means you can simply use `mmkv`\nnormally. This package will be automatically included in your app when you do,\nso you do not need to add it to your `pubspec.yaml`.\n\nHowever, if you `import` this package to use any of its APIs directly, you\nshould add it to your `pubspec.yaml` as usual.\n\n[1]: https://pub.dev/packages/mmkv\n[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin\n"
  },
  {
    "path": "flutter/mmkv_ohos/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "flutter/mmkv_ohos/lib/mmkv_ohos.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:ffi\";\nimport \"package:flutter/services.dart\";\n// import \"package:path_provider/path_provider.dart\";\n// import \"package:path_provider_ohos/path_provider_ohos.dart\";\nimport \"package:mmkv_platform_interface/mmkv_platform_interface.dart\";\n\n// final\nclass MMKVPlatformOHOS extends MMKVPluginPlatformFFI {\n  // A default real implementation of the platform interface would go here.\n  static void registerWith() {\n    MMKVPluginPlatform.instance = MMKVPlatformOHOS();\n  }\n\n  static final _nativeLib = DynamicLibrary.open(\"libmmkv.so\");\n\n  @override\n  DynamicLibrary nativeLib() {\n    return _nativeLib;\n  }\n\n  static const MethodChannel _channel = MethodChannel(\"mmkv\");\n\n  @override\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\n    final Map<String, dynamic> params = {\n      \"rootDir\": rootDir,\n      \"logLevel\": logLevel,\n    };\n    final cacheDir = await getTemporaryPath();\n    params[\"cacheDir\"] = cacheDir;\n    if (logHandler != null) {\n      print(\"warn: Flutter on OHOS does not support log redirecting! You should use the ArtTS interface instead.\");\n    }\n\n    final ret = await _channel.invokeMethod(\"initializeMMKV\", params);\n    return ret;\n  }\n\n  /// OHOS doesn't publish their path_provider package to pub.dev\n  /// we got no choice but to override these calls\n  /// to make the dependency on them as less as possible\n\n  // static final _pathProviderImp = PathProviderOhos();\n\n  @override\n  Future<String> getApplicationDocumentsPath() async {\n    // final result = await getApplicationDocumentsDirectory();\n    // return result.path;\n    // final result = await _pathProviderImp.getApplicationDocumentsPath();\n    // return result!;\n    final result = await _channel.invokeMethod(\"getApplicationDocumentsPath\");\n    return result;\n  }\n\n  @override\n  Future<String> getTemporaryPath() async {\n    // final result = await getTemporaryDirectory();\n    // return result.path;\n    // final result = await _pathProviderImp.getTemporaryPath();\n    // return result!;\n    final result = await _channel.invokeMethod(\"getTemporaryPath\");\n    return result;\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/.gitignore",
    "content": "/node_modules\n/oh_modules\n/.preview\n/.idea\n/build\n/.cxx\n/.test\n/BuildProfile.ets\n/oh-package-lock.json5\nlocal.properties\n/har\n"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/build-profile.json5",
    "content": "{\n  \"apiType\": \"stageMode\",\n  \"buildOption\": {\n  },\n  \"targets\": [\n    {\n      \"name\": \"default\"\n    }\n  ]\n}\n"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/hvigorfile.ts",
    "content": "// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.\nexport { harTasks } from '@ohos/hvigor-ohos-plugin';"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/index.ets",
    "content": "/*\n* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*     http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport MMKVPlugin from './src/main/ets/components/plugin/MMKVPlugin';\nexport default MMKVPlugin;\n"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/oh-package.json5",
    "content": "{\n  \"name\": \"mmkv_ohos\",\n  \"version\": \"2.4.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"index.ets\",\n  \"author\": \"\",\n  \"license\": \"Apache-2.0\",\n  \"dependencies\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\",\n    \"@tencent/mmkv\": \">=2.4.0 <2.5\"\n//    \"@tencent/mmkv\": \"file:/Users/lingol/Developer/mmkv/OpenHarmony/MMKV/build/default/outputs/default/MMKV.har\"\n  },\n  \"devDependencies\": {},\n  \"dynamicDependencies\": {}\n}\n"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/src/main/ets/components/plugin/MMKVPlugin.ets",
    "content": "import {\n  FlutterPlugin,\n  FlutterPluginBinding,\n  MethodCall,\n  MethodCallHandler,\n  MethodChannel,\n  MethodResult,\n} from '@ohos/flutter_ohos';\nimport { MMKV } from '@tencent/mmkv';\n// import { MMKV } from 'mmkv_internal';\n\nimport { hilog } from '@kit.PerformanceAnalysisKit';\nimport common from '@ohos.app.ability.common';\n\n/** MMKVPlugin **/\nexport default class MMKVPlugin implements FlutterPlugin, MethodCallHandler {\n  private channel: MethodChannel | null = null;\n  private context: common.Context | null = null;\n\n  constructor(context?: common.Context) {\n    // hilog.info(0x0000, 'start', 'MMKVPlugin constructor: %{public}s', context);\n    if (context) {\n      this.context = context;\n    }\n  }\n\n  getUniqueClassName(): string {\n    return \"MMKVPlugin\"\n  }\n\n  onAttachedToEngine(binding: FlutterPluginBinding): void {\n    this.channel = new MethodChannel(binding.getBinaryMessenger(), \"mmkv\");\n    this.channel.setMethodCallHandler(this)\n\n    this.context = binding.getApplicationContext();\n    // hilog.info(0x0000, 'start', 'MMKVPlugin onAttachedToEngine: %{public}s', this.context);\n  }\n\n  onDetachedFromEngine(binding: FlutterPluginBinding): void {\n    if (this.channel != null) {\n      this.channel.setMethodCallHandler(null)\n    }\n  }\n\n  onMethodCall(call: MethodCall, result: MethodResult): void {\n    // hilog.info(0x0000, 'start', 'onMethodCall: %{public}s', call.method);\n    if (call.method == \"initializeMMKV\") {\n      let rootDir: string = call.argument(\"rootDir\");\n      let cacheDir: string = call.argument(\"cacheDir\");\n      let logLevel: number = call.argument(\"logLevel\");\n      let ret: string = MMKV.initializeWithPath(rootDir, cacheDir, logLevel);\n      result.success(ret);\n    } else if (call.method == \"getApplicationDocumentsPath\") {\n      let ret: string = this.getApplicationDocumentsPath();\n      result.success(ret);\n    } else if (call.method == \"getTemporaryPath\") {\n      let ret: string = this.getTemporaryPath();\n      result.success(ret);\n    } else {\n      result.notImplemented()\n    }\n  }\n\n  private getApplicationDocumentsPath(): string {\n    // return PathUtils.getDataDirectory(this.context) ?? \"\";\n    if (this.context) {\n      return this.context.filesDir;\n    }\n    return \"\";\n  }\n\n  private getTemporaryPath(): string {\n    if (this.context) {\n      return this.context.cacheDir;\n    }\n    return \"\";\n  }\n}"
  },
  {
    "path": "flutter/mmkv_ohos/ohos/src/main/module.json5",
    "content": "{\n  \"module\": {\n    \"name\": \"mmkv_ohos\",\n    \"type\": \"har\",\n    \"deviceTypes\": [\n      \"default\",\n      \"tablet\"\n    ]\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv_ohos/pubspec.yaml",
    "content": "name: mmkv_ohos\ndescription: OHOS platform implementation of MMKV.\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_ohos\nversion: 2.4.0\nhomepage: https://github.com/Tencent/mmkv\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n#  path_provider: ^2.0.1\n#  path_provider_ohos:\n#    git:\n#      url: \"https://gitee.com/openharmony-sig/flutter_packages.git\"\n#      path: \"packages/path_provider/path_provider_ohos\"\n  ffi: ^2.0.0\n  mmkv_platform_interface:\n    '>=2.4.0 <2.5.0'\n#    path: ../mmkv_platform_interface\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)\n  # which should be registered in the plugin registry. This is required for\n  # using method channels.\n  # The Android 'package' specifies package in which the registered class is.\n  # This is required for using method channels on Android.\n  # The 'ffiPlugin' specifies that native code should be built and bundled.\n  # This is required for using `dart:ffi`.\n  # All these are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    implements: mmkv\n    platforms:\n      ohos:\n        package: com.tencent.mmkv\n        pluginClass: MMKVPlugin\n        dartPluginClass: MMKVPlatformOHOS\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/CHANGELOG.md",
    "content": "# MMKV Platform Interface Change Log\n## v2.4.0 / 2026-03-18\n* Add `onMMKVContentLoadSuccessfully` to `MMKVHandler`.\n* Add `MMKVConfig` and `defaultMMKV(config)` support.\n\n## v2.3.0 / 2025-12-03\n* Alter `getMMKVWithID2()`, `getDefaultMMKV()`, `reKey()` and `checkReSetCryptKey()` to support **AES-256 encryption** functionality.\n\n## v2.2.3 / 2025-08-20\n* Protect from `freePtr()` not found.\n\n## v2.2.2 / 2025-08-20\n* Add `freePtr()`, mainly for Windows.\n\n## v2.2.1 / 2025-4-25\n* Add `importFrom()`.\n\n## v2.2.0 / 2025-4-24\n* Add `checkExist()`.\n* Add `groupPath()` for iOS.\n* Add `isFileValid()`.\n\n## v2.1.0 / 2025-02-18\n* Bump to 2.1 to setup a breaking change version.\n* Add `getNameSpace()`.\n\n## v2.0.0 / 2024-10-25\nBump to 2.0 to setup a breaking change version.\n\n## v1.0.3 / 2024-10-24\n* Rollback `isMultiProcess()`.\n* Rollback `isReadOnly()`.\n\n## v1.0.2 / 2024-10-21\n* Add `isMultiProcess()`.\n* Add `isReadOnly()`.\n\n## v1.0.1 / 2024-07-12\n* Add override point for path_provider.\n\n## v1.0.0 / 2024-04-19\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/README.md",
    "content": "# mmkv\\_platform\\_interface\n\nA common platform interface for the [`MMKV`][1] plugin.\n\nThis interface allows platform-specific implementations of the `MMKV`\nplugin, as well as the plugin itself, to ensure they are supporting the\nsame interface.\n\n# Usage\n\nTo implement a new platform-specific implementation of `MMKV`, extend\n[`MMKVPluginPlatform`][2] with an implementation that performs the\nplatform-specific behavior, and when you register your plugin, set the default\n`MMKVPluginPlatform` by calling\n`MMKVPluginPlatform.instance = MyMMKVPluginPlatform()`.\n\nCheckout [`mmkv_ios`][3] or [`mmkv_android`][4] for example.\n\n# Note on breaking changes\n\nStrongly prefer non-breaking changes (such as adding a method to the interface)\nover breaking changes for this package.\n\nSee https://flutter.dev/go/platform-interface-breaking-changes for a discussion\non why a less-clean interface is preferable to a breaking change.\n\n[1]: ../mmkv\n[2]: lib/mmkv_platform_interface.dart\n[3]: ../mmkv_ios\n[4]: ../mmkv_android\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/lib/mmkv_platform_ffi.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:ffi\";\nimport \"package:ffi/ffi.dart\";\nimport \"mmkv_platform_interface.dart\";\n\n/// A helper class to ease the implementation of MMKV platform plugin in FFI\n// abstract base\nclass MMKVPluginPlatformFFI extends MMKVPluginPlatform {\n  /// tells which dylib to lookup for function pointer\n  DynamicLibrary nativeLib() {\n    throw UnimplementedError();\n  }\n\n  /// a chance to map native function name (to avoid potential conflict)\n  String nativeFuncName(String name) {\n    return name;\n  }\n\n  @override\n  Pointer<Void> Function(Pointer<Utf8> mmapID, int, Pointer<Utf8> cryptKey, Pointer<Utf8> rootDir, int expectedCapacity, int isNameSpace, int aes256,\n      int enableKeyExpire, int expiredInSeconds, int enableCompareBeforeSet, int recover, int itemSizeLimit) getMMKVWithIDFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Pointer<Void> Function(Pointer<Utf8>, Uint32, Pointer<Utf8>, Pointer<Utf8>, Uint64, Int8, Int8,\n        Int32, Uint32, Int8, Int32, Uint32)>>(\"getMMKVWithID\")\n        .asFunction();\n  }\n\n  @override\n  Pointer<Void> Function(int, Pointer<Utf8> cryptKey, int aes256, int expectedCapacity, int enableKeyExpire, int expiredInSeconds, int enableCompareBeforeSet, int recover,\n      int itemSizeLimit) getDefaultMMKVFunc() {\n    return nativeLib().lookup<NativeFunction<Pointer<Void> Function(Uint32, Pointer<Utf8>, Int8, Uint64, Int32, Uint32, Int8, Int32, Uint32)>>(\"getDefaultMMKV\").asFunction();\n  }\n\n  @override\n  Pointer<Utf8> Function(Pointer<Void>) mmapIDFunc() {\n    return nativeLib().lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Void>)>>(nativeFuncName(\"mmapID\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeBoolFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int8)>>(nativeFuncName(\"encodeBool\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeBoolV2Func() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int8, Uint32)>>(nativeFuncName(\"encodeBool_v2\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeBoolFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int8)>>(nativeFuncName(\"decodeBool\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeInt32Func() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int32)>>(nativeFuncName(\"encodeInt32\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeInt32V2Func() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int32, Uint32)>>(nativeFuncName(\"encodeInt32_v2\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeInt32Func() {\n    return nativeLib().lookup<NativeFunction<Int32 Function(Pointer<Void>, Pointer<Utf8>, Int32)>>(nativeFuncName(\"decodeInt32\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeInt64Func() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int64)>>(nativeFuncName(\"encodeInt64\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeInt64V2Func() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Int64, Uint32)>>(nativeFuncName(\"encodeInt64_v2\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeInt64Func() {\n    return nativeLib().lookup<NativeFunction<Int64 Function(Pointer<Void>, Pointer<Utf8>, Int64)>>(nativeFuncName(\"decodeInt64\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, double) encodeDoubleFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Double)>>(nativeFuncName(\"encodeDouble\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, double, int) encodeDoubleV2Func() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Double, Uint32)>>(nativeFuncName(\"encodeDouble_v2\"))\n        .asFunction();\n  }\n\n  @override\n  double Function(Pointer<Void>, Pointer<Utf8>, double) decodeDoubleFunc() {\n    return nativeLib().lookup<NativeFunction<Double Function(Pointer<Void>, Pointer<Utf8>, Double)>>(nativeFuncName(\"decodeDouble\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int) encodeBytesFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, Uint64)>>(nativeFuncName(\"encodeBytes\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int, int) encodeBytesV2Func() {\n    return nativeLib()\n        .lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, Uint64, Uint32)>>(nativeFuncName(\"encodeBytes_v2\"))\n        .asFunction();\n  }\n\n  @override\n  Pointer<Uint8> Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint64>) decodeBytesFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Pointer<Uint8> Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint64>)>>(nativeFuncName(\"decodeBytes\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) reKeyFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Uint8>, Uint64, Int8)>>(nativeFuncName(\"reKey\")).asFunction();\n  }\n\n  @override\n  Pointer<Uint8> Function(Pointer<Void>, Pointer<Uint64>) cryptKeyFunc() {\n    return nativeLib().lookup<NativeFunction<Pointer<Uint8> Function(Pointer<Void>, Pointer<Uint64>)>>(nativeFuncName(\"cryptKey\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) checkReSetCryptKeyFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Void Function(Pointer<Void>, Pointer<Uint8>, Uint64, Int8)>>(nativeFuncName(\"checkReSetCryptKey\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, int) valueSizeFunc() {\n    return nativeLib().lookup<NativeFunction<Uint32 Function(Pointer<Void>, Pointer<Utf8>, Int8)>>(nativeFuncName(\"valueSize\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Void>, int) writeValueToNBFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Int32 Function(Pointer<Void>, Pointer<Utf8>, Pointer<Void>, Uint32)>>(nativeFuncName(\"writeValueToNB\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Pointer<Pointer<Utf8>>>, Pointer<Pointer<Uint32>>, int) allKeysFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Uint64 Function(Pointer<Void>, Pointer<Pointer<Pointer<Utf8>>>, Pointer<Pointer<Uint32>>, Int8)>>(\n        nativeFuncName(\"allKeys\"))\n        .asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, Pointer<Utf8>) containsKeyFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Void>, Pointer<Utf8>)>>(nativeFuncName(\"containsKey\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>, int) countFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Void>, Int8)>>(nativeFuncName(\"count\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>) totalSizeFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Void>)>>(nativeFuncName(\"totalSize\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void>) actualSizeFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Void>)>>(nativeFuncName(\"actualSize\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, Pointer<Utf8>) removeValueForKeyFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>, Pointer<Utf8>)>>(nativeFuncName(\"removeValueForKey\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, Pointer<Pointer<Utf8>>, Pointer<Uint32>, int) removeValuesForKeysFunc() {\n    return nativeLib()\n        .lookup<NativeFunction<Void Function(Pointer<Void>, Pointer<Pointer<Utf8>>, Pointer<Uint32>, Uint64)>>(nativeFuncName(\"removeValuesForKeys\"))\n        .asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, int) clearAllFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>, Uint32)>>(nativeFuncName(\"clearAll\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, int) mmkvSyncFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>, Int8)>>(\"mmkvSync\").asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>) clearMemoryCacheFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>)>>(nativeFuncName(\"clearMemoryCache\")).asFunction();\n  }\n\n  @override\n  int Function() pageSizeFunc() {\n    return nativeLib().lookup<NativeFunction<Int32 Function()>>(nativeFuncName(\"pageSize\")).asFunction();\n  }\n\n  @override\n  Pointer<Utf8> Function() versionFunc() {\n    return nativeLib().lookup<NativeFunction<Pointer<Utf8> Function()>>(nativeFuncName(\"version\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>) trimFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>)>>(nativeFuncName(\"trim\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>) mmkvCloseFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>)>>(\"mmkvClose\").asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void>, Pointer<Void>, int) memcpyFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>, Pointer<Void>, Uint64)>>(\"mmkvMemcpy\").asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> dstDir, Pointer<Utf8> rootPath) backupOneFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>)>>(nativeFuncName(\"backupOne\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> srcDir, Pointer<Utf8> rootPath) restoreOneFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>)>>(nativeFuncName(\"restoreOne\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> dstDir) backupAllFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Utf8>)>>(nativeFuncName(\"backupAll\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> srcDir) restoreAllFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Utf8>)>>(nativeFuncName(\"restoreAll\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>, int) enableAutoExpireFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>, Uint32)>>(nativeFuncName(\"enableAutoExpire\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>) disableAutoExpireFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>)>>(nativeFuncName(\"disableAutoExpire\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>) enableCompareBeforeSetFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>)>>(nativeFuncName(\"enableCompareBeforeSet\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>) disableCompareBeforeSetFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>)>>(nativeFuncName(\"disableCompareBeforeSet\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) removeStorageFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Utf8>, Pointer<Utf8>)>>(nativeFuncName(\"removeStorage\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>) isMultiProcessFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>)>>(nativeFuncName(\"isMultiProcess\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Void>) isReadOnlyFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Void>)>>(nativeFuncName(\"isReadOnly\")).asFunction();\n  }\n\n  @override\n  ErrorCallbackRegister registerErrorHandlerFunc() {\n    return nativeLib().lookup<NativeFunction<ErrorCallbackRegisterWrap>>(nativeFuncName(\"registerErrorHandler\")).asFunction();\n  }\n\n  @override\n  ContentCallbackRegister registerContentHandlerFunc() {\n    return nativeLib().lookup<NativeFunction<ContentCallbackRegisterWrap>>(nativeFuncName(\"registerContentChangeNotify\")).asFunction();\n  }\n\n  @override\n  ContentCallbackRegister registerContentLoadedHandlerFunc() {\n    return nativeLib().lookup<NativeFunction<ContentCallbackRegisterWrap>>(nativeFuncName(\"registerContentLoadedNotify\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer<Void> p1) checkContentChangedFunc() {\n    return nativeLib().lookup<NativeFunction<Void Function(Pointer<Void>)>>(nativeFuncName(\"checkContentChanged\")).asFunction();\n  }\n\n  @override\n  bool Function(Pointer<Utf8> rootPath) getNameSpaceFunc() {\n    return nativeLib().lookup<NativeFunction<Bool Function(Pointer<Utf8>)>>(nativeFuncName(\"getNameSpace\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) checkExistFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Utf8>, Pointer<Utf8>)>>(nativeFuncName(\"checkExist\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) isFileValidFunc() {\n    return nativeLib().lookup<NativeFunction<Int8 Function(Pointer<Utf8>, Pointer<Utf8>)>>(nativeFuncName(\"isFileValid\")).asFunction();\n  }\n\n  @override\n  Pointer<Utf8> Function() groupPathFunc() {\n    return nativeLib().lookup<NativeFunction<Pointer<Utf8> Function()>>(nativeFuncName(\"groupPath\")).asFunction();\n  }\n\n  @override\n  int Function(Pointer<Void> handle, Pointer<Void> srcHandle) importFromFunc() {\n    return nativeLib().lookup<NativeFunction<Uint64 Function(Pointer<Void>, Pointer<Void>)>>(nativeFuncName(\"importFrom\")).asFunction();\n  }\n\n  @override\n  void Function(Pointer) freePtrFunc() {\n    try {\n      return nativeLib().lookup<NativeFunction<Void Function(Pointer)>>(nativeFuncName(\"freePtr\")).asFunction();\n    } catch (e) {\n      return calloc.free;\n    }\n  }\n}\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/lib/mmkv_platform_interface.dart",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2024 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport \"dart:ffi\";\nimport \"package:ffi/ffi.dart\";\nimport \"package:path_provider/path_provider.dart\";\n\nexport \"mmkv_platform_ffi.dart\";\n\n/// Log level for MMKV.\nenum MMKVLogLevel { Debug, Info, Warning, Error, None }\n\n/// The recover strategic of MMKV on errors. {@link MMKV#registerHandler}\nenum MMKVRecoverStrategic {\n  /// The default strategic is to discard everything on errors.\n  OnErrorDiscard,\n\n  /// The recover strategic will try to recover as much data as possible.\n  OnErrorRecover,\n}\n\n/// Callback handler for MMKV.\n/// Callback is called on the operating thread of the MMKV instance.\nclass MMKVHandler {\n  /// return true to enable log redirect\n  bool wantLogRedirect() {\n    return false;\n  }\n\n  /// Log Redirecting.\n  ///\n  /// [level] The level of this log.\n  /// [file] The file name of this log.\n  /// [line] The line of code of this log.\n  /// [function] The function name of this log.\n  /// [message] The content of this log.\n  void mmkvLog(MMKVLogLevel level, String file, int line, String function, String message) {\n    print(\"redirect <$file:$line::$function> $message\");\n  }\n\n  /// By default MMKV will discard all data on crc32-check failure. [MMKVRecoverStrategic.OnErrorDiscard]\n  /// return [MMKVRecoverStrategic.OnErrorRecover] to recover any data on the file.\n  /// [mmapID] The unique ID of the MMKV instance.\n  MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) {\n    return MMKVRecoverStrategic.OnErrorDiscard;\n  }\n\n  /// By default MMKV will discard all data on file length mismatch. [MMKVRecoverStrategic.OnErrorDiscard]\n  /// return [MMKVRecoverStrategic.OnErrorRecover] to recover any data on the file.\n  /// [mmapID] The unique ID of the MMKV instance.\n  MMKVRecoverStrategic onMMKVFileLengthError(String mmapID) {\n    return MMKVRecoverStrategic.OnErrorDiscard;\n  }\n\n  /// return true to enable inter-process content change notification\n  bool wantContentChangeNotification() {\n    return false;\n  }\n\n  /// Inter-process content change notification.\n  ///\n  /// Triggered by any method call, such as getXXX() or setXXX() or [MMKV.checkContentChangedByOuterProcess()].\n  /// [mmapID] The unique ID of the changed MMKV instance.\n  void onContentChangedByOuterProcess(String mmapID) {\n    return;\n  }\n\n  /// Called when an MMKV file is loaded successfully.\n  /// [mmapID] The unique ID of the loaded MMKV instance.\n  void onMMKVContentLoadSuccessfully(String mmapID) {\n    return;\n  }\n}\n\n/// The interface class that all implementation of MMKV platform plugin must extend\n// abstract base\nclass MMKVPluginPlatform {\n  MMKVPluginPlatform();\n\n  // A plugin can have a default implementation, as shown here, or `instance`\n  // can be nullable, and the default instance can be null.\n  static MMKVPluginPlatform? instance = null;\n\n  MMKVHandler? theHandler = null;\n\n  // Methods for the plugin's platform interface would go here, often with\n  // implementations that throw UnimplementedError.\n\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeBoolFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Void> Function(Pointer<Utf8> mmapID, int, Pointer<Utf8> cryptKey, Pointer<Utf8> rootDir, int expectedCapacity, int isNameSpace, int aes256,\n      int enableKeyExpire, int expiredInSeconds, int enableCompareBeforeSet, int recover, int itemSizeLimit) getMMKVWithIDFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Void> Function(int, Pointer<Utf8> cryptKey, int aes256, int expectedCapacity, int enableKeyExpire, int expiredInSeconds,\n      int enableCompareBeforeSet, int recover, int itemSizeLimit) getDefaultMMKVFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Utf8> Function(Pointer<Void>) mmapIDFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeBoolV2Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeBoolFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeInt32Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeInt32V2Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeInt32Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) encodeInt64Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int, int) encodeInt64V2Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) decodeInt64Func() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, double) encodeDoubleFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, double, int) encodeDoubleV2Func() {\n    throw UnimplementedError();\n  }\n\n  double Function(Pointer<Void>, Pointer<Utf8>, double) decodeDoubleFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int) encodeBytesFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint8>, int, int) encodeBytesV2Func() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Uint8> Function(Pointer<Void>, Pointer<Utf8>, Pointer<Uint64>) decodeBytesFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) reKeyFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Uint8> Function(Pointer<Void>, Pointer<Uint64>) cryptKeyFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, Pointer<Uint8>, int length, int aes256) checkReSetCryptKeyFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, int) valueSizeFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Void>, int) writeValueToNBFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Pointer<Pointer<Utf8>>>, Pointer<Pointer<Uint32>>, int) allKeysFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, Pointer<Utf8>) containsKeyFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>, int) countFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>) totalSizeFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void>) actualSizeFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, Pointer<Utf8>) removeValueForKeyFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, Pointer<Pointer<Utf8>>, Pointer<Uint32>, int) removeValuesForKeysFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, int) clearAllFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, int) mmkvSyncFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>) clearMemoryCacheFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function() pageSizeFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Utf8> Function() versionFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>) trimFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>) mmkvCloseFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>, Pointer<Void>, int) memcpyFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> dstDir, Pointer<Utf8> rootPath) backupOneFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> srcDir, Pointer<Utf8> rootPath) restoreOneFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> dstDir) backupAllFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> srcDir) restoreAllFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>, int) enableAutoExpireFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>) disableAutoExpireFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>) enableCompareBeforeSetFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>) disableCompareBeforeSetFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) removeStorageFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>) isMultiProcessFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Void>) isReadOnlyFunc() {\n    throw UnimplementedError();\n  }\n\n  ErrorCallbackRegister registerErrorHandlerFunc() {\n    throw UnimplementedError();\n  }\n\n  ContentCallbackRegister registerContentHandlerFunc() {\n    throw UnimplementedError();\n  }\n\n  ContentCallbackRegister registerContentLoadedHandlerFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer<Void>) checkContentChangedFunc() {\n    throw UnimplementedError();\n  }\n\n  bool Function(Pointer<Utf8> rootPath) getNameSpaceFunc() {\n    throw UnimplementedError();\n  }\n\n  void Function(Pointer) freePtrFunc() {\n    throw UnimplementedError();\n  }\n\n  // some platform doesn't publish their path_provider package to pub.dev\n  // provide override point for these calls\n  Future<String> getApplicationDocumentsPath() async {\n    final result = await getApplicationDocumentsDirectory();\n    return result.path;\n  }\n\n  Future<String> getTemporaryPath() async {\n    final result = await getTemporaryDirectory();\n    return result.path;\n  }\n\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) checkExistFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Utf8> mmapID, Pointer<Utf8> rootPath) isFileValidFunc() {\n    throw UnimplementedError();\n  }\n\n  Pointer<Utf8> Function() groupPathFunc() {\n    throw UnimplementedError();\n  }\n\n  int Function(Pointer<Void> handle, Pointer<Void> srcHandle) importFromFunc() {\n    throw UnimplementedError();\n  }\n}\n\ntypedef LogCallbackWrap = Void Function(Uint32, Pointer<Utf8>, Int32, Pointer<Utf8>, Pointer<Utf8>);\ntypedef LogCallbackRegisterWrap = Void Function(Pointer<NativeFunction<LogCallbackWrap>>);\ntypedef LogCallbackRegister = void Function(Pointer<NativeFunction<LogCallbackWrap>>);\n\ntypedef ErrorCallbackWrap = Int32 Function(Pointer<Utf8>, Int32);\ntypedef ErrorCallbackRegisterWrap = Void Function(Pointer<NativeFunction<ErrorCallbackWrap>>);\ntypedef ErrorCallbackRegister = void Function(Pointer<NativeFunction<ErrorCallbackWrap>>);\n\ntypedef ContentCallbackWrap = Void Function(Pointer<Utf8>);\ntypedef ContentCallbackRegisterWrap = Void Function(Pointer<NativeFunction<ContentCallbackWrap>>);\ntypedef ContentCallbackRegister = void Function(Pointer<NativeFunction<ContentCallbackWrap>>);\n"
  },
  {
    "path": "flutter/mmkv_platform_interface/pubspec.yaml",
    "content": "name: mmkv_platform_interface\ndescription: A common platform interface for the MMKV plugin.\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_platform_interface\n# NOTE: We strongly prefer non-breaking changes, even at the expense of a\n# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes\nversion: 2.4.0\nhomepage: https://github.com/Tencent/MMKV\n\nenvironment:\n  sdk: '>=2.17.0 <4.0.0'\n  flutter: \">=3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  path_provider: ^2.0.1\n  ffi: ^2.0.0\n\ndev_dependencies:\n  test:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^3.0.2\n"
  },
  {
    "path": "flutter/mmkv_win32/.gitignore",
    "content": "# Miscellaneous\r\n*.class\r\n*.log\r\n*.pyc\r\n*.swp\r\n.DS_Store\r\n.atom/\r\n.build/\r\n.buildlog/\r\n.history\r\n.svn/\r\n.swiftpm/\r\nmigrate_working_dir/\r\n\r\n# IntelliJ related\r\n*.iml\r\n*.ipr\r\n*.iws\r\n.idea/\r\n\r\n# The .vscode folder contains launch configuration and tasks you configure in\r\n# VS Code which you may wish to be included in version control, so this line\r\n# is commented out by default.\r\n#.vscode/\r\n\r\n# Flutter/Dart/Pub related\r\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\r\n/pubspec.lock\r\n**/doc/api/\r\n.dart_tool/\r\n.flutter-plugins\r\n.flutter-plugins-dependencies\r\nbuild/\r\n"
  },
  {
    "path": "flutter/mmkv_win32/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"edada7c56edf4a183c1735310e123c7f923584f1\"\n  channel: \"stable\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: edada7c56edf4a183c1735310e123c7f923584f1\n      base_revision: edada7c56edf4a183c1735310e123c7f923584f1\n    - platform: windows\n      create_revision: edada7c56edf4a183c1735310e123c7f923584f1\n      base_revision: edada7c56edf4a183c1735310e123c7f923584f1\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "flutter/mmkv_win32/CHANGELOG.md",
    "content": "# MMKV Platform Windows Change Log\n## v2.4.0 / 2026-03-18\nKeep up with native lib v2.4.0.\n\n## v2.3.0 / 2025-12-03\nKeep up with native lib v2.3.0.\n\n## v2.2.4 / 2025-09-25\n* Keep up with native lib v2.2.4.\n\n## v2.2.3 / 2025-08-20\nThe initial release.\n"
  },
  {
    "path": "flutter/mmkv_win32/LICENSE",
    "content": "Tencent is pleased to support the open source community by making MMKV available.  \nCopyright (C) 2018 THL A29 Limited, a Tencent company.  All rights reserved.\nIf you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.\nIf you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms.  Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.\nA copy of the BSD 3-Clause License is included in this file.\n\nOther dependencies and licenses:\n\nOpen Source Software Licensed Under the OpenSSL License: \n----------------------------------------------------------------------------------------\n1. OpenSSL  1.1.0i\nCopyright (c) 1998-2018 The OpenSSL Project.  \nAll rights reserved.\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)  \nAll rights reserved.\n\n\nTerms of the OpenSSL License:\n---------------------------------------------------\nLICENSE ISSUES:\n--------------------------------------------------------------------\n\nThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.\nSee below for the actual license texts.\n\nOpenSSL License:\n--------------------------------------------------------------------\nCopyright (c) 1998-2018 The OpenSSL Project.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must display the following acknowledgment:\n\"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n\n4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.\n\n5. Products derived from this software may not be called \"OpenSSL\" nor may \"OpenSSL\" appear in their names without prior written permission of the OpenSSL Project.\n\n6. Redistributions of any form whatsoever must retain the following acknowledgment: \"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n\nTHIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n====================================================================\n* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).  This product includes software written by Tim Hudson (tjh@cryptsoft.com).\n\n\nOriginal SSLeay License:\n--------------------------------------------------------------------\nCopyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\nAll rights reserved.\n \nThis package is an SSL implementation written by Eric Young (eay@cryptsoft.com).\nThe implementation was written so as to conform with Netscapes SSL.\n \nThis library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).  \n\nCopyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed.  If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. \n  \nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n3. All advertising materials mentioning features or use of this software must display the following acknowledgement:\" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)\" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).\n4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n \nTHIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n  \nThe licence and distribution terms for any publically available version or derivative of this code cannot be changed.  i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]\n\n\n\nOpen Source Software Licensed Under the Apache License, Version 2.0: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. MultiprocessSharedPreferences  v1.0\nCopyright (C) 2014 seven456@gmail.com\n\n\nTerms of the Apache License, Version 2.0:\n--------------------------------------------------------------------\nApache License Version 2.0, January 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\na) \tYou must give any other recipients of the Work or Derivative Works a copy of this License; and\n\nb) \tYou must cause any modified files to carry prominent notices stating that You changed the files; and\n\nc) \tYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\nd) \tIf the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. \n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. \n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n\nOpen Source Software Licensed Under the zlib License: \nThe below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.\n----------------------------------------------------------------------------------------\n1. zlib  v1.2.11\nCopyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\nTerms of the zlib License:\n--------------------------------------------------------------------\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\n\nOpen Source Software Licensed Under the BSD 3-Clause License: \n----------------------------------------------------------------------------------------\n1. pybind11 v2.5.0\nCopyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nTerms of the BSD 3-Clause License:\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nNeither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "flutter/mmkv_win32/README.md",
    "content": "# mmkv\\_win32\n\nThe Windows implementation detail of [`MMKV`][1].\n\n## Usage\n\nNever use it alone. It's NOT a complete set of MMKV functionality. It just provides the FFI implementation needed by MMKV.\n\nThis package is [endorsed][2], which means you can simply use `mmkv`\nnormally. This package will be automatically included in your app when you do,\nso you do not need to add it to your `pubspec.yaml`.\n\nHowever, if you `import` this package to use any of its APIs directly, you\nshould add it to your `pubspec.yaml` as usual.\n\n[1]: https://pub.dev/packages/mmkv\n[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin\n"
  },
  {
    "path": "flutter/mmkv_win32/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\r\n\r\n# Additional information about this file can be found at\r\n# https://dart.dev/guides/language/analysis-options\r\n"
  },
  {
    "path": "flutter/mmkv_win32/lib/mmkv_win32.dart",
    "content": "/*\r\n * Tencent is pleased to support the open source community by making\r\n * MMKV available.\r\n *\r\n * Copyright (C) 2025 A29 Limited, a Tencent company.\r\n * All rights reserved.\r\n *\r\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\r\n * this file except in compliance with the License. You may obtain a copy of\r\n * the License at\r\n *\r\n *       https://opensource.org/licenses/BSD-3-Clause\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport 'dart:ffi';\r\nimport 'package:ffi/ffi.dart';\r\n// import 'package:flutter/services.dart';\r\nimport 'package:mmkv_platform_interface/mmkv_platform_interface.dart';\r\n\r\n// final\r\nclass MMKVPlatformWin32 extends MMKVPluginPlatformFFI {\r\n  static void registerWith() {\r\n    MMKVPluginPlatform.instance = MMKVPlatformWin32();\r\n  }\r\n\r\n  static final _nativeLib = DynamicLibrary.open(\"mmkv_win32_plugin.dll\");\r\n\r\n  @override\r\n  DynamicLibrary nativeLib() {\r\n    return _nativeLib;\r\n  }\r\n\r\n  static final Pointer<Utf16> Function(Pointer<Utf8> rootDir, int logLevel, Pointer<NativeFunction<LogCallbackWrap>>)\r\n      _mmkvInitialize = _nativeLib\r\n          .lookup<NativeFunction<Pointer<Utf16> Function(Pointer<Utf8>, Int32, Pointer<NativeFunction<LogCallbackWrap>>)>>(\r\n              \"mmkvInitialize\")\r\n          .asFunction();\r\n\r\n  @override\r\n  Future<String> initialize(String rootDir, {String? groupDir, int logLevel = 1, Pointer<NativeFunction<LogCallbackWrap>>? logHandler}) async {\r\n    final rootDirPtr = _string2Pointer(rootDir);\r\n\r\n    final ret = _mmkvInitialize(rootDirPtr, logLevel, logHandler ?? nullptr);\r\n\r\n    calloc.free(rootDirPtr);\r\n\r\n    return _pointer2String(ret) ?? rootDir;\r\n  }\r\n}\r\n\r\nPointer<Utf8> _string2Pointer(String? str) {\r\n  if (str != null) {\r\n    return str.toNativeUtf8();\r\n  }\r\n  return nullptr;\r\n}\r\n\r\nString? _pointer2String(Pointer<Utf16>? ptr) {\r\n  if (ptr != null && ptr != nullptr) {\r\n    return ptr.toDartString();\r\n  }\r\n  return null;\r\n}\r\n"
  },
  {
    "path": "flutter/mmkv_win32/pubspec.yaml",
    "content": "name: mmkv_win32\r\ndescription: Windows platform implementation of MMKV.\r\nrepository: https://github.com/Tencent/MMKV/tree/master/flutter/mmkv_win32\r\nversion: 2.4.0\r\nhomepage: https://github.com/Tencent/mmkv\r\n\r\nenvironment:\r\n  sdk: '>=2.17.0 <4.0.0'\r\n  flutter: \">=3.0.0\"\r\n\r\ndependencies:\r\n  flutter:\r\n    sdk: flutter\r\n  ffi: ^2.0.0\r\n  mmkv_platform_interface:\r\n    '>=2.4.0 <2.5.0'\r\n#    path: ../mmkv_platform_interface\r\n\r\ndev_dependencies:\r\n  flutter_test:\r\n    sdk: flutter\r\n  flutter_lints: ^5.0.0\r\n\r\n# For information on the generic Dart part of this file, see the\r\n# following page: https://dart.dev/tools/pub/pubspec\r\n\r\n# The following section is specific to Flutter packages.\r\nflutter:\r\n  # This section identifies this Flutter project as a plugin project.\r\n  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)\r\n  # which should be registered in the plugin registry. This is required for\r\n  # using method channels.\r\n  # The Android 'package' specifies package in which the registered class is.\r\n  # This is required for using method channels on Android.\r\n  # The 'ffiPlugin' specifies that native code should be built and bundled.\r\n  # This is required for using `dart:ffi`.\r\n  # All these are used by the tooling to maintain consistency when\r\n  # adding or updating assets for this project.\r\n  plugin:\r\n    implements: mmkv\r\n    platforms:\r\n      windows:\r\n        # we don't really need a pluginClass for Windows, \r\n        # just to make the flutter build process happy.\r\n        pluginClass: MmkvWin32Plugin\r\n        dartPluginClass: MMKVPlatformWin32\r\n        ffiPlugin: true\r\n"
  },
  {
    "path": "iOS/MMKV/MMKV/AutoCleanInfo.hpp",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2025 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef AutoCleanInfo_h\n#define AutoCleanInfo_h\n\n#include <queue>\n\nstruct AutoCleanInfo {\n    NSString *m_key;\n    uint64_t m_time;\n\n    AutoCleanInfo(NSString *key, uint64_t time) {\n        m_key = [key retain];\n        m_time = time;\n    }\n\n    AutoCleanInfo(AutoCleanInfo &&src) {\n        m_key = src.m_key;\n        src.m_key = nil;\n        m_time = src.m_time;\n    }\n\n    void operator=(AutoCleanInfo &&other) {\n        std::swap(m_key, other.m_key);\n        std::swap(m_time, other.m_time);\n    }\n\n    ~AutoCleanInfo() {\n        if (m_key) {\n            [m_key release];\n            m_key = nil;\n        }\n    }\n\n    bool operator<(const AutoCleanInfo &other) const {\n        // the oldest m_time comes first\n        return m_time > other.m_time;\n    }\n\n    // just forbid it for possibly misuse\n    explicit AutoCleanInfo(const AutoCleanInfo &other) = delete;\n    AutoCleanInfo &operator=(const AutoCleanInfo &other) = delete;\n};\n\ntypedef std::priority_queue<AutoCleanInfo> AutoCleanInfoQueue_t;\n\n#endif /* AutoCleanInfo_h */\n"
  },
  {
    "path": "iOS/MMKV/MMKV/MMKV.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"MMKVHandler.h\"\n\n#ifndef MMKV_OUT\n#define MMKV_OUT\n#endif\n\ntypedef NS_ENUM(NSUInteger, MMKVMode) {\n    MMKVSingleProcess = 1 << 0,\n    MMKVMultiProcess = 1 << 1,\n    // 2~4 are preserved for Android\n    MMKVReadOnly = 1 << 5,\n};\n\ntypedef NS_ENUM(UInt32, MMKVExpireDuration) {\n    MMKVExpireNever = 0,\n    MMKVExpireInMinute = 60,\n    MMKVExpireInHour = 60 * 60,\n    MMKVExpireInDay = 24 * 60 * 60,\n    MMKVExpireInMonth = 30 * 24 * 60 * 60,\n    MMKVExpireInYear = 365 * 30 * 24 * 60 * 60,\n};\n\n// all-in-one configuration for creating MMKV instance\ntypedef struct {\n    MMKVMode mode; // = MMKVSingleProcess;\n\n    // using AES-256 key length\n    BOOL aes256; // = NO;\n    NSData * _Nullable cryptKey; // = nil;\n\n    NSString * _Nullable rootPath; // = nil;\n\n    // the initial file size\n    size_t expectedCapacity; // = 0;\n\n    /// @YES / @NO to set this value\n    /// if nil, auto expire is off\n    NSNumber * _Nullable enableKeyExpire; // = nil;\n    uint32_t expiredInSeconds; // = MMKVExpireNever;\n\n    BOOL enableCompareBeforeSet; // = NO;\n\n    // if not set, use the old style callback\n    MMKVRecoverStrategic recover; // = MMKVOnErrorNotSet;\n\n    // the size limit of a key-value pair, reject insert if pass limit\n    uint32_t itemSizeLimit; // = 0;\n} MMKVConfig;\n\nstatic inline MMKVConfig MMKVConfigDefault(void) {\n    MMKVConfig config = {\n        .mode = MMKVSingleProcess, .aes256 = NO, .cryptKey = nil, .rootPath = nil,\n        .expectedCapacity = 0, .enableKeyExpire = nil, .expiredInSeconds = MMKVExpireNever,\n        .enableCompareBeforeSet = NO, .recover = MMKVOnErrorNotSet, .itemSizeLimit = 0,\n    };\n    return config;\n}\n\nNS_ASSUME_NONNULL_BEGIN\n\n#ifdef __cplusplus\nnamespace mmkv {\nclass MMKV;\n}\n#endif\n\n@class MMKVNameSpace;\n\n@interface MMKV : NSObject\n\n/// call this in main thread, before calling any other MMKV methods\n/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv\n/// @return root dir of MMKV\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir NS_SWIFT_NAME(initialize(rootDir:));\n\n/// call this in main thread, before calling any other MMKV methods\n/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv\n/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging\n/// @return root dir of MMKV\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:logLevel:));\n\n/// call this in main thread, before calling any other MMKV methods\n/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv\n/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging\n/// @return root dir of MMKV\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel handler:(nullable id<MMKVHandler>)handler NS_SWIFT_NAME(initialize(rootDir:logLevel:handler:));\n\n/// call this in main thread, before calling any other MMKV methods\n/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv\n/// @param groupDir the root dir of multi-process MMKV, MMKV with MMKVMultiProcess mode will be stored in groupDir/mmkv\n/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging\n/// @return root dir of MMKV\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:groupDir:logLevel:));\n\n/// call this in main thread, before calling any other MMKV methods\n/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv\n/// @param groupDir the root dir of multi-process MMKV, MMKV with MMKVMultiProcess mode will be stored in groupDir/mmkv\n/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging\n/// @return root dir of MMKV\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel handler:(nullable id<MMKVHandler>)handler NS_SWIFT_NAME(initialize(rootDir:groupDir:logLevel:handler:));\n\n/// a generic purpose instance (in MMKVSingleProcess mode)\n+ (nullable instancetype)defaultMMKV;\n\n/// an encrypted generic purpose instance (in MMKVSingleProcess mode)\n+ (nullable instancetype)defaultMMKVWithConfig:(MMKVConfig)config;\n\n/// an encrypted generic purpose instance (in MMKVSingleProcess mode)\n+ (nullable instancetype)defaultMMKVWithCryptKey:(nullable NSData *)cryptKey;\n\n/// an encrypted generic purpose instance (in MMKVSingleProcess mode)\n/// @param aes256 use aes 256 key length\n+ (nullable instancetype)defaultMMKVWithCryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256;\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID NS_SWIFT_NAME(init(mmapID:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID config:(MMKVConfig)config NS_SWIFT_NAME(init(mmapID:config:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:mode:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(init(mmapID:cryptKey:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 NS_SWIFT_NAME(init(mmapID:cryptKey:aes256:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:cryptKey:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:cryptKey:aes256:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:cryptKey:mode:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:cryptKey:aes256:mode:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:rootPath:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:rootPath:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:cryptKey:aes256:rootPath:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:mode:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n/// @param expectedCapacity the file size you expected when opening or creating file\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode expectedCapacity:(size_t)expectedCapacity  NS_SWIFT_NAME(init(mmapID:cryptKey:aes256:rootPath:mode:expectedCapacity:));\n\n/// you can call this on applicationWillTerminate, it's totally fine if you don't call\n+ (void)onAppTerminate;\n\n+ (NSString *)mmkvBasePath;\n+ (nullable NSString *)mmkvGroupPath;\n\n/// get a namespace with custom root dir\n+ (MMKVNameSpace *)nameSpace:(NSString *)rootPath;\n\n/// identical with the original MMKV with the global root dir\n+ (nullable MMKVNameSpace *)defaultNameSpace;\n\n/// if you want to change the base path, do it BEFORE getting any MMKV instance\n/// otherwise the behavior is undefined\n+ (void)setMMKVBasePath:(NSString *)basePath __attribute__((deprecated(\"use +initializeMMKV: instead\", \"+initializeMMKV:\")));\n\n// protection from possible misuse\n- (void)setValue:(nullable id)value forKey:(NSString *)key __attribute__((deprecated(\"use setObject:forKey: instead\")));\n- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath __attribute__((deprecated(\"use setObject:forKey: instead\")));\n\n- (NSString *)mmapID;\n\n/// transform plain text into encrypted text, or vice versa by passing newKey = nil\n/// you can change existing crypt key with different key\n/// @param newKey 16 bytes at most\n- (BOOL)reKey:(nullable NSData *)newKey NS_SWIFT_NAME(reset(cryptKey:));\n\n/// transform plain text into encrypted text, or vice versa by passing newKey = nil\n/// you can change existing crypt key with different key\n/// @param newKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n- (BOOL)reKey:(nullable NSData *)newKey aes256:(BOOL)aes256 NS_SWIFT_NAME(reset(cryptKey:aes256:));\n\n- (nullable NSData *)cryptKey;\n\n/// just reset cryptKey (will not encrypt or decrypt anything)\n/// usually you should call this method after other process reKey() the multi-process mmkv\n/// @param cryptKey 16 bytes at most\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(checkReSet(cryptKey:));\n\n/// just reset cryptKey (will not encrypt or decrypt anything)\n/// usually you should call this method after other process reKey() the multi-process mmkv\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 NS_SWIFT_NAME(checkReSet(cryptKey:aes256:));\n\n- (BOOL)setObject:(nullable NSObject<NSCoding> *)object forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setObject:(nullable NSObject<NSCoding> *)object forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setBool:(BOOL)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setBool:(BOOL)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setFloat:(float)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setFloat:(float)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setDouble:(double)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setDouble:(double)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setString:(NSString *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setString:(NSString *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (BOOL)setData:(NSData *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));\n- (BOOL)setData:(NSData *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds NS_SWIFT_NAME(set(_:forKey:expireDuration:));\n\n- (nullable id)getObjectOfClass:(Class)cls forKey:(NSString *)key NS_SWIFT_NAME(object(of:forKey:));\n\n- (BOOL)getBoolForKey:(NSString *)key __attribute__((swift_name(\"bool(forKey:)\")));\n- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue __attribute__((swift_name(\"bool(forKey:defaultValue:)\")));\n- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue __attribute__((swift_name(\"bool(forKey:defaultValue:hasValue:)\")));\n\n- (int32_t)getInt32ForKey:(NSString *)key NS_SWIFT_NAME(int32(forKey:));\n- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue NS_SWIFT_NAME(int32(forKey:defaultValue:));\n- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(int32(forKey:defaultValue:hasValue:));\n\n- (uint32_t)getUInt32ForKey:(NSString *)key NS_SWIFT_NAME(uint32(forKey:));\n- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue NS_SWIFT_NAME(uint32(forKey:defaultValue:));\n- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(uint32(forKey:defaultValue:hasValue:));\n\n- (int64_t)getInt64ForKey:(NSString *)key NS_SWIFT_NAME(int64(forKey:));\n- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue NS_SWIFT_NAME(int64(forKey:defaultValue:));\n- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(int64(forKey:defaultValue:hasValue:));\n\n- (uint64_t)getUInt64ForKey:(NSString *)key NS_SWIFT_NAME(uint64(forKey:));\n- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue NS_SWIFT_NAME(uint64(forKey:defaultValue:));\n- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(uint64(forKey:defaultValue:hasValue:));\n\n- (float)getFloatForKey:(NSString *)key NS_SWIFT_NAME(float(forKey:));\n- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue NS_SWIFT_NAME(float(forKey:defaultValue:));\n- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(float(forKey:defaultValue:hasValue:));\n\n- (double)getDoubleForKey:(NSString *)key NS_SWIFT_NAME(double(forKey:));\n- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue NS_SWIFT_NAME(double(forKey:defaultValue:));\n- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(double(forKey:defaultValue:hasValue:));\n\n- (nullable NSString *)getStringForKey:(NSString *)key NS_SWIFT_NAME(string(forKey:));\n- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue NS_SWIFT_NAME(string(forKey:defaultValue:));\n- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(string(forKey:defaultValue:hasValue:));\n\n- (nullable NSDate *)getDateForKey:(NSString *)key NS_SWIFT_NAME(date(forKey:));\n- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue NS_SWIFT_NAME(date(forKey:defaultValue:));\n- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(date(forKey:defaultValue:hasValue:));\n\n- (nullable NSData *)getDataForKey:(NSString *)key NS_SWIFT_NAME(data(forKey:));\n- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue NS_SWIFT_NAME(data(forKey:defaultValue:));\n- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue hasValue:(MMKV_OUT nullable BOOL *)hasValue NS_SWIFT_NAME(data(forKey:defaultValue:hasValue:));\n\n// return the actual size consumption of the key's value\n// Note: might be a little bigger than value's length\n- (size_t)getValueSizeForKey:(NSString *)key actualSize:(BOOL)actualSize NS_SWIFT_NAME(valueSize(forKey:actualSize:));\n\n/// @return size written into buffer\n/// @return -1 on any error\n- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer NS_SWIFT_NAME(writeValue(forKey:buffer:));\n\n- (BOOL)containsKey:(NSString *)key NS_SWIFT_NAME(contains(key:));\n\n- (size_t)count;\n\n- (size_t)totalSize;\n\n- (size_t)actualSize;\n\n+ (size_t)pageSize;\n\n+ (NSString *)version;\n\n- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block;\n- (NSArray *)allKeys;\n\n/// return count of non-expired keys, keep in mind that it comes with cost\n- (size_t)countNonExpiredKeys;\n\n/// return all non-expired keys, keep in mind that it comes with cost\n- (NSArray *)allNonExpiredKeys;\n\n/// all keys created (or last modified) longger than expiredInSeconds will be deleted on next full-write-back\n/// enableCompareBeforeSet will be invalid when Expiration is on\n/// @param expiredInSeconds = MMKVExpireNever (0) means no common expiration duration for all keys, aka each key will have it's own expiration duration\n- (BOOL)enableAutoKeyExpire:(uint32_t) expiredInSeconds NS_SWIFT_NAME(enableAutoKeyExpire(expiredInSeconds:));\n\n- (BOOL)disableAutoKeyExpire;\n\n/// Enable data compare before set, for better performance\n/// If data for key seldom changes, use it\n/// Notice: When encryption or expiration is on, compare-before-set will be invalid.\n/// For encryption, compare operation must decrypt data which is time consuming\n/// For expiration, compare is useless because in most cases the expiration time changes every time.\n- (BOOL)enableCompareBeforeSet;\n\n- (BOOL)disableCompareBeforeSet;\n\n- (void)removeValueForKey:(NSString *)key NS_SWIFT_NAME(removeValue(forKey:));\n\n- (void)removeValuesForKeys:(NSArray<NSString *> *)arrKeys NS_SWIFT_NAME(removeValues(forKeys:));\n\n- (void)clearAllWithKeepingSpace;\n\n- (void)clearAll;\n\n// MMKV's size won't reduce after deleting key-values\n// call this method after lots of deleting if you care about disk usage\n// note that `clearAll` has the similar effect of `trim`\n- (void)trim;\n\n// import all key-value items from source\n// return count of items imported\n- (size_t)importFrom:(MMKV *)src;\n\n/// call this method if the instance is no longer needed in the near future\n/// any subsequent call to the instance is undefined behavior\n- (void)close;\n\n/// call this method if you are facing memory-warning\n/// any subsequent call to the instance will load all key-values from file again\n- (void)clearMemoryCache;\n\n/// enable auto cleanup items that not been accessed recently\n/// disable by default\n/// note: if an item is strong referenced by outside, it won't be cleanup\n+ (void)enableAutoCleanUp:(uint32_t)maxIdleMinutes NS_SWIFT_NAME(enableAutoCleanUp(maxIdleMinutes:));\n\n+ (void)disableAutoCleanUp;\n\n/// you don't need to call this, really, I mean it\n/// unless you worry about running out of battery\n- (void)sync;\n- (void)async;\n\n- (BOOL)isMultiProcess;\n\n- (BOOL)isReadOnly;\n\n#ifdef __cplusplus\n- (mmkv::MMKV *)cppInstance;\n#endif\n\n/// backup one MMKV instance to dstDir\n/// @param mmapID the MMKV ID to backup\n/// @param rootPath the customize root path of the MMKV, if null then backup from the root dir of MMKV\n/// @param dstDir the backup destination directory\n+ (BOOL)backupOneMMKV:(NSString *)mmapID rootPath:(nullable NSString *)rootPath toDirectory:(NSString *)dstDir NS_SWIFT_NAME(backup(mmapID:rootPath:dstDir:));\n\n/// restore one MMKV instance from srcDir\n/// @param mmapID the MMKV ID to restore\n/// @param rootPath the customize root path of the MMKV, if null then restore to the root dir of MMKV\n/// @param srcDir the restore source directory\n+ (BOOL)restoreOneMMKV:(NSString *)mmapID rootPath:(nullable NSString *)rootPath fromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restore(mmapID:rootPath:srcDir:));\n\n/// backup all MMKV instance to dstDir\n/// @param rootPath the customize root path of the MMKV\n/// @param dstDir the backup destination directory\n/// @return count of MMKV successfully backuped\n+ (size_t)backupAll:(nullable NSString *)rootPath toDirectory:(NSString *)dstDir NS_SWIFT_NAME(backupAll(rootPath:dstDir:));\n\n/// restore all MMKV instance from srcDir\n/// @param rootPath the customize root path of the MMKV\n/// @param srcDir the restore source directory\n/// @return count of MMKV successfully restored\n+ (size_t)restoreAll:(nullable NSString *)rootPath fromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restoreAll(rootPath:srcDir:));\n\n/// backup one MMKVMultiProcess MMKV instance to dstDir\n/// @param mmapID the MMKV ID to backup\n/// @param dstDir the backup destination directory\n+ (BOOL)backupMultiProcessMMKV:(NSString *)mmapID toDirectory:(NSString *)dstDir NS_SWIFT_NAME(backupMultiProcess(mmapID:dstDir:));\n\n/// restore one MMKVMultiProcess MMKV instance from srcDir\n/// @param mmapID the MMKV ID to restore\n/// @param srcDir the restore source directory\n+ (BOOL)restoreMultiProcessMMKV:(NSString *)mmapID fromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restoreMultiProcess(mmapID:srcDir:));\n\n/// backup all MMKVMultiProcess MMKV instance to dstDir\n/// @param dstDir the backup destination directory\n/// @return count of MMKV successfully backuped\n+ (size_t)backupAllMultiProcessToDirectory:(NSString *)dstDir NS_SWIFT_NAME(backupAllMultiProcess(dstDir:));\n\n/// restore all MMKVMultiProcess MMKV instance from srcDir\n/// @param srcDir the restore source directory\n/// @return count of MMKV successfully restored\n+ (size_t)restoreAllMultiProcessFromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restoreAllMultiProcess(srcDir:));\n\n/// check if content changed by other process\n- (void)checkContentChanged;\n\n+ (void)registerHandler:(id<MMKVHandler>)handler __attribute__((deprecated(\"use +initializeMMKV:logLevel:handler: instead\")));\n+ (void)unregiserHandler;\n\n/// MMKVLogInfo by default\n/// MMKVLogNone to disable all logging\n+ (void)setLogLevel:(MMKVLogLevel)logLevel __attribute__((deprecated(\"use +initializeMMKV:logLevel: instead\", \"initializeMMKV:nil logLevel\")));\n\n/// Migrate NSUserDefault data to MMKV\n/// @param userDaults the dictionaryRepresentation of the NSUserDefaults instance to be imported\n/// @return imported count of key-values\n- (uint64_t)migrateFromUserDefaultsDictionaryRepresentation:(NSDictionary *)userDaults NS_SWIFT_NAME(migrateFrom(userDefaultsDictionaryRepresentation:));\n// use [MMKV migrateFromUserDefaultsDictionaryRepresentation:] instead\n- (uint32_t)migrateFromUserDefaults:(id) userDaults NS_SWIFT_NAME(migrateFrom(userDefaults:)) NS_UNAVAILABLE;\n\n/// detect if the MMKV file is valid or not\n/// Note: Don't use this to check the existence of the instance, the return value is undefined if the file was never created.\n+ (BOOL)isFileValid:(NSString *)mmapID NS_SWIFT_NAME(isFileValid(for:));\n+ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(isFileValid(for:rootPath:));\n\n/// remove the storage of the MMKV, including the data file & meta file (.crc)\n/// Note: the existing instance (if any) will be closed & destroyed\n+ (BOOL)removeStorage:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(removeStorage(for:rootPath:));\n+ (BOOL)removeStorage:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(removeStorage(for:mode:));\n\n/// detect if the MMKV file exist or not\n+ (BOOL)checkExist:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(checkExist(for:rootPath:));\n+ (BOOL)checkExist:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(checkExist(for:mode:));\n\n// protection from potential misuse\n+ (void)initialize NS_UNAVAILABLE;\n\n@end\n\n@interface MMKVNameSpace : NSObject\n\n- (NSString*)rootPath;\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID NS_SWIFT_NAME(mmkv(mmapID:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID config:(MMKVConfig)config NS_SWIFT_NAME(init(mmapID:config:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param expectedCapacity the file size you expected when opening or creating file\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(mmkv(mmapID:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(mmkv(mmapID:mode:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(mmkv(mmapID:cryptKey:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 NS_SWIFT_NAME(mmkv(mmapID:cryptKey:aes256:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param expectedCapacity the file size you expected when opening or creating file\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(mmkv(mmapID:cryptKey:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param expectedCapacity the file size you expected when opening or creating file\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 expectedCapacity:(size_t)expectedCapacity NS_SWIFT_NAME(mmkv(mmapID:cryptKey:aes256:expectedCapacity:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 16 bytes at most\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode NS_SWIFT_NAME(mmkv(mmapID:cryptKey:mode:));\n\n/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID\n/// @param cryptKey 32 bytes at most\n/// @param aes256 use aes 256 key length\n/// @param mode MMKVReadOnly for readonly MMKV, MMKVMultiProcess for multi-process MMKV\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 mode:(MMKVMode)mode NS_SWIFT_NAME(mmkv(mmapID:cryptKey:aes256:mode:));\n\n/// backup one MMKV instance from the customize root path to dstDir\n/// @param mmapID the MMKV ID to backup\n/// @param dstDir the backup destination directory\n- (BOOL)backupOneMMKV:(NSString *)mmapID toDirectory:(NSString *)dstDir NS_SWIFT_NAME(backup(mmapID:dstDir:));\n\n/// restore one MMKV instance from srcDir to the customize root path\n/// @param mmapID the MMKV ID to restore\n/// @param srcDir the restore source directory\n- (BOOL)restoreOneMMKV:(NSString *)mmapID fromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restore(mmapID:srcDir:));\n\n/// backup all MMKV instance from the customize root path to dstDir\n/// @param dstDir the backup destination directory\n/// @return count of MMKV successfully backuped\n- (size_t)backupAllToDirectory:(NSString *)dstDir NS_SWIFT_NAME(backupAll(dstDir:));\n\n/// restore all MMKV instance from srcDir to the customize root path\n/// @param srcDir the restore source directory\n/// @return count of MMKV successfully restored\n- (size_t)restoreAllFromDirectory:(NSString *)srcDir NS_SWIFT_NAME(restoreAll(srcDir:));\n\n/// detect if the MMKV file is valid or not\n/// Note: Don't use this to check the existence of the instance, the return value is undefined if the file was never created.\n- (BOOL)isFileValid:(NSString *)mmapID NS_SWIFT_NAME(isFileValid(for:));\n\n/// remove the storage of the MMKV, including the data file & meta file (.crc)\n/// Note: the existing instance (if any) will be closed & destroyed\n- (BOOL)removeStorage:(NSString *)mmapID NS_SWIFT_NAME(removeStorage(for:));\n\n/// detect if the MMKV file exist or not\n- (BOOL)checkExist:(NSString *)mmapID NS_SWIFT_NAME(checkExist(for:));\n\n// protection from potential misuse\n- (instancetype)init NS_UNAVAILABLE;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "iOS/MMKV/MMKV/MMKVAppExtension/include/MMKVAppExtension/MMKV.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../../MMKV.h\"\n"
  },
  {
    "path": "iOS/MMKV/MMKV/MMKVAppExtension/libMMKV.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../libMMKV.mm\"\n"
  },
  {
    "path": "iOS/MMKV/MMKV/MMKVHandler.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef MMKVHandler_h\n#define MMKVHandler_h\n#import <Foundation/Foundation.h>\n\ntypedef NS_ENUM(NSUInteger, MMKVRecoverStrategic) {\n    MMKVOnErrorDiscard = 0,\n    MMKVOnErrorRecover,\n    MMKVOnErrorNotSet,\n};\n\ntypedef NS_ENUM(NSUInteger, MMKVLogLevel) {\n    MMKVLogDebug = 0, // not available for release/product build\n    MMKVLogInfo = 1,  // default level\n    MMKVLogWarning,\n    MMKVLogError,\n    MMKVLogNone, // special level used to disable all log messages\n};\n\n// callback is called on the operating thread of the MMKV instance\n@protocol MMKVHandler <NSObject>\n@optional\n\n// by default MMKV will discard all datas on crc32-check failure\n// return `MMKVOnErrorRecover` to recover any data on the file\n- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID;\n\n// by default MMKV will discard all datas on file length mismatch\n// return `MMKVOnErrorRecover` to recover any data on the file\n- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID;\n\n// by default MMKV will print log using NSLog\n// implement this method to redirect MMKV's log\n- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message;\n\n// called when content is changed by other process\n// doesn't guarantee real-time notification\n- (void)onMMKVContentChange:(NSString *)mmapID;\n\n// called when an MMKV file is loaded successfully.\n// This is triggered only when MMKV actually opens and maps the file.\n- (void)onMMKVContentLoadSuccessfully:(NSString *)mmapID;\n\n@end\n\n#endif /* MMKVHandler_h */\n"
  },
  {
    "path": "iOS/MMKV/MMKV/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKV/MMKV/Resources/PrivacyInfo.xcprivacy",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>NSPrivacyTracking</key>\n    <false/>\n    \n    <key>NSPrivacyTrackingDomains</key>\n    <array/>\n    \n    <key>NSPrivacyCollectedDataTypes</key>\n    <array/>\n    \n    <key>NSPrivacyAccessedAPITypes</key>\n    <array/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKV/MMKV/fakeinclude/MMKV/MMKV.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2026 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../MMKV.h\"\n"
  },
  {
    "path": "iOS/MMKV/MMKV/libMMKV.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"MMKV.h\"\n#import <MMKVCore/MMKV.h>\n#import <MMKVCore/MMKVLog.h>\n#import <MMKVCore/ScopedLock.hpp>\n#import <MMKVCore/ThreadLock.h>\n#import <CommonCrypto/CommonDigest.h>\n#import <TargetConditionals.h>\n#import \"AutoCleanInfo.hpp\"\n\n#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION) && !(TARGET_OS_MACCATALYST)\n#import <UIKit/UIKit.h>\n#endif\n\n#if __has_feature(objc_arc)\n#error This file must be compiled with MRC. Use -fno-objc-arc flag.\n#endif\n\nusing namespace std;\n\nstatic NSMutableDictionary *g_instanceDic = nil;\nstatic mmkv::ThreadLock *g_lock;\nstatic id<MMKVHandler> g_callbackHandler = nil;\nstatic bool g_isLogRedirecting = false;\nstatic NSString *g_basePath = nil;\nstatic NSString *g_groupPath = nil;\n\n#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION) && !(TARGET_OS_MACCATALYST)\nstatic BOOL g_isRunningInAppExtension = NO;\n#endif\n\n#pragma makr - callbacks\n\n// C++ adapter class that bridges mmkv::MMKVHandler to Objective-C MMKVHandler protocol\nclass ObjCMMKVHandler : public mmkv::MMKVHandler {\npublic:\n    void mmkvLog(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message) override {\n        if (g_callbackHandler && [g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {\n            [g_callbackHandler mmkvLogWithLevel:(MMKVLogLevel) level file:file line:line func:function message:message];\n        }\n    }\n\n    mmkv::MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID) override {\n        if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)]) {\n            auto ret = [g_callbackHandler onMMKVCRCCheckFail:[NSString stringWithUTF8String:mmapID.c_str()]];\n            return (mmkv::MMKVRecoverStrategic) ret;\n        }\n        return mmkv::OnErrorDiscard;\n    }\n\n    mmkv::MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID) override {\n        if ([g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {\n            auto ret = [g_callbackHandler onMMKVFileLengthError:[NSString stringWithUTF8String:mmapID.c_str()]];\n            return (mmkv::MMKVRecoverStrategic) ret;\n        }\n        return mmkv::OnErrorDiscard;\n    }\n\n    void onContentChangedByOuterProcess(const std::string &mmapID) override {\n        if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {\n            [g_callbackHandler onMMKVContentChange:[NSString stringWithUTF8String:mmapID.c_str()]];\n        }\n    }\n\n    void onMMKVContentLoadSuccessfully(const std::string &mmapID) override {\n        if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentLoadSuccessfully:)]) {\n            [g_callbackHandler onMMKVContentLoadSuccessfully:[NSString stringWithUTF8String:mmapID.c_str()]];\n        }\n    }\n};\n\nstatic ObjCMMKVHandler g_cppHandler;\n\n@interface MMKVNameSpace ()\n\n- (instancetype) initWith:(NSString *)path;\n\n@end\n\n@implementation MMKV {\n    NSString *m_mmapID;\n    NSString *m_mmapKey;\n    mmkv::MMKV *m_mmkv;\n    uint64_t m_lastAccessTime;\n}\n\n#pragma mark - init\n\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir {\n    return [MMKV initializeMMKV:rootDir logLevel:MMKVLogInfo handler:nil];\n}\n\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel {\n    return [MMKV initializeMMKV:rootDir logLevel:logLevel handler:nil];\n}\n\n+ (void)initialize {\n    if (self == MMKV.class) {\n        g_instanceDic = [[NSMutableDictionary alloc] init];\n        g_lock = new mmkv::ThreadLock();\n        g_lock->initialize();\n    }\n}\n\nstatic BOOL g_hasCalledInitializeMMKV = NO;\n\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel handler:(id<MMKVHandler>)handler {\n    if (g_hasCalledInitializeMMKV) {\n        MMKVWarning(\"already called +initializeMMKV before, ignore this request\");\n        return [self mmkvBasePath];\n    }\n    [g_callbackHandler release];\n    g_callbackHandler = [handler retain];\n    mmkv::MMKVHandler *cppHandler = nullptr;\n    if (g_callbackHandler) {\n        if ([g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {\n            g_isLogRedirecting = true;\n        }\n        cppHandler = &g_cppHandler;\n    }\n\n    if (rootDir != nil) {\n        [g_basePath release];\n        g_basePath = [rootDir retain];\n    } else {\n        [self mmkvBasePath];\n    }\n    NSAssert(g_basePath.length > 0, @\"MMKV not initialized properly, must not call +initializeMMKV: before -application:didFinishLaunchingWithOptions:\");\n    mmkv::MMKV::initializeMMKV(g_basePath.UTF8String, (mmkv::MMKVLogLevel) logLevel, cppHandler);\n\n    if (g_callbackHandler) {\n        mmkv::MMKV::registerHandler(&g_cppHandler);\n    }\n    \n#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION) && !(TARGET_OS_MACCATALYST)\n    // just in case someone forget to set the MMKV_IOS_EXTENSION macro\n    if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@\".appex\"]) {\n        g_isRunningInAppExtension = YES;\n    }\n    if (!g_isRunningInAppExtension) {\n        auto appState = [UIApplication sharedApplication].applicationState;\n        auto isInBackground = (appState == UIApplicationStateBackground);\n        mmkv::MMKV::setIsInBackground(isInBackground);\n        MMKVInfo(\"appState:%ld\", (long) appState);\n\n        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];\n        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];\n    }\n#endif\n\n    g_hasCalledInitializeMMKV = YES;\n\n    return [self mmkvBasePath];\n}\n\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel {\n    auto ret = [MMKV initializeMMKV:rootDir logLevel:logLevel handler:nil];\n\n    g_groupPath = [[groupDir stringByAppendingPathComponent:@\"mmkv\"] retain];\n    MMKVInfo(\"groupDir: %@\", g_groupPath);\n\n    return ret;\n}\n\n+ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel handler:(nullable id<MMKVHandler>)handler {\n    auto ret = [MMKV initializeMMKV:rootDir logLevel:logLevel handler:handler];\n\n    g_groupPath = [[groupDir stringByAppendingPathComponent:@\"mmkv\"] retain];\n    MMKVInfo(\"groupDir: %@\", g_groupPath);\n\n    return ret;\n}\n\n// a generic purpose instance\n+ (instancetype)defaultMMKV {\n    return [MMKV mmkvWithID:(@\"\" DEFAULT_MMAP_ID) cryptKey:nil aes256:NO rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (nullable instancetype)defaultMMKVWithConfig:(MMKVConfig)config {\n    return [MMKV mmkvWithID:(@\"\" DEFAULT_MMAP_ID) config:config];\n}\n\n+ (nullable instancetype)defaultMMKVWithCryptKey:(nullable NSData *)cryptKey {\n    return [MMKV mmkvWithID:(@\"\" DEFAULT_MMAP_ID) cryptKey:cryptKey aes256:NO rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (nullable instancetype)defaultMMKVWithCryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 {\n    return [MMKV mmkvWithID:(@\"\" DEFAULT_MMAP_ID) cryptKey:cryptKey aes256:aes256 rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n// any unique ID (com.tencent.xin.pay, etc)\n+ (instancetype)mmkvWithID:(NSString *)mmapID {\n    return [MMKV mmkvWithID:mmapID cryptKey:nil aes256:NO rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (nullable instancetype)mmkvWithID:(NSString *)mmapID config:(MMKVConfig)config {\n    if (config.rootPath == nil) {\n        if (config.mode & MMKVMultiProcess) {\n            config.rootPath = g_groupPath;\n        }\n    }\n    return [MMKV doGetWithID:mmapID config:config];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID\n                   cryptKey:nil\n                     aes256:NO\n                   rootPath:nil\n                       mode:MMKVSingleProcess\n           expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode {\n    auto rootPath = (mode & MMKVSingleProcess) ? nil : g_groupPath;\n    return [MMKV mmkvWithID:mmapID cryptKey:nil aes256:NO rootPath:rootPath mode:mode expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:nil mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:nil mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:nil mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode {\n    auto rootPath = (mode & MMKVSingleProcess) ? nil : g_groupPath;\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:rootPath mode:mode expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 mode:(MMKVMode)mode {\n    auto rootPath = (mode & MMKVSingleProcess) ? nil : g_groupPath;\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:rootPath mode:mode expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {\n    return [MMKV mmkvWithID:mmapID cryptKey:nil aes256:NO rootPath:rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID cryptKey:nil aes256:NO rootPath:rootPath mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 rootPath:(nullable NSString *)rootPath {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:rootPath mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:rootPath mode:mode expectedCapacity:0];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:rootPath mode:mode expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode expectedCapacity:(size_t)expectedCapacity {\n    NSAssert(g_hasCalledInitializeMMKV, @\"MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods\");\n    if (mode & MMKVMultiProcess) {\n        if (!rootPath) {\n            rootPath = g_groupPath;\n        }\n        if (!rootPath) {\n            MMKVError(\"Getting a multi-process MMKV [%@] without setting groupDir makes no sense\", mmapID);\n            MMKV_ASSERT(0);\n        }\n    }\n    return [self doGetWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:rootPath mode:mode expectedCapacity:expectedCapacity];\n}\n\n+ (instancetype)doGetWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode expectedCapacity:(size_t)expectedCapacity {\n    auto config = MMKVConfigDefault();\n    config.mode = mode;\n    config.aes256 = aes256;\n    config.cryptKey = cryptKey;\n    config.rootPath = rootPath;\n    config.expectedCapacity = expectedCapacity;\n\n    return [MMKV doGetWithID:mmapID config:config];\n}\n\n+ (instancetype)doGetWithID:(NSString *)mmapID config:(const MMKVConfig&)config {\n    if (mmapID.length <= 0) {\n        return nil;\n    }\n    SCOPED_LOCK(g_lock);\n\n    NSString *kvKey = [MMKV mmapKeyWithMMapID:mmapID rootPath:config.rootPath];\n    MMKV *kv = [g_instanceDic objectForKey:kvKey];\n    if (kv == nil) {\n        kv = [[MMKV alloc] initWithMMapID:mmapID config:config];\n        if (!kv->m_mmkv) {\n            [kv release];\n            return nil;\n        }\n        kv->m_mmapKey = kvKey;\n        [g_instanceDic setObject:kv forKey:kvKey];\n        [kv release];\n    }\n    kv->m_lastAccessTime = llround([NSDate timeIntervalSinceReferenceDate] * 1000);\n    return kv;\n}\n\n- (instancetype)initWithMMapID:(NSString *)mmapID config:(const MMKVConfig&)config {\n    if (self = [super init]) {\n        mmkv::MMKVConfig cppConfig;\n        cppConfig.mode = (mmkv::MMKVMode) config.mode;\n        cppConfig.aes256 = config.aes256;\n\n        auto rootPath = config.rootPath;\n        string pathTmp;\n        if (rootPath.length > 0) {\n            pathTmp = rootPath.UTF8String;\n        }\n        string cryptKeyTmp;\n        auto cryptKey = config.cryptKey;\n        if (cryptKey.length > 0) {\n            cryptKeyTmp = string((char *) cryptKey.bytes, cryptKey.length);\n        }\n        cppConfig.rootPath = pathTmp.empty() ? nullptr : &pathTmp;\n        cppConfig.cryptKey = cryptKeyTmp.empty() ? nullptr : &cryptKeyTmp;\n\n        cppConfig.expectedCapacity = config.expectedCapacity;\n\n        if (config.enableKeyExpire != nil) {\n            cppConfig.enableKeyExpire = (config.enableKeyExpire.boolValue == YES);\n        }\n        cppConfig.expiredInSeconds = config.expiredInSeconds;\n        cppConfig.enableCompareBeforeSet = (config.enableCompareBeforeSet == YES);\n\n        if (config.recover != MMKVOnErrorNotSet) {\n            cppConfig.recover = (mmkv::MMKVRecoverStrategic) config.recover;\n        }\n\n        cppConfig.itemSizeLimit = config.itemSizeLimit;\n\n        m_mmkv = mmkv::MMKV::mmkvWithID(mmapID.UTF8String, cppConfig);\n        if (!m_mmkv) {\n            return self;\n        }\n        m_mmapID = [[NSString alloc] initWithUTF8String:m_mmkv->mmapID().c_str()];\n\n#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION) && !(TARGET_OS_MACCATALYST)\n        if (!g_isRunningInAppExtension) {\n            [[NSNotificationCenter defaultCenter] addObserver:self\n                                                     selector:@selector(onMemoryWarning)\n                                                         name:UIApplicationDidReceiveMemoryWarningNotification\n                                                       object:nil];\n        }\n#endif\n    }\n    return self;\n}\n\n- (void)dealloc {\n    [self clearMemoryCache];\n\n    [[NSNotificationCenter defaultCenter] removeObserver:self];\n\n    MMKVInfo(\"dealloc %@\", m_mmapID);\n    [m_mmapID release];\n\n    if (m_mmkv) {\n        m_mmkv->close();\n        m_mmkv = nullptr;\n    }\n\n    [super dealloc];\n}\n\n- (NSString *)mmapID {\n    return m_mmapID;\n}\n\n#pragma mark - Application state\n\n#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION) && !(TARGET_OS_MACCATALYST)\n- (void)onMemoryWarning {\n    MMKVInfo(\"cleaning on memory warning %@\", m_mmapID);\n\n    [self clearMemoryCache];\n}\n\n+ (void)didEnterBackground {\n    mmkv::MMKV::setIsInBackground(true);\n    MMKVInfo(\"isInBackground:%d\", true);\n}\n\n+ (void)didBecomeActive {\n    mmkv::MMKV::setIsInBackground(false);\n    MMKVInfo(\"isInBackground:%d\", false);\n}\n#endif\n\n- (void)clearAll {\n    m_mmkv->clearAll();\n}\n\n- (void)clearAllWithKeepingSpace {\n    m_mmkv->clearAll(true);\n}\n\n- (void)clearMemoryCache {\n    if (m_mmkv) {\n        m_mmkv->clearMemoryCache();\n    }\n}\n\n- (void)close {\n    SCOPED_LOCK(g_lock);\n    MMKVInfo(\"closing %@\", m_mmapID);\n\n    [g_instanceDic removeObjectForKey:m_mmapKey];\n\n    if (self.retainCount > 1) {\n        MMKVWarning(\"There's still reference on this kv: %@\", m_mmapID);\n    }\n}\n\n- (void)trim {\n    m_mmkv->trim();\n}\n\n- (size_t)importFrom:(MMKV *)src {\n    if (!src) {\n        return 0;\n    }\n    return m_mmkv->importFrom(src->m_mmkv);\n}\n\n#pragma mark - encryption & decryption\n\n#ifndef MMKV_DISABLE_CRYPT\n\n- (nullable NSData *)cryptKey {\n    auto str = m_mmkv->cryptKey();\n    if (str.length() > 0) {\n        return [NSData dataWithBytes:str.data() length:str.length()];\n    }\n    return nil;\n}\n\n- (BOOL)reKey:(nullable NSData *)newKey {\n    return [self reKey:newKey aes256:NO];\n}\n\n- (BOOL)reKey:(nullable NSData *)newKey aes256:(BOOL)aes256 {\n    string key;\n    if (newKey.length > 0) {\n        key = string((char *) newKey.bytes, newKey.length);\n    }\n    return m_mmkv->reKey(key, aes256);\n}\n\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {\n    [self  checkReSetCryptKey:cryptKey aes256:NO];\n}\n\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 {\n    if (cryptKey.length > 0) {\n        string key = string((char *) cryptKey.bytes, cryptKey.length);\n        m_mmkv->checkReSetCryptKey(&key, aes256);\n    } else {\n        m_mmkv->checkReSetCryptKey(nullptr, aes256);\n    }\n}\n\n#else\n\n- (nullable NSData *)cryptKey {\n    return nil;\n}\n\n- (BOOL)reKey:(nullable NSData *)newKey {\n    return NO;\n}\n\n- (BOOL)reKey:(nullable NSData *)newKey aes256:(BOOL)aes256 {\n    return NO;\n}\n\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {\n}\n\n- (void)checkReSetCryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 {\n}\n\n#endif // MMKV_DISABLE_CRYPT\n\n#pragma mark - set & get\n\n- (BOOL)setObject:(nullable NSObject<NSCoding> *)object forKey:(NSString *)key {\n    return m_mmkv->set(object, key);\n}\n\n- (BOOL)setObject:(nullable NSObject<NSCoding> *)object forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(object, key, seconds);\n}\n\n- (BOOL)setBool:(BOOL)value forKey:(NSString *)key {\n    return m_mmkv->set((bool) value, key);\n}\n\n- (BOOL)setBool:(BOOL)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set((bool) value, key, seconds);\n}\n\n- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setFloat:(float)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setFloat:(float)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setDouble:(double)value forKey:(NSString *)key {\n    return m_mmkv->set(value, key);\n}\n\n- (BOOL)setDouble:(double)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return m_mmkv->set(value, key, seconds);\n}\n\n- (BOOL)setString:(NSString *)value forKey:(NSString *)key {\n    return [self setObject:value forKey:key];\n}\n\n- (BOOL)setString:(NSString *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return [self setObject:value forKey:key expireDuration:seconds];\n}\n\n- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key {\n    return [self setObject:value forKey:key];\n}\n\n- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return [self setObject:value forKey:key expireDuration:seconds];\n}\n\n- (BOOL)setData:(NSData *)value forKey:(NSString *)key {\n    return [self setObject:value forKey:key];\n}\n\n- (BOOL)setData:(NSData *)value forKey:(NSString *)key expireDuration:(uint32_t)seconds {\n    return [self setObject:value forKey:key expireDuration:seconds];\n}\n\n- (id)getObjectOfClass:(Class)cls forKey:(NSString *)key {\n    return m_mmkv->getObject(key, cls);\n}\n\n- (BOOL)getBoolForKey:(NSString *)key {\n    return [self getBoolForKey:key defaultValue:FALSE hasValue:nil];\n}\n- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue {\n    return [self getBoolForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getBool(key, defaultValue, (bool *) hasValue);\n}\n\n- (int32_t)getInt32ForKey:(NSString *)key {\n    return [self getInt32ForKey:key defaultValue:0 hasValue:nil];\n}\n- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue {\n    return [self getInt32ForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getInt32(key, defaultValue, (bool *) hasValue);\n}\n\n- (uint32_t)getUInt32ForKey:(NSString *)key {\n    return [self getUInt32ForKey:key defaultValue:0 hasValue:nil];\n}\n- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue {\n    return [self getUInt32ForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getUInt32(key, defaultValue, (bool *) hasValue);\n}\n\n- (int64_t)getInt64ForKey:(NSString *)key {\n    return [self getInt64ForKey:key defaultValue:0 hasValue:nil];\n}\n- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue {\n    return [self getInt64ForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getInt64(key, defaultValue, (bool *) hasValue);\n}\n\n- (uint64_t)getUInt64ForKey:(NSString *)key {\n    return [self getUInt64ForKey:key defaultValue:0 hasValue:nil];\n}\n- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue {\n    return [self getUInt64ForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getUInt64(key, defaultValue, (bool *) hasValue);\n}\n\n- (float)getFloatForKey:(NSString *)key {\n    return [self getFloatForKey:key defaultValue:0 hasValue:nil];\n}\n- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue {\n    return [self getFloatForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getFloat(key, defaultValue, (bool *) hasValue);\n}\n\n- (double)getDoubleForKey:(NSString *)key {\n    return [self getDoubleForKey:key defaultValue:0 hasValue:nil];\n}\n- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue {\n    return [self getDoubleForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    return m_mmkv->getDouble(key, defaultValue, (bool *) hasValue);\n}\n\n- (nullable NSString *)getStringForKey:(NSString *)key {\n    return [self getStringForKey:key defaultValue:nil hasValue:nil];\n}\n- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue {\n    return [self getStringForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    if (key.length <= 0) {\n        return defaultValue;\n    }\n    NSString *valueString = [self getObjectOfClass:NSString.class forKey:key];\n    if (!valueString) {\n        if (hasValue != nil) {\n            *hasValue = false;\n        }\n        valueString = defaultValue;\n    }\n    return valueString;\n}\n\n- (nullable NSDate *)getDateForKey:(NSString *)key {\n    return [self getDateForKey:key defaultValue:nil hasValue:nil];\n}\n- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue {\n    return [self getDateForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    if (key.length <= 0) {\n        return defaultValue;\n    }\n    NSDate *valueDate = [self getObjectOfClass:NSDate.class forKey:key];\n    if (!valueDate) {\n        if (hasValue != nil) {\n            *hasValue = false;\n        }\n        valueDate = defaultValue;\n    }\n    return valueDate;\n}\n\n- (nullable NSData *)getDataForKey:(NSString *)key {\n    return [self getDataForKey:key defaultValue:nil];\n}\n- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue {\n    return [self getDataForKey:key defaultValue:defaultValue hasValue:nil];\n}\n- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue hasValue:(MMKV_OUT BOOL *)hasValue {\n    if (key.length <= 0) {\n        return defaultValue;\n    }\n    NSData *valueData = [self getObjectOfClass:NSData.class forKey:key];\n    if (!valueData) {\n        if (hasValue != nil) {\n            *hasValue = false;\n        }\n        valueData = defaultValue;\n    }\n    return valueData;\n}\n\n- (size_t)getValueSizeForKey:(NSString *)key actualSize:(BOOL)actualSize {\n    return m_mmkv->getValueSize(key, actualSize);\n}\n\n- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer {\n    return m_mmkv->writeValueToBuffer(key, buffer.mutableBytes, static_cast<int32_t>(buffer.length));\n}\n\n#pragma mark - enumerate\n\n- (BOOL)containsKey:(NSString *)key {\n    return m_mmkv->containsKey(key);\n}\n\n- (size_t)count {\n    return m_mmkv->count();\n}\n\n- (size_t)totalSize {\n    return m_mmkv->totalSize();\n}\n\n- (size_t)actualSize {\n    return m_mmkv->actualSize();\n}\n\n+ (size_t)pageSize {\n    return mmkv::DEFAULT_MMAP_SIZE;\n}\n\n+ (NSString *)version {\n    return [NSString stringWithCString:MMKV_VERSION encoding:NSASCIIStringEncoding];\n}\n\n- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block {\n    m_mmkv->enumerateKeys(block);\n}\n\n- (NSArray *)allKeys {\n    return m_mmkv->allKeysObjC();\n}\n\n- (size_t)countNonExpiredKeys {\n    return m_mmkv->count(true);\n}\n\n- (NSArray *)allNonExpiredKeys {\n    return m_mmkv->allKeysObjC(true);\n}\n\n- (BOOL)enableAutoKeyExpire:(uint32_t)expiredInSeconds {\n    if (m_mmkv->isCompareBeforeSetEnabled()) {\n        MMKVWarning(\"enableCompareBeforeSet will be invalid when Expiration is on\");\n#if DEBUG\n        MMKV_ASSERT(0);\n#endif\n    }\n    return m_mmkv->enableAutoKeyExpire(expiredInSeconds);\n}\n\n- (BOOL)disableAutoKeyExpire {\n    return m_mmkv->disableAutoKeyExpire();\n}\n\n- (BOOL)enableCompareBeforeSet {\n    if (m_mmkv->isExpirationEnabled()) {\n        MMKVWarning(\"enableCompareBeforeSet is invalid when Expiration is on\");\n#if DEBUG\n        MMKV_ASSERT(0);\n#endif\n        return NO;\n    }\n    if (m_mmkv->isEncryptionEnabled()) {\n        MMKVWarning(\"enableCompareBeforeSet is invalid when key encryption is on\");\n#if DEBUG\n        MMKV_ASSERT(0);\n#endif\n        return NO;\n    }\n\n    return m_mmkv->enableCompareBeforeSet();\n}\n\n- (BOOL)disableCompareBeforeSet {\n    return m_mmkv->disableCompareBeforeSet();\n}\n\n- (void)removeValueForKey:(NSString *)key {\n    m_mmkv->removeValueForKey(key);\n}\n\n- (void)removeValuesForKeys:(NSArray *)arrKeys {\n    m_mmkv->removeValuesForKeys(arrKeys);\n}\n\n- (void)sync {\n    m_mmkv->sync(mmkv::MMKV_SYNC);\n}\n\n- (void)async {\n    m_mmkv->sync(mmkv::MMKV_ASYNC);\n}\n\n- (void)checkContentChanged {\n    m_mmkv->checkContentChanged();\n}\n\n- (BOOL)isMultiProcess {\n    return m_mmkv->isMultiProcess();\n}\n\n- (BOOL)isReadOnly {\n    return m_mmkv->isReadOnly();\n}\n\n- (mmkv::MMKV *)cppInstance {\n    return m_mmkv;\n}\n\n+ (void)onAppTerminate {\n    g_lock->lock();\n\n    // make sure no further call will go into m_mmkv\n    [g_instanceDic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, MMKV *_Nonnull mmkv, BOOL *_Nonnull stop) {\n        mmkv->m_mmkv = nullptr;\n    }];\n    [g_instanceDic release];\n    g_instanceDic = nil;\n\n    [g_basePath release];\n    g_basePath = nil;\n\n    [g_groupPath release];\n    g_groupPath = nil;\n\n    mmkv::MMKV::onExit();\n\n    g_lock->unlock();\n    delete g_lock;\n    g_lock = nullptr;\n}\n\nstatic bool g_isAutoCleanUpEnabled = false;\nstatic uint32_t g_maxIdleMS = 0;\nconstexpr int DoCleanUpDurationMS = 2 * 1000;\nstatic dispatch_source_t g_autoCleanUpTimer = nullptr;\nstatic AutoCleanInfoQueue_t g_cleanQueue = {};\n\n+ (void)enableAutoCleanUp:(uint32_t)maxIdleMinutes {\n    MMKVInfo(\"enable auto clean up with maxIdleMinutes:%zu\", maxIdleMinutes);\n    SCOPED_LOCK(g_lock);\n\n    g_isAutoCleanUpEnabled = true;\n    g_maxIdleMS = maxIdleMinutes * 60 * 1000;\n\n    if (!g_autoCleanUpTimer) {\n        g_autoCleanUpTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));\n        dispatch_source_set_event_handler(g_autoCleanUpTimer, ^{\n            [MMKV tryAutoCleanUpInstances];\n        });\n    } else {\n        dispatch_suspend(g_autoCleanUpTimer);\n    }\n    dispatch_source_set_timer(g_autoCleanUpTimer,\n                              dispatch_time(DISPATCH_TIME_NOW, g_maxIdleMS * NSEC_PER_MSEC),\n                              g_maxIdleMS * NSEC_PER_MSEC,\n                              0);\n    dispatch_resume(g_autoCleanUpTimer);\n}\n\n+ (void)disableAutoCleanUp {\n    MMKVInfo(\"disable auto clean up\");\n    SCOPED_LOCK(g_lock);\n\n    g_isAutoCleanUpEnabled = false;\n    g_maxIdleMS = 0;\n\n    if (g_autoCleanUpTimer) {\n        dispatch_source_cancel(g_autoCleanUpTimer);\n        dispatch_release(g_autoCleanUpTimer);\n        g_autoCleanUpTimer = nullptr;\n    }\n}\n\n/// clean up mmkv instance that not been access lately\n///   There are two phases of auto clean:\n///   1. check-cleanup phase with longer duration: CleanUpDurationMS\n///   2. do-cleanup phase with faster duration: DoCleanUpDurationSecends\n+ (void)tryAutoCleanUpInstances {\n    SCOPED_LOCK(g_lock);\n\n#if defined(MMKV_IOS) && !(TARGET_OS_MACCATALYST)\n    if (mmkv::MMKV::isInBackground()) {\n        MMKVInfo(\"don't cleanup in background, might just wakeup from suspend\");\n        return;\n    }\n#endif\n\n    const uint64_t now = llround([NSDate timeIntervalSinceReferenceDate] * 1000);\n\n    // mark that we were once in do-cleanup phase and maybe needs reset timer\n    bool inDoCleanupPhase = !g_cleanQueue.empty();\n    while (!g_cleanQueue.empty()) {\n        auto &info = g_cleanQueue.top();\n        auto mmkv = (MMKV *) [g_instanceDic objectForKey:info.m_key];\n        if (mmkv) {\n            if (mmkv->m_mmkv->try_lock_thread()) {\n                // check m_lastAccessTime again\n                if (mmkv->m_lastAccessTime + g_maxIdleMS <= now && mmkv.retainCount == 1) {\n                    // clean one at a time, prevent from holding g_lock for too long\n                    @autoreleasepool {\n                        MMKVInfo(\"auto cleanup mmkv [%@], m_time: %llu\", info.m_key, info.m_time);\n                        [g_instanceDic removeObjectForKey:info.m_key];\n                        g_cleanQueue.pop();\n                        // enumerate & check again if hit the bottom\n                        if (g_cleanQueue.empty()) {\n                            break;\n                        }\n                        return;\n                    }\n                }\n                MMKVInfo(\"ignore auto cleanup mmkv [%@], m_lastAccessTime: %llu\", info.m_key, mmkv->m_lastAccessTime);\n                mmkv->m_mmkv->unlock_thread();\n            }\n            // if we reach here, it's must have been access by someone, ignore it\n            g_cleanQueue.pop();\n        } else {\n            // someone else has closed it\n            MMKVInfo(\"ignore already closed mmkv [%@]\", info.m_key);\n            g_cleanQueue.pop();\n        }\n    }\n\n    [g_instanceDic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {\n        auto mmkv = (MMKV *) obj;\n        if (mmkv->m_lastAccessTime + g_maxIdleMS <= now && mmkv.retainCount == 1) {\n            MMKVInfo(\"adding to cleanup queue mmkv [%@], m_lastAccessTime: %llu\", key, mmkv->m_lastAccessTime);\n            g_cleanQueue.emplace((NSString *) key, mmkv->m_lastAccessTime);\n        }\n    }];\n\n    if (g_autoCleanUpTimer) {\n        if (!inDoCleanupPhase && !g_cleanQueue.empty()) {\n            MMKVInfo(\"switch to do-cleanup phase with faster duration\");\n            dispatch_source_set_timer(g_autoCleanUpTimer,\n                                      dispatch_time(DISPATCH_TIME_NOW, DoCleanUpDurationMS * NSEC_PER_MSEC),\n                                      DoCleanUpDurationMS * NSEC_PER_MSEC,\n                                      60 * NSEC_PER_SEC);\n        } else if (inDoCleanupPhase && g_cleanQueue.empty()) {\n            MMKVInfo(\"switch back to check-cleanup phase with longer duration\");\n            dispatch_source_set_timer(g_autoCleanUpTimer,\n                                      dispatch_time(DISPATCH_TIME_NOW, g_maxIdleMS * NSEC_PER_MSEC),\n                                      g_maxIdleMS * NSEC_PER_MSEC,\n                                      60 * NSEC_PER_SEC);\n        }\n    }\n}\n\n+ (NSString *)mmkvBasePath {\n    if (g_basePath.length > 0) {\n        return g_basePath;\n    }\n\n#if TARGET_OS_TV\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);\n#else\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);\n#endif\n    NSString *documentPath = (NSString *) [paths firstObject];\n    if ([documentPath length] > 0) {\n        g_basePath = [[documentPath stringByAppendingPathComponent:@\"mmkv\"] retain];\n        return g_basePath;\n    } else {\n        return @\"\";\n    }\n}\n\n+ (void)setMMKVBasePath:(NSString *)basePath {\n    if (basePath.length > 0) {\n        [g_basePath release];\n        g_basePath = [basePath retain];\n        [MMKV initializeMMKV:basePath];\n\n        // still warn about it\n        g_hasCalledInitializeMMKV = NO;\n\n        MMKVInfo(\"set MMKV base path to: %@\", g_basePath);\n    }\n}\n\n+ (NSString *)mmkvGroupPath {\n    return g_groupPath;\n}\n\n- (void)setValue:(nullable id)value forKey:(NSString *)key {\n    [super setValue:value forKey:key];\n}\n\n- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath {\n    [super setValue:value forKeyPath:keyPath];\n}\n\nstatic NSString *md5(NSString *value) {\n    uint8_t md[CC_MD5_DIGEST_LENGTH] = {};\n    char tmp[3] = {}, buf[33] = {};\n    auto data = [value dataUsingEncoding:NSUTF8StringEncoding];\n    CC_MD5((uint8_t *) data.bytes, data.length, md);\n    for (auto ch : md) {\n        snprintf(tmp, sizeof(tmp), \"%2.2x\", ch);\n        strcat(buf, tmp);\n    }\n    return [NSString stringWithCString:buf encoding:NSASCIIStringEncoding];\n}\n\n+ (NSString *)mmapKeyWithMMapID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {\n    NSString *string = nil;\n    if ([rootPath length] > 0 && [rootPath isEqualToString:[MMKV mmkvBasePath]] == NO) {\n        string = md5([rootPath stringByAppendingPathComponent:mmapID]);\n    } else {\n        string = mmapID;\n    }\n    MMKVDebug(\"mmapKey: %@\", string);\n    return string;\n}\n\n+ (BOOL)isFileValid:(NSString *)mmapID {\n    return [self isFileValid:mmapID rootPath:nil];\n}\n\n+ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path {\n    if (mmapID.length > 0) {\n        if (path.length > 0) {\n            string rootPath(path.UTF8String);\n            return mmkv::MMKV::isFileValid(mmapID.UTF8String, &rootPath);\n        } else {\n            return mmkv::MMKV::isFileValid(mmapID.UTF8String, nullptr);\n        }\n    }\n    return NO;\n}\n\n#pragma mark - backup & restore\n\n+ (BOOL)backupOneMMKV:(NSString *)mmapID rootPath:(nullable NSString *)path toDirectory:(NSString *)dstDir {\n    if (path.length > 0) {\n        string rootPath(path.UTF8String);\n        return mmkv::MMKV::backupOneToDirectory(mmapID.UTF8String, dstDir.UTF8String, &rootPath);\n    }\n    return mmkv::MMKV::backupOneToDirectory(mmapID.UTF8String, dstDir.UTF8String);\n}\n\n+ (BOOL)restoreOneMMKV:(NSString *)mmapID rootPath:(nullable NSString *)path fromDirectory:(NSString *)srcDir {\n    if (path.length > 0) {\n        string rootPath(path.UTF8String);\n        return mmkv::MMKV::restoreOneFromDirectory(mmapID.UTF8String, srcDir.UTF8String, &rootPath);\n    }\n    return mmkv::MMKV::restoreOneFromDirectory(mmapID.UTF8String, srcDir.UTF8String);\n}\n\n+ (size_t)backupAll:(nullable NSString *)path toDirectory:(NSString *)dstDir {\n    if (path.length > 0) {\n        string rootPath(path.UTF8String);\n        return mmkv::MMKV::backupAllToDirectory(dstDir.UTF8String, &rootPath);\n    }\n    return mmkv::MMKV::backupAllToDirectory(dstDir.UTF8String);\n}\n\n+ (size_t)restoreAll:(nullable NSString *)path fromDirectory:(NSString *)srcDir {\n    if (path.length > 0) {\n        string rootPath(path.UTF8String);\n        return mmkv::MMKV::restoreAllFromDirectory(srcDir.UTF8String, &rootPath);\n    }\n    return mmkv::MMKV::restoreAllFromDirectory(srcDir.UTF8String);\n}\n\n+ (BOOL)backupMultiProcessMMKV:(NSString *)mmapID toDirectory:(NSString *)dstDir {\n    if (!g_groupPath) {\n        MMKVError(\"Backup a multi-process MMKV [%@] without setting groupDir makes no sense\", mmapID);\n        MMKV_ASSERT(0);\n    }\n    return [MMKV backupOneMMKV:mmapID rootPath:g_groupPath toDirectory:dstDir];\n}\n\n+ (BOOL)restoreMultiProcessMMKV:(NSString *)mmapID fromDirectory:(NSString *)srcDir {\n    if (!g_groupPath) {\n        MMKVError(\"Restore a multi-process MMKV [%@] without setting groupDir makes no sense\", mmapID);\n        MMKV_ASSERT(0);\n    }\n    return [MMKV restoreOneMMKV:mmapID rootPath:g_groupPath fromDirectory:srcDir];\n}\n\n+ (size_t)backupAllMultiProcessToDirectory:(NSString *)dstDir {\n    if (!g_groupPath) {\n        MMKVError(\"Backup multi-process MMKV without setting groupDir makes no sense.\");\n        MMKV_ASSERT(0);\n    }\n    return [MMKV backupAll:g_groupPath toDirectory:dstDir];\n}\n\n+ (size_t)restoreAllMultiProcessFromDirectory:(NSString *)srcDir {\n    if (!g_groupPath) {\n        MMKVError(\"Restore multi-process MMKV without setting groupDir makes no sense.\");\n        MMKV_ASSERT(0);\n    }\n    return [MMKV restoreAll:g_groupPath fromDirectory:srcDir];\n}\n\n#pragma mark - handler\n\n+ (void)registerHandler:(id<MMKVHandler>)handler {\n    SCOPED_LOCK(g_lock);\n    [g_callbackHandler release];\n    g_callbackHandler = [handler retain];\n\n    if (g_callbackHandler) {\n        if ([g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {\n            g_isLogRedirecting = true;\n        }\n        mmkv::MMKV::registerHandler(&g_cppHandler);\n    } else {\n        g_isLogRedirecting = false;\n        mmkv::MMKV::unRegisterHandler();\n    }\n}\n\n+ (void)unregiserHandler {\n    SCOPED_LOCK(g_lock);\n\n    g_isLogRedirecting = false;\n    [g_callbackHandler release];\n    g_callbackHandler = nil;\n\n    mmkv::MMKV::unRegisterHandler();\n}\n\n+ (void)setLogLevel:(MMKVLogLevel)logLevel {\n    mmkv::MMKV::setLogLevel((mmkv::MMKVLogLevel) logLevel);\n}\n\n- (uint64_t)migrateFromUserDefaultsDictionaryRepresentation:(NSDictionary *)dic {\n    if (dic.count <= 0) {\n        MMKVInfo(\"migrate data fail, dic is nil or empty\");\n        return 0;\n    }\n    @autoreleasepool {\n        __block uint64_t count = 0;\n        [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {\n            if ([key isKindOfClass:[NSString class]]) {\n                NSString *stringKey = key;\n                if ([MMKV tranlateData:obj key:stringKey kv:self]) {\n                    count++;\n                }\n            } else {\n                MMKVWarning(\"unknown type of key:%@\", key);\n            }\n        }];\n        return count;\n    }\n}\n\n+ (BOOL)tranlateData:(id)obj key:(NSString *)key kv:(MMKV *)kv {\n    if ([obj isKindOfClass:[NSString class]]) {\n        return [kv setString:obj forKey:key];\n    } else if ([obj isKindOfClass:[NSData class]]) {\n        return [kv setData:obj forKey:key];\n    } else if ([obj isKindOfClass:[NSDate class]]) {\n        return [kv setDate:obj forKey:key];\n    } else if ([obj isKindOfClass:[NSNumber class]]) {\n        NSNumber *num = obj;\n        CFNumberType numberType = CFNumberGetType((CFNumberRef) obj);\n        switch (numberType) {\n            case kCFNumberCharType:\n            case kCFNumberSInt8Type:\n            case kCFNumberSInt16Type:\n            case kCFNumberSInt32Type:\n            case kCFNumberIntType:\n            case kCFNumberShortType:\n                return [kv setInt32:num.intValue forKey:key];\n            case kCFNumberSInt64Type:\n            case kCFNumberLongType:\n            case kCFNumberNSIntegerType:\n            case kCFNumberLongLongType:\n                return [kv setInt64:num.longLongValue forKey:key];\n            case kCFNumberFloat32Type:\n                return [kv setFloat:num.floatValue forKey:key];\n            case kCFNumberFloat64Type:\n            case kCFNumberDoubleType:\n                return [kv setDouble:num.doubleValue forKey:key];\n            default:\n                MMKVWarning(\"unknown number type:%ld, key:%@\", (long) numberType, key);\n                return NO;\n        }\n    } else if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {\n        return [kv setObject:obj forKey:key];\n    } else {\n        MMKVWarning(\"unknown type of key:%@\", key);\n    }\n    return NO;\n}\n\n+ (BOOL)removeStorage:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(removeStorage(for:rootPath:)) {\n    SCOPED_LOCK(g_lock);\n    NSString *kvKey = [MMKV mmapKeyWithMMapID:mmapID rootPath:path];\n    MMKV *kv = [[g_instanceDic objectForKey:kvKey] retain];\n    if (kv != nil) {\n        [g_instanceDic removeObjectForKey:kvKey];\n        if (kv.retainCount > 1) {\n            MMKVWarning(\"There's still reference on this kv: %@\", mmapID);\n            // we can't wait for dealloc\n            kv->m_mmkv = nullptr;\n        }\n        [kv release];\n    }\n\n    if (mmapID.length > 0) {\n        if (path.length > 0) {\n            string rootPath(path.UTF8String);\n            return mmkv::MMKV::removeStorage(mmapID.UTF8String, &rootPath);\n        } else {\n            return mmkv::MMKV::removeStorage(mmapID.UTF8String, nullptr);\n        }\n    }\n    return NO;\n}\n\n+ (BOOL)removeStorage:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(removeStorage(for:mode:)) {\n    auto rootPath = (mode & MMKVSingleProcess) ? nil : g_groupPath;\n    return [self removeStorage:mmapID rootPath:rootPath];\n}\n\n+ (BOOL)checkExist:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(checkExist(for:rootPath:)) {\n    if (mmapID.length > 0) {\n        if (path.length > 0) {\n            string rootPath(path.UTF8String);\n            return mmkv::MMKV::checkExist(mmapID.UTF8String, &rootPath);\n        } else {\n            return mmkv::MMKV::checkExist(mmapID.UTF8String, nullptr);\n        }\n    }\n    return NO;\n}\n\n+ (BOOL)checkExist:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(checkExist(for:mode:)) {\n    auto rootPath = (mode & MMKVSingleProcess) ? nil : g_groupPath;\n    return [self checkExist:mmapID rootPath:rootPath];\n}\n\n+ (MMKVNameSpace *)nameSpace:(NSString *)rootPath {\n    mmkv::MMKV::nameSpace(rootPath.UTF8String);\n    return [[[MMKVNameSpace alloc] initWith:rootPath] autorelease];\n}\n\n+ (MMKVNameSpace *)defaultNameSpace {\n    if (!g_hasCalledInitializeMMKV) {\n        MMKVWarning(\"MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods\");\n        return nil;\n    }\n    return [[[MMKVNameSpace alloc] initWith:[self mmkvBasePath]] autorelease];\n}\n\n@end\n\n#pragma  mark - MMKVNameSpace\n\n@implementation MMKVNameSpace {\n    NSString *_rootPath;\n    string *m_rootPath;\n}\n\n- (instancetype)initWith:(NSString *)path {\n    if (self = [super init]) {\n        _rootPath = [path retain];\n        m_rootPath = nullptr;\n    }\n    return self;\n}\n\n- (void) dealloc {\n    [_rootPath release];\n    _rootPath = nil;\n\n    delete m_rootPath;\n    m_rootPath = nullptr;\n\n    [super dealloc];\n}\n\n- (NSString *)rootPath {\n    return _rootPath;\n}\n\n- (string *)strRootPath {\n    if (!m_rootPath) {\n        m_rootPath = new string(_rootPath.UTF8String);\n    }\n    return m_rootPath;\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID {\n    return [MMKV doGetWithID:mmapID cryptKey:nil aes256:NO rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(NSString *)mmapID config:(MMKVConfig)config NS_SWIFT_NAME(init(mmapID:config:)) {\n    config.rootPath = _rootPath;\n    return [MMKV doGetWithID:mmapID config:config];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID mode:(MMKVMode)mode {\n    return [MMKV doGetWithID:mmapID cryptKey:nil aes256:NO rootPath:_rootPath mode:mode expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:_rootPath mode:mode expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 mode:(MMKVMode)mode {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:_rootPath mode:mode expectedCapacity:0];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV doGetWithID:mmapID cryptKey:nil aes256:NO rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:NO rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n- (nullable MMKV *)mmkvWithID:(nonnull NSString *)mmapID cryptKey:(nullable NSData *)cryptKey aes256:(BOOL)aes256 expectedCapacity:(size_t)expectedCapacity {\n    return [MMKV doGetWithID:mmapID cryptKey:cryptKey aes256:aes256 rootPath:_rootPath mode:MMKVSingleProcess expectedCapacity:expectedCapacity];\n}\n\n- (BOOL)backupOneMMKV:(NSString *)mmapID toDirectory:(NSString *)dstDir {\n    return mmkv::MMKV::backupOneToDirectory(mmapID.UTF8String, dstDir.UTF8String, [self strRootPath]);\n}\n\n- (BOOL)restoreOneMMKV:(NSString *)mmapID fromDirectory:(NSString *)srcDir {\n    return mmkv::MMKV::restoreOneFromDirectory(mmapID.UTF8String, srcDir.UTF8String, [self strRootPath]);\n}\n\n- (size_t)backupAllToDirectory:(NSString *)dstDir {\n    return mmkv::MMKV::backupAllToDirectory(dstDir.UTF8String, [self strRootPath]);\n}\n\n- (size_t)restoreAllFromDirectory:(NSString *)srcDir {\n    return mmkv::MMKV::restoreAllFromDirectory(srcDir.UTF8String, [self strRootPath]);\n}\n\n- (BOOL)isFileValid:(NSString *)mmapID {\n    return mmkv::MMKV::isFileValid(mmapID.UTF8String, [self strRootPath]);\n}\n\n- (BOOL)removeStorage:(NSString *)mmapID {\n    return mmkv::MMKV::removeStorage(mmapID.UTF8String, [self strRootPath]);\n}\n\n- (BOOL)checkExist:(NSString *)mmapID {\n    return mmkv::MMKV::checkExist(mmapID.UTF8String, [self strRootPath]);\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tCB042EEF2AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CB042EEE2AB9970500F8DD8C /* AutoCleanInfo.hpp */; };\n\t\tCB042EF02AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CB042EEE2AB9970500F8DD8C /* AutoCleanInfo.hpp */; };\n\t\tCB042EF12AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CB042EEE2AB9970500F8DD8C /* AutoCleanInfo.hpp */; };\n\t\tCB0DCC78242B5AAF009AFE59 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBC1A90320DA94A000AD5087 /* libz.tbd */; };\n\t\tCB1E0645242B84D800F2FD42 /* libMMKV.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD4952046984F00931B5F /* libMMKV.mm */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB1E0647242B84D800F2FD42 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBC1A90320DA94A000AD5087 /* libz.tbd */; };\n\t\tCB1E0648242B84D800F2FD42 /* libMMKVCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB6F6C0123ACCF47000351EA /* libMMKVCore.a */; };\n\t\tCB1E0649242B84D800F2FD42 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4CC2046ACBC00931B5F /* Foundation.framework */; };\n\t\tCB1E064B242B84D800F2FD42 /* MMKVHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = CBDF328F2192C0000028DB4D /* MMKVHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCB1E064C242B84D800F2FD42 /* MMKV.h in Headers */ = {isa = PBXBuildFile; fileRef = CB1FD4942046984F00931B5F /* MMKV.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCB1FD4962046984F00931B5F /* libMMKV.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD4952046984F00931B5F /* libMMKV.mm */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCB1FD4972046984F00931B5F /* MMKV.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB1FD4942046984F00931B5F /* MMKV.h */; };\n\t\tCB1FD4CD2046ACBC00931B5F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4CC2046ACBC00931B5F /* Foundation.framework */; };\n\t\tCB6F6C0223ACCF53000351EA /* libMMKVCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB6F6C0123ACCF47000351EA /* libMMKVCore.a */; };\n\t\tCB6F6C0523ACCF62000351EA /* libMMKVCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB6F6C0123ACCF47000351EA /* libMMKVCore.a */; };\n\t\tCBC1A90220DA948A00AD5087 /* libMMKV.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD4952046984F00931B5F /* libMMKV.mm */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBC1A90620DA94B900AD5087 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4CC2046ACBC00931B5F /* Foundation.framework */; };\n\t\tCBC1A90C20DA97DF00AD5087 /* MMKV.h in Headers */ = {isa = PBXBuildFile; fileRef = CB1FD4942046984F00931B5F /* MMKV.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCBDF32902192C0000028DB4D /* MMKVHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = CBDF328F2192C0000028DB4D /* MMKVHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCBDF32912192C0340028DB4D /* MMKVHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CBDF328F2192C0000028DB4D /* MMKVHandler.h */; };\n\t\tCBF19040243D706D001C82ED /* libMMKV.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD4952046984F00931B5F /* libMMKV.mm */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\tCBF19042243D706D001C82ED /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBC1A90320DA94A000AD5087 /* libz.tbd */; };\n\t\tCBF19044243D706D001C82ED /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4CC2046ACBC00931B5F /* Foundation.framework */; };\n\t\tCBF19046243D706D001C82ED /* MMKVHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = CBDF328F2192C0000028DB4D /* MMKVHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCBF19047243D706D001C82ED /* MMKV.h in Headers */ = {isa = PBXBuildFile; fileRef = CB1FD4942046984F00931B5F /* MMKV.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tCBF1907F243D7627001C82ED /* libMMKVCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CBF19079243D70FD001C82ED /* libMMKVCore.a */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tCB1E0643242B84D800F2FD42 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB9563D723AB2D9500ACCD39;\n\t\t\tremoteInfo = Core;\n\t\t};\n\t\tCB6F6C0023ACCF47000351EA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CB9563D823AB2D9500ACCD39;\n\t\t\tremoteInfo = Core;\n\t\t};\n\t\tCB6F6C0323ACCF58000351EA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB9563D723AB2D9500ACCD39;\n\t\t\tremoteInfo = Core;\n\t\t};\n\t\tCB6F6C0623ACCF65000351EA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB9563D723AB2D9500ACCD39;\n\t\t\tremoteInfo = Core;\n\t\t};\n\t\tCBF19078243D70FD001C82ED /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CBF19076243D70BA001C82ED;\n\t\t\tremoteInfo = MMKVWatchCore;\n\t\t};\n\t\tF69177D32E8399830075A17A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CBF1904E243D70BA001C82ED;\n\t\t\tremoteInfo = MMKVWatchCore;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tCB1FD48F2046984F00931B5F /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = include/MMKV;\n\t\t\tdstSubfolderSpec = 16;\n\t\t\tfiles = (\n\t\t\t\tCBDF32912192C0340028DB4D /* MMKVHandler.h in CopyFiles */,\n\t\t\t\tCB1FD4972046984F00931B5F /* MMKV.h in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tCB042EEE2AB9970500F8DD8C /* AutoCleanInfo.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AutoCleanInfo.hpp; sourceTree = \"<group>\"; };\n\t\tCB1E0651242B84D800F2FD42 /* MMKVAppExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MMKVAppExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB1FD4912046984F00931B5F /* libMMKV.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMMKV.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB1FD4942046984F00931B5F /* MMKV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKV.h; sourceTree = \"<group>\"; };\n\t\tCB1FD4952046984F00931B5F /* libMMKV.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = libMMKV.mm; sourceTree = \"<group>\"; };\n\t\tCB1FD4CC2046ACBC00931B5F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };\n\t\tCB1FD4CE2046ACC100931B5F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };\n\t\tCB6F6BFC23ACCF47000351EA /* Core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = Core.xcodeproj; path = ../../Core/Core.xcodeproj; sourceTree = \"<group>\"; };\n\t\tCBC1A8F620DA946200AD5087 /* MMKV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MMKV.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCBC1A90320DA94A000AD5087 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };\n\t\tCBC1A90A20DA95E100AD5087 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCBDF328F2192C0000028DB4D /* MMKVHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKVHandler.h; sourceTree = \"<group>\"; };\n\t\tCBF1904C243D706D001C82ED /* MMKVWatchExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MMKVWatchExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tCB1E0646242B84D800F2FD42 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1E0647242B84D800F2FD42 /* libz.tbd in Frameworks */,\n\t\t\t\tCB1E0648242B84D800F2FD42 /* libMMKVCore.a in Frameworks */,\n\t\t\t\tCB1E0649242B84D800F2FD42 /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB1FD48E2046984F00931B5F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB6F6C0223ACCF53000351EA /* libMMKVCore.a in Frameworks */,\n\t\t\t\tCB1FD4CD2046ACBC00931B5F /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBC1A8F220DA946200AD5087 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB0DCC78242B5AAF009AFE59 /* libz.tbd in Frameworks */,\n\t\t\t\tCB6F6C0523ACCF62000351EA /* libMMKVCore.a in Frameworks */,\n\t\t\t\tCBC1A90620DA94B900AD5087 /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19041243D706D001C82ED /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF1907F243D7627001C82ED /* libMMKVCore.a in Frameworks */,\n\t\t\t\tCBF19042243D706D001C82ED /* libz.tbd in Frameworks */,\n\t\t\t\tCBF19044243D706D001C82ED /* Foundation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tCB1FD4882046984F00931B5F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB6F6BFC23ACCF47000351EA /* Core.xcodeproj */,\n\t\t\t\tCB1FD4932046984F00931B5F /* MMKV */,\n\t\t\t\tCB1FD4922046984F00931B5F /* Products */,\n\t\t\t\tCB1FD4C92046ACB200931B5F /* Frameworks */,\n\t\t\t);\n\t\t\tindentWidth = 4;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD4922046984F00931B5F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD4912046984F00931B5F /* libMMKV.a */,\n\t\t\t\tCBC1A8F620DA946200AD5087 /* MMKV.framework */,\n\t\t\t\tCB1E0651242B84D800F2FD42 /* MMKVAppExtension.framework */,\n\t\t\t\tCBF1904C243D706D001C82ED /* MMKVWatchExtension.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD4932046984F00931B5F /* MMKV */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB042EEE2AB9970500F8DD8C /* AutoCleanInfo.hpp */,\n\t\t\t\tCB1FD4942046984F00931B5F /* MMKV.h */,\n\t\t\t\tCB1FD4952046984F00931B5F /* libMMKV.mm */,\n\t\t\t\tCBDF328F2192C0000028DB4D /* MMKVHandler.h */,\n\t\t\t\tCBC1A90720DA959400AD5087 /* Resources */,\n\t\t\t);\n\t\t\tpath = MMKV;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD4C92046ACB200931B5F /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBC1A90320DA94A000AD5087 /* libz.tbd */,\n\t\t\t\tCB1FD4CE2046ACC100931B5F /* UIKit.framework */,\n\t\t\t\tCB1FD4CC2046ACBC00931B5F /* Foundation.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB6F6BFD23ACCF47000351EA /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB6F6C0123ACCF47000351EA /* libMMKVCore.a */,\n\t\t\t\tCBF19079243D70FD001C82ED /* libMMKVCore.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBC1A90720DA959400AD5087 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBC1A90A20DA95E100AD5087 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tCB1E064A242B84D800F2FD42 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB042EF02AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */,\n\t\t\t\tCB1E064B242B84D800F2FD42 /* MMKVHandler.h in Headers */,\n\t\t\t\tCB1E064C242B84D800F2FD42 /* MMKV.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBC1A8F320DA946200AD5087 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB042EEF2AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */,\n\t\t\t\tCBDF32902192C0000028DB4D /* MMKVHandler.h in Headers */,\n\t\t\t\tCBC1A90C20DA97DF00AD5087 /* MMKV.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19045243D706D001C82ED /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB042EF12AB9970500F8DD8C /* AutoCleanInfo.hpp in Headers */,\n\t\t\t\tCBF19046243D706D001C82ED /* MMKVHandler.h in Headers */,\n\t\t\t\tCBF19047243D706D001C82ED /* MMKV.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tCB1E0641242B84D800F2FD42 /* MMKVAppExtension */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB1E064E242B84D800F2FD42 /* Build configuration list for PBXNativeTarget \"MMKVAppExtension\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB1E0644242B84D800F2FD42 /* Sources */,\n\t\t\t\tCB1E0646242B84D800F2FD42 /* Frameworks */,\n\t\t\t\tCB1E064A242B84D800F2FD42 /* Headers */,\n\t\t\t\tCB1E064D242B84D800F2FD42 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB1E0642242B84D800F2FD42 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVAppExtension;\n\t\t\tproductName = MMKV;\n\t\t\tproductReference = CB1E0651242B84D800F2FD42 /* MMKVAppExtension.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tCB1FD4902046984F00931B5F /* MMKV Static */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB1FD49A2046984F00931B5F /* Build configuration list for PBXNativeTarget \"MMKV Static\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB1FD48F2046984F00931B5F /* CopyFiles */,\n\t\t\t\tCB1FD48D2046984F00931B5F /* Sources */,\n\t\t\t\tCB1FD48E2046984F00931B5F /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB6F6C0423ACCF58000351EA /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = \"MMKV Static\";\n\t\t\tproductName = MMKV;\n\t\t\tproductReference = CB1FD4912046984F00931B5F /* libMMKV.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tCBC1A8F520DA946200AD5087 /* MMKV */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBC1A8FB20DA946200AD5087 /* Build configuration list for PBXNativeTarget \"MMKV\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBC1A8F120DA946200AD5087 /* Sources */,\n\t\t\t\tCBC1A8F220DA946200AD5087 /* Frameworks */,\n\t\t\t\tCBC1A8F320DA946200AD5087 /* Headers */,\n\t\t\t\tCBC1A8F420DA946200AD5087 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB6F6C0723ACCF65000351EA /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKV;\n\t\t\tproductName = MMKV;\n\t\t\tproductReference = CBC1A8F620DA946200AD5087 /* MMKV.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tCBF1903C243D706D001C82ED /* MMKVWatchExtension */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBF19049243D706D001C82ED /* Build configuration list for PBXNativeTarget \"MMKVWatchExtension\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBF1903F243D706D001C82ED /* Sources */,\n\t\t\t\tCBF19041243D706D001C82ED /* Frameworks */,\n\t\t\t\tCBF19045243D706D001C82ED /* Headers */,\n\t\t\t\tCBF19048243D706D001C82ED /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF69177D42E8399830075A17A /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVWatchExtension;\n\t\t\tproductName = MMKV;\n\t\t\tproductReference = CBF1904C243D706D001C82ED /* MMKVWatchExtension.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tCB1FD4892046984F00931B5F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastUpgradeCheck = 1530;\n\t\t\t\tORGANIZATIONNAME = Lingol;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tCB1E0641242B84D800F2FD42 = {\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tCB1FD4902046984F00931B5F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tCBC1A8F520DA946200AD5087 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tCBF1903C243D706D001C82ED = {\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = CB1FD48C2046984F00931B5F /* Build configuration list for PBXProject \"MMKV\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = CB1FD4882046984F00931B5F;\n\t\t\tproductRefGroup = CB1FD4922046984F00931B5F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = CB6F6BFD23ACCF47000351EA /* Products */;\n\t\t\t\t\tProjectRef = CB6F6BFC23ACCF47000351EA /* Core.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tCB1FD4902046984F00931B5F /* MMKV Static */,\n\t\t\t\tCBC1A8F520DA946200AD5087 /* MMKV */,\n\t\t\t\tCB1E0641242B84D800F2FD42 /* MMKVAppExtension */,\n\t\t\t\tCBF1903C243D706D001C82ED /* MMKVWatchExtension */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\tCB6F6C0123ACCF47000351EA /* libMMKVCore.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libMMKVCore.a;\n\t\t\tremoteRef = CB6F6C0023ACCF47000351EA /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tCBF19079243D70FD001C82ED /* libMMKVCore.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libMMKVCore.a;\n\t\t\tremoteRef = CBF19078243D70FD001C82ED /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tCB1E064D242B84D800F2FD42 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBC1A8F420DA946200AD5087 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19048243D706D001C82ED /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tCB1E0644242B84D800F2FD42 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1E0645242B84D800F2FD42 /* libMMKV.mm in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB1FD48D2046984F00931B5F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1FD4962046984F00931B5F /* libMMKV.mm in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBC1A8F120DA946200AD5087 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBC1A90220DA948A00AD5087 /* libMMKV.mm in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF1903F243D706D001C82ED /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF19040243D706D001C82ED /* libMMKV.mm in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tCB1E0642242B84D800F2FD42 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = Core;\n\t\t\ttargetProxy = CB1E0643242B84D800F2FD42 /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB6F6C0423ACCF58000351EA /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = Core;\n\t\t\ttargetProxy = CB6F6C0323ACCF58000351EA /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB6F6C0723ACCF65000351EA /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = Core;\n\t\t\ttargetProxy = CB6F6C0623ACCF65000351EA /* PBXContainerItemProxy */;\n\t\t};\n\t\tF69177D42E8399830075A17A /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = MMKVWatchCore;\n\t\t\ttargetProxy = F69177D32E8399830075A17A /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin XCBuildConfiguration section */\n\t\tCB1E064F242B84D800F2FD42 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\tMMKV_IOS_EXTENSION,\n\t\t\t\t);\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVAppExtension;\n\t\t\t\tPRODUCT_NAME = MMKVAppExtension;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB1E0650242B84D800F2FD42 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"NDEBUG=1\",\n\t\t\t\t\tMMKV_IOS_EXTENSION,\n\t\t\t\t);\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVAppExtension;\n\t\t\t\tPRODUCT_NAME = MMKVAppExtension;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB1FD4982046984F00931B5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMARKETING_VERSION = 2.4.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx appletvos appletvsimulator\";\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB1FD4992046984F00931B5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = \"NDEBUG=1\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMARKETING_VERSION = 2.4.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphonesimulator iphoneos macosx appletvos appletvsimulator\";\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB1FD49B2046984F00931B5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = MMKV;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB1FD49C2046984F00931B5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tOTHER_LDFLAGS = \"-ObjC\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = MMKV;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBC1A8FC20DA946200AD5087 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKV;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3,7\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBC1A8FD20DA946200AD5087 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKV;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,3,7\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBF1904A243D706D001C82ED /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\tMMKV_IOS_EXTENSION,\n\t\t\t\t);\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVWatchExtension;\n\t\t\t\tPRODUCT_NAME = MMKVWatchExtension;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"watchsimulator watchos\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBF1904B243D706D001C82ED /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"NDEBUG=1\",\n\t\t\t\t\tMMKV_IOS_EXTENSION,\n\t\t\t\t);\n\t\t\t\tGENERATE_MASTER_OBJECT_FILE = NO;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/MMKV/Resources/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphoneos*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\t\"OTHER_LDFLAGS[sdk=iphonesimulator*]\" = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tUIKit,\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVWatchExtension;\n\t\t\t\tPRODUCT_NAME = MMKVWatchExtension;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSUPPORTED_PLATFORMS = \"watchsimulator watchos\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tCB1E064E242B84D800F2FD42 /* Build configuration list for PBXNativeTarget \"MMKVAppExtension\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB1E064F242B84D800F2FD42 /* Debug */,\n\t\t\t\tCB1E0650242B84D800F2FD42 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB1FD48C2046984F00931B5F /* Build configuration list for PBXProject \"MMKV\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB1FD4982046984F00931B5F /* Debug */,\n\t\t\t\tCB1FD4992046984F00931B5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB1FD49A2046984F00931B5F /* Build configuration list for PBXNativeTarget \"MMKV Static\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB1FD49B2046984F00931B5F /* Debug */,\n\t\t\t\tCB1FD49C2046984F00931B5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBC1A8FB20DA946200AD5087 /* Build configuration list for PBXNativeTarget \"MMKV\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBC1A8FC20DA946200AD5087 /* Debug */,\n\t\t\t\tCBC1A8FD20DA946200AD5087 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBF19049243D706D001C82ED /* Build configuration list for PBXNativeTarget \"MMKVWatchExtension\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBF1904A243D706D001C82ED /* Debug */,\n\t\t\t\tCBF1904B243D706D001C82ED /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = CB1FD4892046984F00931B5F /* Project object */;\n}\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:MMKV.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/xcshareddata/xcschemes/MMKV For App Extension.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1E0641242B84D800F2FD42\"\n               BuildableName = \"MMKVAppExtension.framework\"\n               BlueprintName = \"MMKVAppExtension\"\n               ReferencedContainer = \"container:MMKV.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1E0641242B84D800F2FD42\"\n            BuildableName = \"MMKVAppExtension.framework\"\n            BlueprintName = \"MMKVAppExtension\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/xcshareddata/xcschemes/MMKV Static.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1FD4902046984F00931B5F\"\n               BuildableName = \"libMMKV.a\"\n               BlueprintName = \"MMKV Static\"\n               ReferencedContainer = \"container:MMKV.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD4902046984F00931B5F\"\n            BuildableName = \"libMMKV.a\"\n            BlueprintName = \"MMKV Static\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD4902046984F00931B5F\"\n            BuildableName = \"libMMKV.a\"\n            BlueprintName = \"MMKV Static\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/xcshareddata/xcschemes/MMKV.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBC1A8F520DA946200AD5087\"\n               BuildableName = \"MMKV.framework\"\n               BlueprintName = \"MMKV\"\n               ReferencedContainer = \"container:MMKV.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBC1A8F520DA946200AD5087\"\n            BuildableName = \"MMKV.framework\"\n            BlueprintName = \"MMKV\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBC1A8F520DA946200AD5087\"\n            BuildableName = \"MMKV.framework\"\n            BlueprintName = \"MMKV\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKV/MMKV.xcodeproj/xcshareddata/xcschemes/MMKVWatchExtension.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBF1903C243D706D001C82ED\"\n               BuildableName = \"MMKVWatchExtension.framework\"\n               BlueprintName = \"MMKVWatchExtension\"\n               ReferencedContainer = \"container:MMKV.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF1903C243D706D001C82ED\"\n            BuildableName = \"MMKVWatchExtension.framework\"\n            BlueprintName = \"MMKVWatchExtension\"\n            ReferencedContainer = \"container:MMKV.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/Config.xcconfig",
    "content": "//\n//  Config.xcconfig\n//  MMKVDemo\n//\n//  Created by lingol on 2020/12/31.\n//  Copyright © 2020 Lingol. All rights reserved.\n//\n\n// Configuration settings file format documentation can be found at:\n// https://help.apple.com/xcode/#/dev745c5c974\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/AppDelegate.h",
    "content": "//\n//  AppDelegate.h\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/AppDelegate.m",
    "content": "//\n//  AppDelegate.m\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import \"AppDelegate.h\"\n#import <MMKV/MMKV.h>\n\n@interface AppDelegate ()\n\n@end\n\n@implementation AppDelegate\n\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    // Override point for customization after application launch.\n    [MMKV initializeMMKV:nil];\n    return YES;\n}\n\n\n#pragma mark - UISceneSession lifecycle\n\n\n- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {\n    // Called when a new scene session is being created.\n    // Use this method to select a configuration to create the new scene with.\n    return [[UISceneConfiguration alloc] initWithName:@\"Default Configuration\" sessionRole:connectingSceneSession.role];\n}\n\n\n- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {\n    // Called when the user discards a scene session.\n    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.\n    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.\n}\n\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"32700.99.1234\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_12\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"22684\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"393\" height=\"852\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" fixedFrame=\"YES\" text=\"Hello, world!\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"f0M-2F-G2Y\">\n                                <rect key=\"frame\" x=\"149\" y=\"416\" width=\"95\" height=\"21\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMinX=\"YES\" flexibleMaxX=\"YES\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"138.1679389312977\" y=\"-2.1126760563380285\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>SceneDelegate</string>\n\t\t\t\t\t<key>UISceneStoryboardFile</key>\n\t\t\t\t\t<string>Main</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/MMKVCatalystDemo.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-only</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/SceneDelegate.h",
    "content": "//\n//  SceneDelegate.h\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>\n\n@property (strong, nonatomic) UIWindow * window;\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/SceneDelegate.m",
    "content": "//\n//  SceneDelegate.m\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import \"SceneDelegate.h\"\n\n@interface SceneDelegate ()\n\n@end\n\n@implementation SceneDelegate\n\n\n- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {\n    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\n    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\n    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\n}\n\n\n- (void)sceneDidDisconnect:(UIScene *)scene {\n    // Called as the scene is being released by the system.\n    // This occurs shortly after the scene enters the background, or when its session is discarded.\n    // Release any resources associated with this scene that can be re-created the next time the scene connects.\n    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).\n}\n\n\n- (void)sceneDidBecomeActive:(UIScene *)scene {\n    // Called when the scene has moved from an inactive state to an active state.\n    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.\n}\n\n\n- (void)sceneWillResignActive:(UIScene *)scene {\n    // Called when the scene will move from an active state to an inactive state.\n    // This may occur due to temporary interruptions (ex. an incoming phone call).\n}\n\n\n- (void)sceneWillEnterForeground:(UIScene *)scene {\n    // Called as the scene transitions from the background to the foreground.\n    // Use this method to undo the changes made on entering the background.\n}\n\n\n- (void)sceneDidEnterBackground:(UIScene *)scene {\n    // Called as the scene transitions from the foreground to the background.\n    // Use this method to save data, release shared resources, and store enough scene-specific state information\n    // to restore the scene back to its current state.\n}\n\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/ViewController.h",
    "content": "//\n//  ViewController.h\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface ViewController : UIViewController\n\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/ViewController.mm",
    "content": "//\n//  ViewController.m\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import \"ViewController.h\"\n#import <MMKV/MMKV.h>\n\n@interface ViewController ()\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    // Do any additional setup after loading the view.\n\n    [self funcionalTest:NO];\n}\n\n- (void)funcionalTest:(BOOL)decodeOnly {\n    MMKV *mmkv = [MMKV defaultMMKV];\n    \n    if (!decodeOnly) {\n        [mmkv setBool:YES forKey:@\"bool\"];\n    }\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setInt32:-1024 forKey:@\"int32\"];\n    }\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    }\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    }\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    }\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    }\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    }\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    }\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    }\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n    \n    if (!decodeOnly) {\n        [mmkv setObject:[@\"hello, mmkv again and again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    }\n    NSData *data = [mmkv getObjectOfClass:NSData.class forKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n    \n    if (!decodeOnly) {\n        [mmkv removeValueForKey:@\"bool\"];\n        NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    }\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVCatalystDemo/main.m",
    "content": "//\n//  main.m\n//  MMKVCatalystDemo\n//\n//  Created by lingol on 2024/7/9.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char * argv[]) {\n    NSString * appDelegateClassName;\n    @autoreleasepool {\n        // Setup code that might create autoreleased objects goes here.\n        appDelegateClassName = NSStringFromClass([AppDelegate class]);\n    }\n    return UIApplicationMain(argc, argv, nil, appDelegateClassName);\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/AppDelegate.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n@property(strong, nonatomic) UIWindow *window;\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/AppDelegate.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"AppDelegate.h\"\n#import <MMKV/MMKV.h>\n#import \"ViewController.h\"\n\n@interface AppDelegate () <MMKVHandler>\n\n@end\n\n@implementation AppDelegate\n\n#pragma mark - init MMKV in the main thread\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    // test using NameSpace before +[MMKV initializeMMKV:]\n    [self testNameSpace];\n\n    // usally you can just init MMKV with the default root dir like this\n    //[MMKV initializeMMKV:nil];\n\n    // or you can customize MMKV's root dir & group dir\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);\n    NSString *libraryPath = (NSString *) [paths firstObject];\n    if ([libraryPath length] > 0) {\n        NSString *rootDir = [libraryPath stringByAppendingPathComponent:@\"mmkv\"];\n        NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@\"group.tencent.mmkv\"].path;\n\n        // you can turn off logging by passing MMKVLogNone\n        // register handler on init\n        [MMKV initializeMMKV:rootDir groupDir:groupDir logLevel:MMKVLogInfo handler:self];\n\n        NSLog(@\"MMKV version: %@\", [MMKV version]);\n    }\n\n    // enable auto clean up\n    uint32_t maxIdleMinutes = 1;\n    [MMKV enableAutoCleanUp:maxIdleMinutes];\n\n    return YES;\n}\n\n#pragma mark - MMKVHandler\n\n- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID {\n    return MMKVOnErrorRecover;\n}\n\n- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID {\n    return MMKVOnErrorRecover;\n}\n\n- (void)onMMKVContentChange:(NSString *)mmapID {\n    NSLog(@\"onMMKVContentChange: %@\", mmapID);\n}\n\n- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message {\n    const char *levelDesc = NULL;\n    switch (level) {\n        case MMKVLogDebug:\n            levelDesc = \"D\";\n            break;\n        case MMKVLogInfo:\n            levelDesc = \"I\";\n            break;\n        case MMKVLogWarning:\n            levelDesc = \"W\";\n            break;\n        case MMKVLogError:\n            levelDesc = \"E\";\n            break;\n        default:\n            levelDesc = \"N\";\n            break;\n    }\n\n    NSLog(@\"redirect logging [%s] <%s:%d::%s> %@\", levelDesc, file, line, funcname, message);\n}\n\n#pragma mark - UIApplicationDelegate\n\n- (void)applicationWillResignActive:(UIApplication *)application {\n    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n}\n\n- (void)applicationDidEnterBackground:(UIApplication *)application {\n    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n}\n\n- (void)applicationWillEnterForeground:(UIApplication *)application {\n    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n}\n\n- (void)applicationDidBecomeActive:(UIApplication *)application {\n    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n}\n\n- (void)applicationWillTerminate:(UIApplication *)application {\n    // it's totally fine no calling this method\n    [MMKV onAppTerminate];\n}\n\n#pragma mark -\n\n- (void)testNameSpace {\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);\n    NSString *libraryPath = (NSString *) [paths firstObject];\n    NSString *rootDir = [libraryPath stringByAppendingPathComponent:@\"mmkv_namespace\"];\n\n    MMKVNameSpace *ns = [MMKV nameSpace:rootDir];\n    MMKV *kv = [ns mmkvWithID:@\"test_namespace\"];\n    [ViewController testMMKV:kv decodeOnly:NO];\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/DemoSwiftUsage.swift",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2018 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nimport Foundation\n\nclass DemoSwiftUsage : NSObject {\n\t@objc func testSwiftFunctionality() {\n\n        guard let mmkv = MMKV(mmapID: \"testSwift\", mode: MMKVMode.singleProcess) else {\n            return\n        }\n\n        mmkv.set(true, forKey: \"bool\")\n        print(\"Swift: bool = \\(mmkv.bool(forKey: \"bool\"))\")\n\n        mmkv.set(Int32(-1024), forKey: \"int32\")\n        print(\"Swift: int32 = \\(mmkv.int32(forKey: \"int32\"))\")\n\n        mmkv.set(UInt32.max, forKey: \"uint32\")\n        print(\"Swift: uint32 = \\(mmkv.uint32(forKey: \"uint32\"))\")\n\n        mmkv.set(Int64.min, forKey: \"int64\")\n        print(\"Swift: int64 = \\(mmkv.int64(forKey: \"int64\"))\")\n\n        mmkv.set(UInt64.max, forKey: \"uint64\")\n        print(\"Swift: uint64 = \\(mmkv.uint64(forKey: \"uint64\"))\")\n\n        mmkv.set(Float(-3.1415926), forKey: \"float\")\n        print(\"Swift: float = \\(mmkv.float(forKey: \"float\"))\")\n\n        mmkv.set(Double.infinity, forKey: \"double\")\n        print(\"Swift: double = \\(mmkv.double(forKey: \"double\"))\")\n        \n        mmkv.set(\"Hello from Swift\", forKey: \"string\")\n        print(\"Swift: string = \\(mmkv.string(forKey: \"string\") ?? \"\")\")\n\t\t\n        mmkv.set(NSDate(), forKey: \"date\")\n        let date = mmkv.date(forKey: \"date\")\n        print(\"Swift: date = \\(date?.description(with: .current) ?? \"null\")\")\n        \n        mmkv.set(\"Hello from Swift\".data(using: .utf8) ?? Data(), forKey: \"data\")\n        let data = mmkv.data(forKey: \"data\")\n        let str = String(data: data ?? Data(), encoding: .utf8) ?? \"\"\n        print(\"Swift: data = \\(str)\")\n\n        let arr = [1, 0, 2, 4]\n        if let objArr = arr as NSArray? {\n            mmkv.set(objArr, forKey:\"array\")\n            let result = mmkv.object(of: NSArray.self, forKey: \"array\");\n            print(\"Swift: array = \\(result as! NSArray)\")\n        }\n\n        mmkv.removeValue(forKey: \"bool\")\n        print(\"Swift: after delete bool = \\(mmkv.bool(forKey: \"bool\"))\")\n\n        MMKV.checkExist(for: \"testSwift\", mode: MMKVMode.singleProcess)\n\t}\n\n    @objc func testSwiftAutoExpire() {\n        guard let mmkv = MMKV(mmapID: \"testAutoExpire\") else { return }\n\n        mmkv.enableAutoKeyExpire(expiredInSeconds: MMKVExpireDuration.never.rawValue)\n        mmkv.set(true, forKey: \"key\", expireDuration: MMKVExpireDuration.never.rawValue)\n    }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/MMKVDemo-Bridging-Header.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// #import <MMKV/MMKV-Bridging-Header.h>\n#import <MMKV/MMKV.h>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"60x60\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"60x60\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"76x76\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"76x76\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"83.5x83.5\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"size\" : \"1024x1024\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/Resources/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" systemVersion=\"17A277\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/Resources/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14113\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14088\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <button opaque=\"NO\" contentMode=\"scaleToFill\" fixedFrame=\"YES\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"yNL-IO-pkK\">\n                                <rect key=\"frame\" x=\"131\" y=\"311\" width=\"112\" height=\"45\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMinX=\"YES\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"24\"/>\n                                <state key=\"normal\" title=\"Baseline\"/>\n                                <connections>\n                                    <action selector=\"onBtnClick:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"d2T-L8-i1U\"/>\n                                </connections>\n                            </button>\n                            <activityIndicatorView hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" hidesWhenStopped=\"YES\" style=\"gray\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pmg-Ii-sVa\">\n                                <rect key=\"frame\" x=\"178\" y=\"365\" width=\"20\" height=\"20\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMinX=\"YES\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                            </activityIndicatorView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"m_btn\" destination=\"yNL-IO-pkK\" id=\"fQ8-mA-ZAF\"/>\n                        <outlet property=\"m_loading\" destination=\"pmg-Ii-sVa\" id=\"5cE-0f-80C\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"32.799999999999997\" y=\"31.934032983508249\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>fetch</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/Resources/MMKVDemo.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.tencent.mmkv</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/TestMMKVCpp.cpp",
    "content": "//\n//  TestMMKVCpp.cpp\n//  MMKVDemo\n//\n//  Created by lingol on 2025/2/12.\n//  Copyright © 2025 Lingol. All rights reserved.\n//\n\n#include \"TestMMKVCpp.hpp\"\n#include <string>\n\nusing namespace std;\nusing namespace mmkv;\n\n#define MMKVLog printf\n\nstring to_string(const std::string& str) {  return str; }\n\ntemplate <class T>\nstring to_string(const vector<T> &arr, const char* sp = \", \") {\n    string str;\n    for (const auto &element : arr) {\n        str += to_string(element);\n        str += sp;\n    }\n    if (!str.empty()) {\n        str.erase(str.length() - strlen(sp));\n    }\n    return str;\n}\n\nvoid containerTest(MMKV* mmkv, bool decodeOnly) {\n    {\n        if (!decodeOnly) {\n            vector<string> vec = {\"Hello\", \"MMKV-示例\", \"for\", \"POSIX\"};\n            mmkv->set(vec, \"string-set\");\n        }\n        vector<string> vecResult;\n        mmkv->getVector(\"string-set\", vecResult);\n        printf(\"string-set = %s\\n\", to_string(vecResult).c_str());\n    }\n#if __cplusplus>=202002L\n    {\n        if (!decodeOnly) {\n            vector<bool> vec = {true, false, std::numeric_limits<bool>::min(), std::numeric_limits<bool>::max()};\n            mmkv->set(vec, \"bool-set\");\n        }\n        vector<bool> vecResult;\n        mmkv->getVector(\"bool-set\", vecResult);\n        printf(\"bool-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            vector<int32_t> vec = {1024, 0, std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max()};\n            mmkv->set(vec, \"int32-set\");\n        }\n        vector<int32_t> vecResult;\n        mmkv->getVector(\"int32-set\", vecResult);\n        printf(\"int32-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            constexpr uint32_t arr[] = {2048, 0, std::numeric_limits<uint32_t>::min(), std::numeric_limits<uint32_t>::max()};\n            std::span vec = arr;\n            mmkv->set(vec, \"uint32-set\");\n        }\n        vector<uint32_t> vecResult;\n        mmkv->getVector(\"uint32-set\", vecResult);\n        printf(\"uint32-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            constexpr int64_t vec[] = {1024, 0, std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()};\n            mmkv->set(std::span(vec), \"int64-set\");\n        }\n        vector<int64_t> vecResult;\n        mmkv->getVector(\"int64-set\", vecResult);\n        printf(\"int64-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            vector<uint64_t> vec = {1024, 0, std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max()};\n            mmkv->set(vec, \"uint64-set\");\n        }\n        vector<uint64_t> vecResult;\n        mmkv->getVector(\"uint64-set\", vecResult);\n        printf(\"uint64-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            vector<float> vec = {1024.0f, 0.0f, std::numeric_limits<float>::min(), std::numeric_limits<float>::max()};\n            mmkv->set(vec, \"float-set\");\n        }\n        vector<float> vecResult;\n        mmkv->getVector(\"float-set\", vecResult);\n        printf(\"float-set = %s\\n\", to_string(vecResult).c_str());\n    }\n    \n    {\n        if (!decodeOnly) {\n            vector<double> vec = {1024.0, 0.0, std::numeric_limits<double>::min(), std::numeric_limits<double>::max()};\n            mmkv->set(vec, \"double-set\");\n        }\n        vector<double> vecResult;\n        mmkv->getVector(\"double-set\", vecResult);\n        printf(\"double-set = %s\\n\", to_string(vecResult).c_str());\n        \n        // Un-comment to test the functionality of set<!MMKV_SUPPORTED_VALUE_TYPE<T>>(const T& value, key)\n        // mmkv->set(&vecResult, \"unsupported-type\");\n    }\n#endif // __cplusplus>=202002L\n}\n\nvoid functionalTest(MMKV *mmkv, bool decodeOnly) {\n    if (!decodeOnly) {\n        mmkv->set(true, \"bool\");\n    }\n    MMKVLog(\"bool = %d\\n\", mmkv->getBool(\"bool\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(1024, \"int32\");\n    }\n    MMKVLog(\"int32 = %d\\n\", mmkv->getInt32(\"int32\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint32_t>::max(), \"uint32\");\n    }\n    MMKVLog(\"uint32 = %u\\n\", mmkv->getUInt32(\"uint32\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<int64_t>::min(), \"int64\");\n    }\n    MMKVLog(\"int64 = %lld\\n\", mmkv->getInt64(\"int64\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<uint64_t>::max(), \"uint64\");\n    }\n    MMKVLog(\"uint64 = %llu\\n\", mmkv->getUInt64(\"uint64\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(3.14f, \"float\");\n    }\n    MMKVLog(\"float = %f\\n\", mmkv->getFloat(\"float\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(std::numeric_limits<double>::max(), \"double\");\n    }\n    MMKVLog(\"double = %f\\n\", mmkv->getDouble(\"double\"));\n    \n    if (!decodeOnly) {\n        mmkv->set(\"Hello, MMKV-示例 for POSIX\", \"raw_string\");\n        std::string str = \"Hello, MMKV-示例 for POSIX string\";\n        mmkv->set(str, \"string\");\n        mmkv->set(std::string_view(str).substr(7, 21), \"string_view\");\n    }\n    std::string result;\n    mmkv->getString(\"raw_string\", result);\n    MMKVLog(\"raw_string = %s\\n\", result.c_str());\n    mmkv->getString(\"string\", result);\n    MMKVLog(\"string = %s\\n\", result.c_str());\n    mmkv->getString(\"string_view\", result);\n    MMKVLog(\"string_view = %s\\n\", result.c_str());\n    \n    containerTest(mmkv, decodeOnly);\n    \n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"count = %zu, totalSize = %zu\\n\", mmkv->count(), mmkv->totalSize());\n    MMKVLog(\"containsKey[string]: %d\\n\", mmkv->containsKey(\"string\"));\n    \n    mmkv->removeValueForKey(\"bool\");\n    MMKVLog(\"bool: %d\\n\", mmkv->getBool(\"bool\"));\n    mmkv->removeValuesForKeys({\"int\", \"long\"});\n    \n    mmkv->set(\"some string\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string before set null: %s\\n\", result.c_str());\n    mmkv->set((const char *) nullptr, \"null string\");\n    //mmkv->set(\"\", \"null string\");\n    result.erase();\n    mmkv->getString(\"null string\", result);\n    MMKVLog(\"string after set null: %s, containsKey: %d\\n\", result.c_str(), mmkv->containsKey(\"null string\"));\n    \n    //kv.sync();\n    //kv.async();\n    //kv.clearAll();\n    mmkv->clearMemoryCache();\n    MMKVLog(\"allKeys: %s\\n\", ::to_string(mmkv->allKeys()).c_str());\n    MMKVLog(\"isFileValid[%s]: %d\\n\", mmkv->mmapID().c_str(), MMKV::isFileValid(mmkv->mmapID()));\n}\n\nvoid functionalTest(bool decodeOnly) {\n    auto kv = MMKV::mmkvWithID(\"testCpp\");\n    functionalTest(kv, decodeOnly);\n}\n\n// void baseline(mmkv::MMKV *kv) {\n//     return;\n// }\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/TestMMKVCpp.hpp",
    "content": "//\n//  TestMMKVCpp.hpp\n//  MMKVDemo\n//\n//  Created by lingol on 2025/2/12.\n//  Copyright © 2025 Lingol. All rights reserved.\n//\n\n#ifndef TestMMKVCpp_hpp\n#define TestMMKVCpp_hpp\n\n#include <MMKVCore/MMKV.h>\n\nvoid functionalTest(bool decodeOnly);\n\nvoid baseline(mmkv::MMKV *kv);\n\n#endif /* TestMMKVCpp_hpp */\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/ViewController+TestCaseBad.h",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#import \"ViewController.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface ViewController (TestCaseBad)\n\n- (void)testCornerSize;\n\n- (void)testFastRemoveCornerSize;\n\n- (void)testChineseCharKey;\n\n- (void)testItemSizeHolderOverride;\n\n- (void)testAutoExpireWildPtr;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/ViewController+TestCaseBad.mm",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2020 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#import \"ViewController+TestCaseBad.h\"\n#import <MMKV/MMKV.h>\n#import <string>\n#import <vector>\n\n@implementation ViewController (TestCaseBad)\n\n- (void)testCornerSize {\n    // auto mmkv = [MMKV mmkvWithID:@\"test/cornerSize\" cryptKey:[@\"crypt\" dataUsingEncoding:NSUTF8StringEncoding]];\n    auto mmkv = [MMKV mmkvWithID:@\"test/cornerSize1\"];\n    [mmkv clearAll];\n    auto size = getpagesize() - 2;\n    size -= 4;\n    NSString *key = @\"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    NSData *value = [NSMutableData dataWithLength:size];\n    [mmkv setObject:value forKey:key];\n}\n\n- (void)testFastRemoveCornerSize {\n    // auto mmkv = [MMKV mmkvWithID:@\"test/FastRemoveCornerSize\" cryptKey:[@\"crypt\" dataUsingEncoding:NSUTF8StringEncoding]];\n    auto mmkv = [MMKV mmkvWithID:@\"test/FastRemoveCornerSize1\"];\n    [mmkv clearAll];\n    auto size = getpagesize() - 4;\n    size -= 4;\n    NSString *key = @\"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    size -= (keySize + 1); // total size of fast remove\n    size /= 16;\n    NSMutableData *value = [NSMutableData dataWithLength:size];\n    auto ptr = (char *) value.mutableBytes;\n    for (int i = 0; i < value.length; i++) {\n        ptr[i] = 'A';\n    }\n    for (int i = 0; i < 16; i++) {\n        [mmkv setObject:value forKey:key]; // when a full write back is occur, here's corruption happens\n        [mmkv removeValueForKey:key];\n    }\n}\n\n- (void)testChineseCharKey {\n    std::vector<std::tuple<std::string, size_t, size_t>> fakeKeyValues = {\n        {\"UUID\", 36, 37},\n        {\"webViewShake\", 1, 2},\n        {\"install_now\", 1, 1},\n        {\"HomeContainerViewControllerGuide\", 32, 33},\n        {\"AdvertisingKey\", 397, 397},\n        {\"PrivacyPolicy\", 1, 1},\n        {\"appVersion\", 6, 7},\n        {\"IS_RECEIVE_PUSH\", 1, 1},\n        {\"invite_url\", 0, 1},\n        {\"kHaveOneDayOrLongTime\", 8, 8},\n        {\"kHaveOneHourOrLongTime\", 8, 8},\n        {\"PO_TYPE\", 139, 139},\n        {\"invite_url\", 41, 42},\n        {\"watermark\", 95, 96},\n        {\"BrowserWhitelist\", 253, 253},\n        {\"open_log\", 0, 1},\n        {\"xz_district_key\", 1390, 1390},\n        {\"open_position\", 1, 1},\n        {\"xz_auth_wechat\", 1, 1},\n        {\"xz_auth_weibo\", 0, 1},\n        {\"xz_auth_qq\", 0, 1},\n        {\"cache_expire\", 0, 8},\n        {\"PopupWindowNotice\", 461, 461},\n        {\"推送通知提示间隔\", 1, 1},\n        {\"版本更新提示间隔\", 0, 1},\n        {\"定位提醒显示间隔\", 0, 8},\n        {\"QFH5_JAVASCRIPT\", 2157, 2159},\n        {\"上次定位_city\", 9, 10},\n        {\"PopupWindowNotice\", 0, 0},\n    };\n\n    auto mmkv = [MMKV mmkvWithID:@\"testChineseCharKey\"];\n    for (auto &kv : fakeKeyValues) {\n        auto key = [NSString stringWithUTF8String:std::get<0>(kv).c_str()];\n        auto size = std::get<1>(kv);\n        const auto orgSize = std::get<2>(kv);\n        if (orgSize == 8) {\n            [mmkv setDouble:0.0 forKey:key];\n        } else if (size < orgSize) {\n            auto buf = [NSMutableData dataWithLength:size];\n            [mmkv setObject:buf forKey:key];\n        } else if (size == 1) {\n            [mmkv setBool:YES forKey:key];\n        } else if (size == 0) {\n            [mmkv removeValueForKey:key];\n        } else if (size > 8) {\n            if (orgSize <= 127) {\n                size -= 1;\n            } else {\n                size -= 2;\n            }\n            auto buf = [NSMutableData dataWithLength:size];\n            [mmkv setObject:buf forKey:key];\n        } else {\n            assert(0);\n        }\n        auto resultSize = [mmkv getValueSizeForKey:key actualSize:NO];\n        assert(resultSize == orgSize);\n        NSLog(@\"%@, %zu\", key, [mmkv actualSize]);\n    }\n}\n\n- (void)testItemSizeHolderOverride {\n    auto mmapID = @\"testItemSizeHolderOverride\";\n    auto cryptKey = nil;\n    // auto mmapID = @\"testItemSizeHolderOverride_crypt\";\n    // auto cryptKey = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n\n    /* do these on v1.1.2\n    {\n        auto mmkv = [MMKV mmkvWithID:mmapID cryptKey:cryptKey];\n\n        // turn on to check crypt MMKV\n        // [mmkv setBool:YES forKey:@\"b\"];\n\n        // turn on (mutex with the above setBool:forKey testcase) to check crypt MMKV\n        // [mmkv setInt32:0xff forKey:@\"i\"];\n\n        auto value = [NSMutableData dataWithLength:512];\n        [mmkv setData:value forKey:@\"data\"];\n        NSLog(@\"%@\", [mmkv allKeys]);\n    }*/\n\n    // do these on v1.2.0\n    {\n        auto mmkv = [MMKV mmkvWithID:mmapID cryptKey:cryptKey];\n        auto actualSize = [mmkv actualSize];\n        auto fileSize = [mmkv totalSize];\n        // force a fullwrieback()\n        auto valueSize = fileSize - actualSize;\n        auto value = [NSMutableData dataWithLength:valueSize];\n        [mmkv setObject:value forKey:@\"bigData\"];\n\n        [mmkv clearMemoryCache];\n\n        // it might throw exception\n        // you won't find the key \"data\"\n        NSLog(@\"%@\", [mmkv allKeys]);\n    }\n}\n\n- (void)testAutoExpireWildPtr {\n    NSString *mmapID = @\"testAutoExpire\";\n    auto mmkv = [MMKV mmkvWithID:mmapID];\n    [mmkv clearAll];\n    [mmkv trim];\n    [mmkv enableAutoKeyExpire:1];\n\n    auto size = getpagesize() - 4;\n    size -= 4;\n    NSString *key = @\"key\";\n    auto keySize = 3 + 1;\n    size -= keySize;\n    auto valueSize = 3;\n    size -= valueSize;\n    // size -= (keySize + 1); // total size of fast remove\n    size /= 16;\n    NSMutableData *value = [NSMutableData dataWithLength:size];\n    auto ptr = (char *) value.mutableBytes;\n    for (int i = 0; i < value.length; i++) {\n        ptr[i] = 'A';\n    }\n    for (int i = 0; i < 15; i++) {\n        [mmkv setObject:value forKey:key]; // when a full write back is occur, here's corruption happens\n    }\n\n    sleep(2);\n    [mmkv setObject:value forKey:key];\n    assert([mmkv containsKey:key]);\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/ViewController.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <UIKit/UIKit.h>\n\n@class MMKV;\n\n@interface ViewController : UIViewController\n\n@property(weak, nonatomic) IBOutlet UIButton *m_btn;\n@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *m_loading;\n\n+ (void)testMMKV:(MMKV *)mmkv decodeOnly:(BOOL)decodeOnly;\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/ViewController.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"MMKVDemo-Swift.h\"\n#import \"ViewController+TestCaseBad.h\"\n#import <MMKV/MMKV.h>\n#import \"TestMMKVCpp.hpp\"\n\n@interface TestNSArchive : NSObject <NSSecureCoding>\n@property(nonatomic, strong) NSString *m_username;\n@property(nonatomic, assign) int32_t m_age;\n@property(nonatomic, assign) float m_score;\n@end\n\n@implementation TestNSArchive\n\n- (id)initWithCoder:(NSCoder *)decoder {\n    self = [super init];\n    if (!self) {\n        return nil;\n    }\n\n    self.m_username = [decoder decodeObjectForKey:@\"m_username\"];\n    self.m_age = [decoder decodeInt32ForKey:@\"m_age\"];\n    self.m_score = [decoder decodeFloatForKey:@\"m_score\"];\n\n    return self;\n}\n\n- (void)encodeWithCoder:(NSCoder *)encoder {\n    [encoder encodeObject:self.m_username forKey:@\"m_username\"];\n    [encoder encodeInteger:self.m_age forKey:@\"m_age\"];\n    [encoder encodeFloat:self.m_score forKey:@\"m_score\"];\n}\n\n+ (BOOL)supportsSecureCoding {\n    return YES;\n}\n\n@end\n\n@implementation ViewController {\n    NSMutableArray *m_arrStrings;\n    NSMutableArray *m_arrStrKeys;\n    NSMutableArray *m_arrIntKeys;\n    NSMutableArray *m_arrObjKeys;\n    NSMutableArray *m_arrNSCodingObjs;\n\n    int m_loops;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n\n    [[NSNotificationCenter defaultCenter] addObserver:self\n                                             selector:@selector(updateTodayContent)\n                                                 name:UIApplicationDidBecomeActiveNotification\n                                               object:nil];\n\n    [self functionTestCpp];\n    [self funcionalTest:NO];\n    [self testReKey];\n    [self testImportFromUserDefault];\n    // [self testCornerSize];\n    // [self testFastRemoveCornerSize];\n    // [self testChineseCharKey];\n    // [self testItemSizeHolderOverride];\n    [self testAutoExpire];\n    // [self testAutoExpireWildPtr];\n\n    DemoSwiftUsage *swiftUsageDemo = [[DemoSwiftUsage alloc] init];\n    [swiftUsageDemo testSwiftFunctionality];\n    [swiftUsageDemo testSwiftAutoExpire];\n\n    [self testMultiProcess];\n    // [self testMultiProcess];\n    [self testBackup];\n    [self testRestore];\n    [self testExpectedCapacity];\n    [self onlyOneKeyTest];\n    [self overrideTest];\n    [self testCompareBeforeSet];\n\n    [self testClearAllWithKeepingSpace];\n    [self testRemoveStorage];\n    [self testReadOnly:NO];\n    [self testImport];\n\n    m_loops = 10000;\n    m_arrStrings = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrStrKeys = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrIntKeys = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrObjKeys = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrNSCodingObjs = [NSMutableArray arrayWithCapacity:m_loops];\n    for (size_t index = 0; index < m_loops; index++) {\n        // auto str = @\"[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication\";\n        // str = [str stringByAppendingFormat:@\", %s-%d\", __FILE__, rand()];\n        NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n        [m_arrStrings addObject:str];\n\n        NSString *strKey = [NSString stringWithFormat:@\"str-%zu\", index];\n        [m_arrStrKeys addObject:strKey];\n\n        NSString *intKey = [NSString stringWithFormat:@\"int-%zu\", index];\n        [m_arrIntKeys addObject:intKey];\n        /*\n        NSString *objKey = [NSString stringWithFormat:@\"obj-%zu\", index];\n        [m_arrObjKeys addObject:objKey];\n        \n        TestNSArchive *obj = [[TestNSArchive alloc] init];\n        obj.m_username = str;\n        obj.m_age = rand();\n        obj.m_age = rand() * rand() * 0.5;\n        [m_arrNSCodingObjs addObject:obj];*/\n    }\n    //getMMKVForBatchTest();\n}\n\n- (void)funcionalTest:(BOOL)decodeOnly {\n    auto path = [MMKV mmkvBasePath];\n    path = [path stringByDeletingLastPathComponent];\n    path = [path stringByAppendingPathComponent:@\"mmkv_2\"];\n    NSData *key_1 = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n    auto mmkv = [MMKV mmkvWithID:@\"test/case_aes\" cryptKey:key_1 rootPath:path];\n\n    if (!decodeOnly) {\n        [mmkv setBool:YES forKey:@\"bool\"];\n    }\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    if (!decodeOnly) {\n        [mmkv setInt32:-1024 forKey:@\"int32\"];\n    }\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    }\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    }\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    }\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    }\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    if (!decodeOnly) {\n        [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    }\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    if (!decodeOnly) {\n        [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    }\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:nil forKey:@\"string\"];\n        NSLog(@\"string after set nil:%@, containsKey:%d\",\n              [mmkv getObjectOfClass:NSString.class\n                              forKey:@\"string\"],\n              [mmkv containsKey:@\"string\"]);\n    }\n\n    if (!decodeOnly) {\n        [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    }\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n\n    if (!decodeOnly) {\n        auto str = @\"[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication\";\n        [mmkv setData:[str dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    }\n    NSData *data = [mmkv getDataForKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n    NSLog(@\"data length:%zu, value size consumption:%zu\", data.length, [mmkv getValueSizeForKey:@\"data\" actualSize:NO]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:[NSData data] forKey:@\"data\"];\n        NSLog(@\"data after set empty data:%@, containsKey:%d\",\n              [mmkv getObjectOfClass:NSData.class\n                              forKey:@\"data\"],\n              [mmkv containsKey:@\"data\"]);\n    }\n\n    if (!decodeOnly) {\n        auto array = @[ @\"1984\", @\"2046\", @\"Move\" ];\n        [mmkv setObject:array forKey:@\"array\"];\n    }\n    NSArray *array = [mmkv getObjectOfClass:NSArray.class forKey:@\"array\"];\n    NSLog(@\"array:%@\", array);\n\n    if (!decodeOnly) {\n        [mmkv removeValueForKey:@\"bool\"];\n        NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n        [mmkv removeValuesForKeys:@[ @\"int32\", @\"uint64\" ]];\n        NSLog(@\"allKeys %@\", [mmkv allKeys]);\n    }\n\n    [mmkv close];\n}\n\n- (void)functionTestCpp {\n    functionalTest(false);\n}\n\n- (void)testMMKV:(NSString *)mmapID withCryptKey:(NSData *)cryptKey aes256:(BOOL)aes256 decodeOnly:(BOOL)decodeOnly {\n    MMKV *mmkv = [MMKV mmkvWithID:mmapID cryptKey:cryptKey aes256:aes256];\n    [ViewController testMMKV:mmkv decodeOnly:decodeOnly];\n\n    NSLog(@\"isFileValid[%@]: %d, checkExist: %d\", mmapID, [MMKV isFileValid:mmapID], [MMKV checkExist:mmapID rootPath:nil]);\n}\n\n+ (void)testMMKV:(MMKV *)mmkv decodeOnly:(BOOL)decodeOnly {\n    if (!decodeOnly) {\n        [mmkv setInt32:-1024 forKey:@\"int32\"];\n    }\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    }\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    }\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    }\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    }\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    if (!decodeOnly) {\n        [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    }\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:@\"An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX.\" forKey:@\"string\"];\n    }\n    NSLog(@\"string:%@\", [mmkv getObjectOfClass:NSString.class forKey:@\"string\"]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:[NSDate date] forKey:@\"date\"];\n    }\n    NSLog(@\"date:%@\", [mmkv getObjectOfClass:NSDate.class forKey:@\"date\"]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:[@\"An efficient, small mobile key-value storage framework developed by WeChat(微信). Works on Android, iOS, macOS, Windows, and POSIX.\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    }\n    NSData *data = [mmkv getObjectOfClass:NSData.class forKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n\n    if (!decodeOnly) {\n        [mmkv removeValueForKey:@\"bool\"];\n    }\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    NSLog(@\"containsKey[string]: %d\", [mmkv containsKey:@\"string\"]);\n\n    [mmkv removeValuesForKeys:@[ @\"int\", @\"long\" ]];\n    [mmkv clearMemoryCache];\n}\n\n- (void)testReKey {\n    NSString *mmapID = @\"testAES_reKey\";\n    [self testMMKV:mmapID withCryptKey:nullptr aes256:NO decodeOnly:NO];\n\n    MMKV *kv = [MMKV mmkvWithID:mmapID cryptKey:nullptr];\n    NSData *key_1 = [@\"Key_Seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n    [kv reKey:key_1];\n    [kv clearMemoryCache];\n    [self testMMKV:mmapID withCryptKey:key_1 aes256:NO decodeOnly:YES];\n\n    NSData *key_2 = [@\"Key_Seq_Very_Looooooooong\" dataUsingEncoding:NSUTF8StringEncoding];\n    [kv reKey:key_2 aes256:YES];\n    [kv clearMemoryCache];\n    [self testMMKV:mmapID withCryptKey:key_2 aes256:YES decodeOnly:YES];\n\n    [kv reKey:nullptr];\n    [kv clearMemoryCache];\n    [self testMMKV:mmapID withCryptKey:nullptr aes256:NO decodeOnly:YES];\n}\n\n- (void)testImportFromUserDefault {\n    NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@\"testNSUserDefaults1\"];\n    [userDefault setBool:YES forKey:@\"bool\"];\n    [userDefault setInteger:std::numeric_limits<NSInteger>::max() forKey:@\"NSInteger\"];\n    [userDefault setFloat:3.14 forKey:@\"float\"];\n    [userDefault setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    [userDefault setObject:@\"hello, NSUserDefaults\" forKey:@\"string\"];\n    [userDefault setObject:[NSDate date] forKey:@\"date\"];\n    [userDefault setObject:[@\"hello, NSUserDefaults again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    [userDefault setURL:[NSURL URLWithString:@\"https://mail.qq.com\"] forKey:@\"url\"];\n\n    NSNumber *number = [NSNumber numberWithBool:YES];\n    [userDefault setObject:number forKey:@\"number_bool\"];\n\n    number = [NSNumber numberWithChar:std::numeric_limits<char>::min()];\n    [userDefault setObject:number forKey:@\"number_char\"];\n\n    number = [NSNumber numberWithUnsignedChar:std::numeric_limits<unsigned char>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_char\"];\n\n    number = [NSNumber numberWithShort:std::numeric_limits<short>::min()];\n    [userDefault setObject:number forKey:@\"number_short\"];\n\n    number = [NSNumber numberWithUnsignedShort:std::numeric_limits<unsigned short>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_short\"];\n\n    number = [NSNumber numberWithInt:std::numeric_limits<int>::min()];\n    [userDefault setObject:number forKey:@\"number_int\"];\n\n    number = [NSNumber numberWithUnsignedInt:std::numeric_limits<unsigned int>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_int\"];\n\n    number = [NSNumber numberWithLong:std::numeric_limits<long>::min()];\n    [userDefault setObject:number forKey:@\"number_long\"];\n\n    number = [NSNumber numberWithUnsignedLong:std::numeric_limits<unsigned long>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_long\"];\n\n    number = [NSNumber numberWithLongLong:std::numeric_limits<long long>::min()];\n    [userDefault setObject:number forKey:@\"number_long_long\"];\n\n    number = [NSNumber numberWithUnsignedLongLong:std::numeric_limits<unsigned long long>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_long_long\"];\n\n    number = [NSNumber numberWithFloat:3.1415];\n    [userDefault setObject:number forKey:@\"number_float\"];\n\n    number = [NSNumber numberWithDouble:std::numeric_limits<double>::max()];\n    [userDefault setObject:number forKey:@\"number_double\"];\n\n    number = [NSNumber numberWithInteger:std::numeric_limits<NSInteger>::min()];\n    [userDefault setObject:number forKey:@\"number_NSInteger\"];\n\n    number = [NSNumber numberWithUnsignedInteger:std::numeric_limits<NSUInteger>::max()];\n    [userDefault setObject:number forKey:@\"number_NSUInteger\"];\n\n    auto mmkv = [MMKV mmkvWithID:@\"testImportNSUserDefaults1\"];\n    [mmkv migrateFromUserDefaultsDictionaryRepresentation:userDefault.dictionaryRepresentation];\n    [mmkv clearMemoryCache];\n    NSLog(@\"%@\", [mmkv allKeys]);\n\n    NSLog(@\"migrate from NSUserDefault begin\");\n\n    NSLog(@\"bool = %d\", [mmkv getBoolForKey:@\"bool\"]);\n    NSLog(@\"NSInteger = %lld\", [mmkv getInt64ForKey:@\"NSInteger\"]);\n    NSLog(@\"float = %f\", [mmkv getFloatForKey:@\"float\"]);\n    NSLog(@\"double = %f\", [mmkv getDoubleForKey:@\"double\"]);\n    NSLog(@\"string = %@\", [mmkv getStringForKey:@\"string\"]);\n    NSLog(@\"date = %@\", [mmkv getDateForKey:@\"date\"]);\n    NSLog(@\"data = %@\", [[NSString alloc] initWithData:[mmkv getDataForKey:@\"data\"] encoding:NSUTF8StringEncoding]);\n    NSLog(@\"url = %@\", [NSKeyedUnarchiver unarchivedObjectOfClass:NSURL.class fromData:[mmkv getDataForKey:@\"url\"] error:nil]);\n    NSLog(@\"number_bool = %d\", [mmkv getBoolForKey:@\"number_bool\"]);\n    NSLog(@\"number_char = %d\", [mmkv getInt32ForKey:@\"number_char\"]);\n    NSLog(@\"number_unsigned_char = %d\", [mmkv getInt32ForKey:@\"number_unsigned_char\"]);\n    NSLog(@\"number_short = %d\", [mmkv getInt32ForKey:@\"number_short\"]);\n    NSLog(@\"number_unsigned_short = %d\", [mmkv getInt32ForKey:@\"number_unsigned_short\"]);\n    NSLog(@\"number_int = %d\", [mmkv getInt32ForKey:@\"number_int\"]);\n    NSLog(@\"number_unsigned_int = %u\", [mmkv getUInt32ForKey:@\"number_unsigned_int\"]);\n    NSLog(@\"number_long = %lld\", [mmkv getInt64ForKey:@\"number_long\"]);\n    NSLog(@\"number_unsigned_long = %llu\", [mmkv getUInt64ForKey:@\"number_unsigned_long\"]);\n    NSLog(@\"number_long_long = %lld\", [mmkv getInt64ForKey:@\"number_long_long\"]);\n    NSLog(@\"number_unsigned_long_long = %llu\", [mmkv getUInt64ForKey:@\"number_unsigned_long_long\"]);\n    NSLog(@\"number_float = %f\", [mmkv getFloatForKey:@\"number_float\"]);\n    NSLog(@\"number_double = %f\", [mmkv getDoubleForKey:@\"number_double\"]);\n    NSLog(@\"number_NSInteger = %lld\", [mmkv getInt64ForKey:@\"number_NSInteger\"]);\n    NSLog(@\"number_NSUInteger = %llu\", [mmkv getUInt64ForKey:@\"number_NSUInteger\"]);\n\n    NSLog(@\"migrate from NSUserDefault end\");\n}\n\n- (void)testAutoExpire {\n    NSString *mmapID = @\"testAutoExpire\";\n\n    // disable auto expire by config\n    auto config = MMKVConfigDefault();\n    config.enableKeyExpire = @NO;\n    // config.recover = MMKVOnErrorRecover;\n    // config.itemSizeLimit = 1;\n    auto mmkv = [MMKV mmkvWithID:mmapID config:config];\n    [mmkv clearAll];\n    [mmkv trim];\n    // [mmkv disableAutoKeyExpire]; // this call become a no-op\n\n    [self testMMKV:mmapID withCryptKey:nil aes256:NO decodeOnly:NO];\n    [mmkv setBool:YES forKey:@\"auto_expire_key_1\"];\n\n    // enable auto expire by config\n    [mmkv close];\n    mmkv = nil;\n    config.enableKeyExpire = @YES;\n    config.expiredInSeconds = 1;\n    mmkv = [MMKV mmkvWithID:mmapID config:config];\n    // [mmkv enableAutoKeyExpire:1]; // this call become a no-op\n\n    [mmkv setString:@\"never_expire_key_1\" forKey:@\"never_expire_key_1\" expireDuration:MMKVExpireNever];\n\n    auto arr = @[ @\"str1\", @\"str2\" ];\n    [mmkv setObject:arr forKey:@\"arr\" expireDuration:0];\n    NSArray *newArr = [mmkv getObjectOfClass:NSArray.class forKey:@\"arr\"];\n    assert([arr isEqualToArray:newArr]);\n\n    // sleep(2);\n    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n        assert([mmkv containsKey:@\"auto_expire_key_1\"] == NO);\n        assert([mmkv containsKey:@\"never_expire_key_1\"] == YES);\n        [self testMMKV:mmapID withCryptKey:nil aes256:NO decodeOnly:YES];\n\n        [mmkv removeValueForKey:@\"never_expire_key_1\"];\n        [mmkv enableAutoKeyExpire:MMKVExpireNever];\n        [mmkv setString:@\"never_expire_key_1\" forKey:@\"never_expire_key_1\"];\n        [mmkv setBool:YES forKey:@\"auto_expire_key_1\" expireDuration:1];\n\n        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n            // sleep(2);\n            assert([mmkv containsKey:@\"never_expire_key_1\"] == YES);\n            assert([mmkv containsKey:@\"auto_expire_key_1\"] == NO);\n        });\n    });\n}\n\n- (IBAction)onBtnClick:(id)sender {\n    [self.m_loading startAnimating];\n    self.m_btn.enabled = NO;\n\n    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n        self->m_arrStrings = [NSMutableArray arrayWithCapacity:self->m_loops];\n        for (size_t index = 0; index < self->m_loops; index++) {\n            // auto str = @\"[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication\";\n            // str = [str stringByAppendingFormat:@\", %s-%d\", __FILE__, rand()];\n            NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n            [self->m_arrStrings addObject:str];\n\n            //TestNSArchive *obj = self->m_arrNSCodingObjs[index];\n            //obj.m_username = str;\n        }\n\n        [self mmkvBaselineTest:self->m_loops];\n        [self userDefaultBaselineTest:self->m_loops];\n        //[self brutleTest];\n\n        [self.m_loading stopAnimating];\n        self.m_btn.enabled = YES;\n    });\n}\n\n#pragma mark - mmkv baseline test\n\n- (void)mmkvBaselineTest:(int)loops {\n    [self mmkvBatchReadInt:loops];\n    [self mmkvBatchWriteInt:loops];\n    [self mmkvBatchReadString:loops];\n    [self mmkvBatchWriteString:loops];\n    //[self mmkvBatchWriteObject:loops];\n    //[self mmkvBatchReadObject:loops];\n\n    //[self mmkvBatchDeleteString:loops];\n    //[[MMKV defaultMMKV] trim];\n\n    // auto mmkv = getMMKVForBatchTest();\n    // [mmkv clearMemoryCache];\n    // [mmkv actualSize];\n}\n\nMMKV *getMMKVForBatchTest() {\n    // return [MMKV mmkvWithID:@\"inter-process\" mode:MMKVMultiProcess];\n    // auto cryptKey = [@\"crypt_key\" dataUsingEncoding:NSUTF8StringEncoding];\n    NSData *cryptKey = nil;\n    // static auto key = [NSString stringWithFormat:@\"batchTest_%d\", rand()];\n    // auto key = @\"batchTest_crypt1\";\n    static auto key = @\"batchTest1\";\n    MMKV *mmkv = [MMKV mmkvWithID:key cryptKey:cryptKey];\n    return mmkv;\n}\n\n- (void)mmkvBatchWriteInt:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            int32_t tmp = rand();\n            NSString *intKey = m_arrIntKeys[index];\n            // NSString *intKey = [NSString stringWithFormat:@\"6AB741D2-426B-4CC2-918B-EC910753FF74-%d\", index];\n            [mmkv setInt32:tmp forKey:intKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv write int %d times, cost:%.1f ms\", loops, cost);\n\n        /* delete some\n        startDate = [NSDate date];\n\n        for (int index = 0; index < (loops / 2); index++) {\n            int32_t tmp = rand() % loops;\n            NSString *intKey = m_arrIntKeys[tmp];\n            [mmkv removeValueForKey:intKey];\n        }\n        endDate = [NSDate date];\n        cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv delete int %d times, cost:%d ms\", (loops / 2), cost);*/\n    }\n}\n\n- (void)mmkvBatchReadInt:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            NSString *intKey = m_arrIntKeys[index];\n            // NSString *intKey = [NSString stringWithFormat:@\"6AB741D2-426B-4CC2-918B-EC910753FF74-%d\", index];\n            [mmkv getInt32ForKey:intKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv read int %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)mmkvBatchWriteString:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            NSString *str = m_arrStrings[index];\n            NSString *strKey = m_arrStrKeys[index];\n            [mmkv setObject:str forKey:strKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv write string %d times, cost:%.1f ms\", loops, cost);\n\n        /* delete some\n        startDate = [NSDate date];\n\n        for (int index = 0; index < (loops / 2); index++) {\n            int32_t tmp = rand() % loops;\n            NSString *strKey = m_arrStrKeys[tmp];\n            [mmkv removeValueForKey:strKey];\n        }\n        endDate = [NSDate date];\n        cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv delete string %d times, cost:%d ms\", (loops / 2), cost);*/\n    }\n}\n\n- (void)mmkvBatchReadString:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            NSString *strKey = m_arrStrKeys[index];\n            [mmkv getObjectOfClass:NSString.class forKey:strKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv read string %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)mmkvBatchDeleteString:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            NSString *strKey = m_arrStrKeys[index];\n            [mmkv removeValueForKey:strKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv delete string %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)mmkvBatchWriteObject:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            TestNSArchive *obj = m_arrNSCodingObjs[index];\n            NSString *objKey = m_arrObjKeys[index];\n            [mmkv setObject:obj forKey:objKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv write object %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)mmkvBatchReadObject:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        MMKV *mmkv = getMMKVForBatchTest();\n        for (int index = 0; index < loops; index++) {\n            NSString *objKey = m_arrObjKeys[index];\n            [mmkv getObjectOfClass:TestNSArchive.class forKey:objKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"mmkv read object %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n#pragma mark - NSUserDefault baseline test\n\n- (void)userDefaultBaselineTest:(int)loops {\n    [self userDefaultBatchWriteInt:loops];\n    [self userDefaultBatchReadInt:loops];\n    [self userDefaultBatchWriteString:loops];\n    [self userDefaultBatchReadString:loops];\n    //[self userDefaultBatchWriteObject:loops];\n    //[self userDefaultBatchReadObject:loops];\n}\n\n- (void)userDefaultBatchWriteInt:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            NSInteger tmp = rand();\n            NSString *intKey = m_arrIntKeys[index];\n            [userdefault setInteger:tmp forKey:intKey];\n        }\n        [userdefault synchronize];\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults write int %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)userDefaultBatchReadInt:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            NSString *intKey = m_arrIntKeys[index];\n            [userdefault integerForKey:intKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults read int %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)userDefaultBatchWriteString:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            NSString *str = m_arrStrings[index];\n            NSString *strKey = m_arrStrKeys[index];\n            [userdefault setObject:str forKey:strKey];\n        }\n        [userdefault synchronize];\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults write string %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)userDefaultBatchReadString:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            NSString *strKey = m_arrStrKeys[index];\n            [userdefault objectForKey:strKey];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults read string %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)userDefaultBatchWriteObject:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            TestNSArchive *obj = m_arrNSCodingObjs[index];\n            NSString *objKey = m_arrObjKeys[index];\n            auto tmp = [NSKeyedArchiver archivedDataWithRootObject:obj requiringSecureCoding:YES error:nil];\n            [userdefault setObject:tmp forKey:objKey];\n        }\n        [userdefault synchronize];\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults write object %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n- (void)userDefaultBatchReadObject:(int)loops {\n    @autoreleasepool {\n        NSDate *startDate = [NSDate date];\n\n        NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];\n        for (int index = 0; index < loops; index++) {\n            NSString *objKey = m_arrObjKeys[index];\n            NSData *tmp = [userdefault objectForKey:objKey];\n            [NSKeyedUnarchiver unarchivedObjectOfClass:TestNSArchive.class fromData:tmp error:nil];\n        }\n        NSDate *endDate = [NSDate date];\n        auto cost = [endDate timeIntervalSinceDate:startDate] * 1000;\n        NSLog(@\"NSUserDefaults read object %d times, cost:%.1f ms\", loops, cost);\n    }\n}\n\n#pragma mark - brutle test\n\n- (void)brutleTest {\n    auto mmkv = [MMKV mmkvWithID:@\"brutleTest\"];\n    auto ptr = malloc(1024);\n    auto data = [NSData dataWithBytes:ptr length:1024];\n    free(ptr);\n    for (size_t index = 0; index < std::numeric_limits<size_t>::max(); index++) {\n        NSString *key = [NSString stringWithFormat:@\"key-%zu\", index];\n        [mmkv setObject:data forKey:key];\n\n        if (index % 1000 == 0) {\n            NSLog(@\"brutleTest size=%zu\", mmkv.totalSize);\n        }\n    }\n}\n\n#pragma mark - multi-process\n\n- (void)testMultiProcess {\n    NSData *key_1 = [@\"multi_process\" dataUsingEncoding:NSUTF8StringEncoding];\n    auto mmkv = [MMKV mmkvWithID:@\"multi_process\" cryptKey:key_1 mode:MMKVMultiProcess];\n\n    [mmkv setBool:YES forKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    [mmkv setInt32:-1024 forKey:@\"int32\"];\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n\n    [mmkv setObject:nil forKey:@\"string\"];\n    NSLog(@\"string after set nil:%@, containsKey:%d\",\n          [mmkv getObjectOfClass:NSString.class\n                          forKey:@\"string\"],\n          [mmkv containsKey:@\"string\"]);\n\n    [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n\n    [mmkv setData:[@\"hello, mmkv again and again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    NSData *data = [mmkv getDataForKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n    NSLog(@\"data length:%zu, value size consumption:%zu\", data.length, [mmkv getValueSizeForKey:@\"data\" actualSize:NO]);\n\n    [mmkv removeValueForKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    [mmkv removeValuesForKeys:@[ @\"int32\", @\"uint64\" ]];\n    NSLog(@\"allKeys %@\", [mmkv allKeys]);\n\n    [mmkv close];\n}\n\n- (void)updateTodayContent {\n    static int count = 0;\n    NSData *key_1 = [@\"multi_process\" dataUsingEncoding:NSUTF8StringEncoding];\n    auto mmkv = [MMKV mmkvWithID:@\"multi_process\" cryptKey:key_1 mode:MMKVMultiProcess];\n    NSString *content = [NSString stringWithFormat:@\"count: %d\", count++];\n    [mmkv setString:content forKey:@\"content\"];\n}\n\n#pragma mark - backup & restore\n\n- (void)testBackup {\n    auto parentPath = [[MMKV mmkvBasePath] stringByDeletingLastPathComponent];\n    auto dstPath = [parentPath stringByAppendingPathComponent:@\"mmkv_backup\"];\n    auto rootPath = [parentPath stringByAppendingPathComponent:@\"mmkv_2\"];\n    auto ret = [MMKV backupOneMMKV:@\"test/case_aes\" rootPath:rootPath toDirectory:dstPath];\n    NSLog(@\"MMKV backup one file ret: %d\", ret);\n    if (ret) {\n        NSData *key_1 = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n        auto backupedMMKV = [MMKV mmkvWithID:@\"test/case_aes\" cryptKey:key_1 rootPath:dstPath];\n        NSLog(@\"check on backup file:%@\", [backupedMMKV allKeys]);\n    }\n\n    auto count = [MMKV backupAll:nil toDirectory:dstPath];\n    NSLog(@\"MMKV backup all count: %zu\", count);\n    if (count > 0) {\n        NSData *key_1 = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n        auto backupedMMKV = [MMKV mmkvWithID:@\"test/case_aes\" cryptKey:key_1 rootPath:dstPath];\n        NSLog(@\"check on backup file[%@] keys:%@\", backupedMMKV.mmapID, [backupedMMKV allKeys]);\n\n        backupedMMKV = [MMKV mmkvWithID:@\"testAES_reKey\" rootPath:dstPath];\n        NSLog(@\"check on backup file[%@] keys:%@\", backupedMMKV.mmapID, [backupedMMKV allKeys]);\n\n        backupedMMKV = [MMKV mmkvWithID:@\"testImportNSUserDefaults1\" rootPath:dstPath];\n        NSLog(@\"check on backup file[%@] keys:%@\", backupedMMKV.mmapID, [backupedMMKV allKeys]);\n\n        backupedMMKV = [MMKV mmkvWithID:@\"testSwift\" rootPath:dstPath];\n        NSLog(@\"check on backup file[%@] keys:%@\", backupedMMKV.mmapID, [backupedMMKV allKeys]);\n    }\n}\n\n- (void)testRestore {\n    auto ID = @\"test/case_aes\";\n    auto parentPath = [[MMKV mmkvBasePath] stringByDeletingLastPathComponent];\n    auto dstPath = [parentPath stringByAppendingPathComponent:@\"mmkv_backup\"];\n    auto rootPath = [parentPath stringByAppendingPathComponent:@\"mmkv_2\"];\n    auto ret = [MMKV backupOneMMKV:ID rootPath:rootPath toDirectory:dstPath];\n    NSLog(@\"MMKV backup one file ret: %d\", ret);\n    if (ret) {\n        NSData *key_1 = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n        auto originMMKV = [MMKV mmkvWithID:ID cryptKey:key_1 rootPath:rootPath];\n        [originMMKV setInt32:__LINE__ forKey:@\"test_restore_key\"];\n        NSLog(@\"file[%@] before restore:%@\", originMMKV.mmapID, [originMMKV allKeys]);\n\n        ret = [MMKV restoreOneMMKV:ID rootPath:rootPath fromDirectory:dstPath];\n        NSLog(@\"MMKV restore one file ret: %d\", ret);\n        if (ret) {\n            NSLog(@\"file[%@] after restore:%@\", originMMKV.mmapID, [originMMKV allKeys]);\n        }\n    }\n\n    auto count = [MMKV restoreAll:nil fromDirectory:dstPath];\n    NSLog(@\"MMKV restore all count: %zu\", count);\n    if (count > 0) {\n        NSData *key_1 = [@\"Key_seq_1\" dataUsingEncoding:NSUTF8StringEncoding];\n        auto restoredKV = [MMKV mmkvWithID:ID cryptKey:key_1];\n        NSLog(@\"check on restore file[%@] keys:%@\", restoredKV.mmapID, [restoredKV allKeys]);\n\n        restoredKV = [MMKV mmkvWithID:@\"testAES_reKey\"];\n        NSLog(@\"check on restore file[%@] keys:%@\", restoredKV.mmapID, [restoredKV allKeys]);\n\n        restoredKV = [MMKV mmkvWithID:@\"testImportNSUserDefaults1\"];\n        NSLog(@\"check on restore file[%@] keys:%@\", restoredKV.mmapID, [restoredKV allKeys]);\n\n        restoredKV = [MMKV mmkvWithID:@\"testSwift\"];\n        NSLog(@\"check on restore file[%@] keys:%@\", restoredKV.mmapID, [restoredKV allKeys]);\n    }\n}\n\n#pragma mark - expected capacity\n- (void)testExpectedCapacity {\n\n    int len = 10000;\n    NSString *value = [NSString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n    for (int i = 0; i < len; i++) {\n        value = [value stringByAppendingString:@\"0\"];\n    }\n    NSLog(@\"value size = %ld\", [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);\n    NSString *key = [NSString stringWithFormat:@\"key0\"];\n\n    // if we know exactly the sizes of key and value, set expectedCapacity for performance improvement\n    size_t expectedSize = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];\n    auto mmkv0 = [MMKV mmkvWithID:@\"expectedCapacityTest0\" expectedCapacity:expectedSize];\n    // 0 times expand\n    [mmkv0 setString:value forKey:key];\n\n    int count = 10;\n    expectedSize *= count;\n    auto mmkv1 = [MMKV mmkvWithID:@\"expectedCapacityTest1\" expectedCapacity:expectedSize];\n    for (int i = 0; i < count; i++) {\n        // 0 times expand\n        [mmkv1 setString:value forKey:[NSString stringWithFormat:@\"key%d\", i]];\n    }\n}\n\n- (void)overrideTest {\n    {\n        auto mmkv0 = [MMKV mmkvWithID:@\"overrideTest\"];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *key2 = [NSString stringWithFormat:@\"hello2\"];\n        NSString *value = [NSString stringWithFormat:@\"world\"];\n\n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        if (![v2 isEqualToString:value]) {\n            NSLog(@\"value = %@\", v2);\n            abort();\n        }\n        [mmkv0 removeValueForKey:key];\n\n        [mmkv0 setString:value forKey:key2];\n        v2 = [mmkv0 getStringForKey:key2];\n        if (![v2 isEqualToString:value]) {\n            NSLog(@\"value = %@\", v2);\n            abort();\n        }\n        [mmkv0 removeValueForKey:key2];\n\n        int len = 10000;\n        NSMutableString *bigValue = [NSMutableString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n        for (int i = 0; i < len; i++) {\n            [bigValue appendString:@\"0\"];\n        }\n        [mmkv0 setString:bigValue forKey:key];\n        auto v3 = [mmkv0 getStringForKey:key];\n        // NSLog(@\"value = %@\", v3);\n        if (![bigValue isEqualToString:v3]) {\n            abort();\n        }\n\n        // rewrite\n        [mmkv0 setString:@\"OK\" forKey:key];\n        auto v4 = [mmkv0 getStringForKey:key];\n        if (![v4 isEqualToString:@\"OK\"]) {\n            NSLog(@\"value = %@\", v2);\n            abort();\n        }\n\n        [mmkv0 setInt32:12345 forKey:key];\n        auto v5 = [mmkv0 getInt32ForKey:key];\n        if (v5 != 12345) {\n            NSLog(@\"value = %d\", v5);\n            abort();\n        }\n        [mmkv0 removeValueForKey:key];\n\n        [mmkv0 clearAll];\n    }\n\n    auto encryptionTestKV = [](NSString* key, NSString* value) {\n        NSData *crypt = [@\"fastestCrypt\" dataUsingEncoding:NSUTF8StringEncoding];\n        auto mmkv0 = [MMKV mmkvWithID:@\"overrideCryptTest\" cryptKey:crypt];\n\n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        if (![value isEqualToString:v2]) {\n            NSLog(@\"value = %@, result = %@\", value, v2);\n            abort();\n        }\n\n        [mmkv0 close];\n        mmkv0 = nil;\n        mmkv0 = [MMKV mmkvWithID:@\"overrideCryptTest\" cryptKey:crypt mode:MMKVSingleProcess];\n        v2 = [mmkv0 getStringForKey:key];\n        if (![value isEqualToString:v2]) {\n            NSLog(@\"value = %@, result = %@\", value, v2);\n            abort();\n        }\n        [mmkv0 setString:value forKey:key];\n        v2 = [mmkv0 getStringForKey:key];\n        if (![value isEqualToString:v2]) {\n            NSLog(@\"value = %@, result = %@\", value, v2);\n            abort();\n        }\n        [mmkv0 removeValueForKey:key];\n    };\n\n    auto encryptionTest = [&](NSString* value) {\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *key2 = [NSString stringWithFormat:@\"hello2\"];\n\n        encryptionTestKV(key, value);\n        encryptionTestKV(key2, value);\n    };\n    // [MMKV removeStorage:@\"overrideCryptTest\" rootPath:nil];\n\n    // test small value\n    encryptionTest(@\"cryptworld\");\n    // test medium value\n    encryptionTest(@\"An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX.\");\n    // test large value\n    encryptionTest(@\"An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX. MMKV is an efficient, small, easy-to-use mobile key-value storage framework used in the WeChat application. It's currently available on Android, iOS/macOS, Windows, POSIX and HarmonyOS NEXT.\");\n}\n\n- (void)onlyOneKeyTest {\n    {\n        auto mmkv0 = [MMKV mmkvWithID:@\"onlyOneKeyTest\"];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *value = [NSString stringWithFormat:@\"world\"];\n        auto v = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v);\n\n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n\n        for (int i = 0; i < 10; i++) {\n            NSString *value2 = [NSString stringWithFormat:@\"world_%d\", i];\n            [mmkv0 setString:value2 forKey:key];\n            auto v2 = [mmkv0 getStringForKey:key];\n            NSLog(@\"value = %@\", v2);\n        }\n\n        int len = 10000;\n        NSMutableString *bigValue = [NSMutableString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n        for (int i = 0; i < len; i++) {\n            [bigValue appendString:@\"0\"];\n        }\n        [mmkv0 setString:bigValue forKey:key];\n        auto v3 = [mmkv0 getStringForKey:key];\n        // NSLog(@\"value = %@\", v3);\n        if (![bigValue isEqualToString:v3]) {\n            abort();\n        }\n\n        [mmkv0 setString:@\"OK\" forKey:key];\n        auto v4 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v4);\n\n        [mmkv0 setInt32:12345 forKey:@\"int\"];\n        auto v5 = [mmkv0 getInt32ForKey:key];\n        NSLog(@\"int value = %d\", v5);\n        [mmkv0 removeValueForKey:@\"int\"];\n    }\n\n    {\n        NSString *crypt = [NSString stringWithFormat:@\"fastest\"];\n        auto mmkv0 = [MMKV mmkvWithID:@\"onlyOneKeyCryptTest\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *value = [NSString stringWithFormat:@\"cryptworld\"];\n        auto v = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v);\n\n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n\n        [mmkv0 setString:@\"hello, cryptworld\" forKey:key];\n        auto v3 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v3);\n\n        [mmkv0 close];\n        mmkv0 = nil;\n\n        auto mmkv1 = [MMKV mmkvWithID:@\"onlyOneKeyCryptTest\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n        auto v4 = [mmkv1 getStringForKey:key];\n        if (![v3 isEqualToString:v4]) {\n            NSLog(@\"value = %@\", v4);\n            abort();\n        }\n\n        for (int i = 0; i < 10; i++) {\n            NSString *value2 = [NSString stringWithFormat:@\"cryptworld_%d\", i];\n            [mmkv1 setString:value2 forKey:key];\n            auto v2 = [mmkv1 getStringForKey:key];\n            if (![v2 isEqualToString:value2]) {\n                NSLog(@\"value = %@\", v2);\n                abort();\n            }\n        }\n    }\n}\n\n- (void)testClearAllWithKeepingSpace {\n    {\n        auto mmkv = [MMKV mmkvWithID:@\"testClearAllWithKeepingSpace\"];\n        [mmkv setFloat:123.456f forKey:@\"key1\"];\n        for (int i = 0; i < 10000; i++) {\n            [mmkv setFloat:123.456f forKey:[NSString stringWithFormat:@\"key_%d\", i]];\n        }\n        auto previousSize = [mmkv totalSize];\n        //    assert(previousSize > [PAGE_SIZE]);\n        [mmkv clearAllWithKeepingSpace];\n        assert([mmkv totalSize] == previousSize);\n        NSLog(@\"testClearAllWithKeepingSpace, size = %zu\", previousSize);\n        assert([mmkv count] == 0);\n        [mmkv setFloat:123.4567f forKey:@\"key2\"];\n        assert([mmkv count] == 1);\n    }\n\n    {\n        NSString *crypt = [NSString stringWithFormat:@\"Crypt123\"];\n        auto mmkv = [MMKV mmkvWithID:@\"testClearAllWithKeepingSpaceCrypt\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n        [mmkv setFloat:123.456f forKey:@\"key1\"];\n        for (int i = 0; i < 10000; i++) {\n            [mmkv setFloat:123.456f forKey:[NSString stringWithFormat:@\"key_%d\", i]];\n        }\n        auto previousSize = [mmkv totalSize];\n        //        assert(previousSize > PAGE_SIZE);\n        [mmkv clearAllWithKeepingSpace];\n        assert([mmkv totalSize] == previousSize);\n        assert([mmkv count] == 0);\n        [mmkv setFloat:123.4567f forKey:@\"key2\"];\n        [mmkv setFloat:223.47f forKey:@\"key3\"];\n        assert([mmkv count] == 2);\n    }\n}\n\n- (void)testCompareBeforeSet {\n    auto mmkv = [MMKV mmkvWithID:@\"testCompareBeforeSet\"];\n    [mmkv enableCompareBeforeSet];\n    [mmkv setBool:true forKey:@\"extra\"];\n\n    {\n        NSString *key = @\"int64\";\n        int64_t v = 123456L;\n        [mmkv setInt64:v forKey:key];\n        long actualSize = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize);\n        NSLog(@\"testCompareBeforeSet v = %lld\", [mmkv getInt64ForKey:key]);\n        [mmkv setInt64:v forKey:key];\n        long actualSize2 = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize2);\n        if (actualSize != actualSize2) {\n            abort();\n        }\n        [mmkv setInt64:v << 1 forKey:key];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", [mmkv actualSize]);\n        NSLog(@\"testCompareBeforeSet v = %lld\", [mmkv getInt64ForKey:key]);\n    }\n\n    {\n        NSString *key = @\"string\";\n        NSString *v = [NSString stringWithFormat:@\"w012A🏊🏻good\"];\n        [mmkv setString:v forKey:key];\n        long actualSize = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize);\n        NSLog(@\"testCompareBeforeSet v = %@\", [mmkv getStringForKey:key]);\n        [mmkv setString:v forKey:key];\n        long actualSize2 = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize2);\n        if (actualSize != actualSize2) {\n            abort();\n        }\n        [mmkv setString:@\"another string\" forKey:key];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", [mmkv actualSize]);\n        NSLog(@\"testCompareBeforeSet v = %@\", [mmkv getStringForKey:key]);\n    }\n}\n\n- (void)testRemoveStorage {\n    auto mmapID = @\"test_remove\";\n    {\n        auto mmkv = [MMKV mmkvWithID:mmapID mode:MMKVMultiProcess];\n        [mmkv setBool:YES forKey:@\"bool\"];\n    }\n    [MMKV removeStorage:mmapID mode:MMKVMultiProcess];\n    {\n        auto mmkv = [MMKV mmkvWithID:mmapID mode:MMKVMultiProcess];\n        if (mmkv.count != 0) {\n            abort();\n        }\n    }\n\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);\n    NSString *libraryPath = (NSString *) [paths firstObject];\n    NSString *rootDir = [libraryPath stringByAppendingPathComponent:@\"mmkv_1\"];\n    mmapID = @\"test_remove/sg\";\n    // {\n    auto mmkv = [MMKV mmkvWithID:mmapID rootPath:rootDir];\n    [mmkv setBool:YES forKey:@\"bool\"];\n    // }\n    [MMKV removeStorage:mmapID rootPath:rootDir];\n    // {\n    mmkv = [MMKV mmkvWithID:mmapID rootPath:rootDir];\n    if (mmkv.count != 0) {\n        abort();\n    }\n    // }\n}\n\n- (void)testReadOnly:(BOOL) isForPrepare {\n    auto name = @\"testReadOnly\";\n    NSData *key_1 = [@\"Key_ReadOnly\" dataUsingEncoding:NSUTF8StringEncoding];\n    if (isForPrepare) {\n        [self testMMKV:name withCryptKey:key_1 aes256:NO decodeOnly:NO];\n    } else {\n        auto mmkvPath = [[NSBundle mainBundle] pathForResource:name ofType:nil];\n        auto mmkvDir = [mmkvPath stringByDeletingLastPathComponent];\n        auto mmkv = [MMKV mmkvWithID:name cryptKey:key_1 rootPath:mmkvDir mode:MMKVReadOnly expectedCapacity:0];\n\n        [ViewController testMMKV:mmkv decodeOnly:YES];\n\n        // also check if it tolerate update operations without crash\n        [ViewController testMMKV:mmkv decodeOnly:NO];\n    }\n}\n\n- (void)testImport {\n    auto mmapID = @\"test_import_src\";\n    auto src = [MMKV mmkvWithID:mmapID];\n    [src setBool:YES forKey:@\"bool\"];\n    [src setInt32:std::numeric_limits<int32_t>::min() forKey:@\"int32\"];\n    [src setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    [src setString:mmapID forKey:@\"string\"];\n\n    auto dst = [MMKV mmkvWithID:@\"test_import_dst\"];\n    [dst clearAll];\n    [dst enableAutoKeyExpire:1];\n    [dst setBool:NO forKey:@\"bool\"];\n    [dst setInt32:-1 forKey:@\"int32\"];\n    [dst setUInt64:1 forKey:@\"uint64\"];\n    [dst setString:@\"mmkv\" forKey:@\"string\"];\n\n    auto count = [dst importFrom:src];\n    assert(count == 4 && dst.count == 4);\n    assert([dst getBoolForKey:@\"bool\"] == YES);\n    assert([dst getInt32ForKey:@\"int32\"] == std::numeric_limits<int32_t>::min());\n    assert([dst getUInt64ForKey:@\"uint64\"] == std::numeric_limits<uint64_t>::max());\n    assert([[dst getStringForKey:@\"string\"] isEqualToString:mmapID]);\n\n    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n        assert([dst countNonExpiredKeys] == 0);\n    });\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo/main.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"AppDelegate.h\"\n#import <UIKit/UIKit.h>\n\nint main(int argc, char *argv[]) {\n    @autoreleasepool {\n        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n    }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t702CE6842B7E85070015E8A4 /* MMKV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; };\n\t\t702CE6852B7E85070015E8A4 /* MMKV.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\t70A630972B7E7EC9003AB1DF /* MMKVVisionDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A630962B7E7EC9003AB1DF /* MMKVVisionDemoApp.swift */; };\n\t\t70A630992B7E7EC9003AB1DF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A630982B7E7EC9003AB1DF /* ContentView.swift */; };\n\t\t70A6309B2B7E7ECA003AB1DF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70A6309A2B7E7ECA003AB1DF /* Assets.xcassets */; };\n\t\t70A6309E2B7E7ECA003AB1DF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70A6309D2B7E7ECA003AB1DF /* Preview Assets.xcassets */; };\n\t\tCB0DCC72242B57FC009AFE59 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBD5359823B4718200D3A404 /* libc++.tbd */; };\n\t\tCB1E0659242B8E3F00F2FD42 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4D12046AF2300931B5F /* libz.tbd */; };\n\t\tCB1E065F242B8E4A00F2FD42 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1E065E242B8E4A00F2FD42 /* libz.tbd */; };\n\t\tCB1FD42520455E2D00931B5F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD42420455E2D00931B5F /* AppDelegate.m */; };\n\t\tCB1FD42820455E2D00931B5F /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD42720455E2D00931B5F /* ViewController.mm */; };\n\t\tCB1FD42B20455E2D00931B5F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB1FD42920455E2D00931B5F /* Main.storyboard */; };\n\t\tCB1FD42D20455E2D00931B5F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB1FD42C20455E2D00931B5F /* Assets.xcassets */; };\n\t\tCB1FD43020455E2D00931B5F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB1FD42E20455E2D00931B5F /* LaunchScreen.storyboard */; };\n\t\tCB1FD43320455E2D00931B5F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CB1FD43220455E2D00931B5F /* main.m */; };\n\t\tCB2C907E2BE9DB890052A2D7 /* MMKV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; };\n\t\tCB2C907F2BE9DB920052A2D7 /* MMKV.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tCB3166FE2A4C6B5F0003221B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CB3166FD2A4C6B5F0003221B /* AppDelegate.m */; };\n\t\tCB3167012A4C6B5F0003221B /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CB3167002A4C6B5F0003221B /* SceneDelegate.m */; };\n\t\tCB3167042A4C6B5F0003221B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CB3167032A4C6B5F0003221B /* ViewController.m */; };\n\t\tCB3167072A4C6B5F0003221B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB3167052A4C6B5F0003221B /* Main.storyboard */; };\n\t\tCB3167092A4C6B600003221B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB3167082A4C6B600003221B /* Assets.xcassets */; };\n\t\tCB31670C2A4C6B600003221B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB31670A2A4C6B600003221B /* LaunchScreen.storyboard */; };\n\t\tCB31670F2A4C6B600003221B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CB31670E2A4C6B600003221B /* main.m */; };\n\t\tCB3167142A4C6B7D0003221B /* MMKV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; };\n\t\tCB3167172A4C6E450003221B /* MMKV.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tCB36F24C20F8A20C009AF46F /* MMKVDemoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB36F24B20F8A20C009AF46F /* MMKVDemoTests.mm */; };\n\t\tCB36F25A20F8D728009AF46F /* MMKVPerformanceTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB36F25920F8D728009AF46F /* MMKVPerformanceTest.mm */; };\n\t\tCB467FBC2431EE3000FD7421 /* MMKVAppExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB467FBB2431EE2D00FD7421 /* MMKVAppExtension.framework */; };\n\t\tCB5C469724AB87F3001B25B8 /* MMKVWatchExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBF1907C243D7116001C82ED /* MMKVWatchExtension.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tCB6180EA26E904AC003FF912 /* MMKVAppExtension.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB467FBB2431EE2D00FD7421 /* MMKVAppExtension.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tCB6D44CC23B3A44400F369AA /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB6D44CB23B3A44400F369AA /* NotificationCenter.framework */; };\n\t\tCB6D44D023B3A44400F369AA /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CB6D44CF23B3A44400F369AA /* TodayViewController.m */; };\n\t\tCB6D44D323B3A44400F369AA /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CB6D44D123B3A44400F369AA /* MainInterface.storyboard */; };\n\t\tCB6D44D723B3A44400F369AA /* MMKVTodayExtensionDemo.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = CB6D44CA23B3A44400F369AA /* MMKVTodayExtensionDemo.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tCB76C5A82C3D69A0008B0E61 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CB76C5A72C3D69A0008B0E61 /* AppDelegate.m */; };\n\t\tCB76C5AB2C3D69A0008B0E61 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CB76C5AA2C3D69A0008B0E61 /* SceneDelegate.m */; };\n\t\tCB76C5AE2C3D69A0008B0E61 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB76C5AD2C3D69A0008B0E61 /* ViewController.mm */; };\n\t\tCB76C5B12C3D69A0008B0E61 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = CB76C5B02C3D69A0008B0E61 /* Base */; };\n\t\tCB76C5B32C3D69A1008B0E61 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB76C5B22C3D69A1008B0E61 /* Assets.xcassets */; };\n\t\tCB76C5B62C3D69A1008B0E61 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = CB76C5B52C3D69A1008B0E61 /* Base */; };\n\t\tCB76C5B92C3D69A1008B0E61 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CB76C5B82C3D69A1008B0E61 /* main.m */; };\n\t\tCB76C5BD2C3D6A05008B0E61 /* MMKV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; };\n\t\tCB76C5BF2C3D6A11008B0E61 /* MMKV.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CB36F25420F8A20C009AF46F /* MMKV.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tCB9C2731215E5DF000448B6C /* libMMKV.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1FD4B52046994D00931B5F /* libMMKV.a */; };\n\t\tCB9F963920FCA7C500A1C14C /* DemoSwiftUsage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB9F963820FCA7C500A1C14C /* DemoSwiftUsage.swift */; };\n\t\tCBB306612435E8F200CB593E /* ViewController+TestCaseBad.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBB306602435E8F200CB593E /* ViewController+TestCaseBad.mm */; };\n\t\tCBB6C823215CDB9500487192 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB6C822215CDB9500487192 /* AppDelegate.m */; };\n\t\tCBB6C826215CDB9500487192 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBB6C825215CDB9500487192 /* ViewController.mm */; };\n\t\tCBB6C828215CDB9600487192 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBB6C827215CDB9600487192 /* Assets.xcassets */; };\n\t\tCBB6C82B215CDB9600487192 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CBB6C829215CDB9600487192 /* Main.storyboard */; };\n\t\tCBB6C82E215CDB9600487192 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB6C82D215CDB9600487192 /* main.m */; };\n\t\tCBDC22E12C8ABDD20000DDA5 /* testReadOnly in Resources */ = {isa = PBXBuildFile; fileRef = CBDC22DF2C8ABDD20000DDA5 /* testReadOnly */; };\n\t\tCBDC22E22C8ABDD20000DDA5 /* testReadOnly.crc in Resources */ = {isa = PBXBuildFile; fileRef = CBDC22E02C8ABDD20000DDA5 /* testReadOnly.crc */; };\n\t\tCBE6689F2D5C52E5005C184E /* TestMMKVCpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBE6689E2D5C52E5005C184E /* TestMMKVCpp.cpp */; };\n\t\tCBF19016243D6F59001C82ED /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CBF19014243D6F59001C82ED /* Interface.storyboard */; };\n\t\tCBF19018243D6F5A001C82ED /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBF19017243D6F5A001C82ED /* Assets.xcassets */; };\n\t\tCBF1901F243D6F5A001C82ED /* WatchApp Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = CBF1901E243D6F5A001C82ED /* WatchApp Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tCBF19025243D6F5A001C82ED /* InterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = CBF19024243D6F5A001C82ED /* InterfaceController.m */; };\n\t\tCBF19028243D6F5A001C82ED /* ExtensionDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBF19027243D6F5A001C82ED /* ExtensionDelegate.mm */; };\n\t\tCBF1902B243D6F5A001C82ED /* NotificationController.m in Sources */ = {isa = PBXBuildFile; fileRef = CBF1902A243D6F5A001C82ED /* NotificationController.m */; };\n\t\tCBF1902D243D6F5A001C82ED /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBF1902C243D6F5A001C82ED /* Assets.xcassets */; };\n\t\tCBF19032243D6F5A001C82ED /* WatchApp.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = CBF19012243D6F59001C82ED /* WatchApp.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tCBF1907E243D71E8001C82ED /* MMKVWatchExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBF1907C243D7116001C82ED /* MMKVWatchExtension.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tCB1FD4B42046994D00931B5F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CB1FD4912046984F00931B5F;\n\t\t\tremoteInfo = MMKV;\n\t\t};\n\t\tCB2C907C2BE9DB7D0052A2D7 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CBC1A8F520DA946200AD5087;\n\t\t\tremoteInfo = MMKV;\n\t\t};\n\t\tCB36F24E20F8A20C009AF46F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD41820455E2D00931B5F /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB1FD41F20455E2D00931B5F;\n\t\t\tremoteInfo = MMKVDemo;\n\t\t};\n\t\tCB36F25320F8A20C009AF46F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CBC1A8F620DA946200AD5087;\n\t\t\tremoteInfo = MMKV;\n\t\t};\n\t\tCB467FBA2431EE2D00FD7421 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CB1E0651242B84D800F2FD42;\n\t\t\tremoteInfo = MMKVAppExtension;\n\t\t};\n\t\tCB5C469824AB8837001B25B8 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CBF1903C243D706D001C82ED;\n\t\t\tremoteInfo = MMKVWatchExtension;\n\t\t};\n\t\tCB6D44D523B3A44400F369AA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD41820455E2D00931B5F /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB6D44C923B3A44400F369AA;\n\t\t\tremoteInfo = MMKVTodayExtensionDemo;\n\t\t};\n\t\tCB9C272F215E5D2400448B6C /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB1FD4902046984F00931B5F;\n\t\t\tremoteInfo = \"MMKV Static\";\n\t\t};\n\t\tCBB306642435EC6000CB593E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CB1E0641242B84D800F2FD42;\n\t\t\tremoteInfo = MMKVAppExtension;\n\t\t};\n\t\tCBF19020243D6F5A001C82ED /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD41820455E2D00931B5F /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CBF1901D243D6F5A001C82ED;\n\t\t\tremoteInfo = \"WatchApp Extension\";\n\t\t};\n\t\tCBF19030243D6F5A001C82ED /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD41820455E2D00931B5F /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = CBF19011243D6F59001C82ED;\n\t\t\tremoteInfo = WatchApp;\n\t\t};\n\t\tCBF1907B243D7116001C82ED /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = CBF1904C243D706D001C82ED;\n\t\t\tremoteInfo = MMKVWatchExtension;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t702CE6862B7E85070015E8A4 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\t702CE6852B7E85070015E8A4 /* MMKV.framework in Embed Frameworks */,\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB3167162A4C6E380003221B /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tCB3167172A4C6E450003221B /* MMKV.framework in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB6180E926E90493003FF912 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tCB2C907F2BE9DB920052A2D7 /* MMKV.framework in CopyFiles */,\n\t\t\t\tCB6180EA26E904AC003FF912 /* MMKVAppExtension.framework in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB6D44DB23B3A44400F369AA /* Embed Foundation Extensions */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 13;\n\t\t\tfiles = (\n\t\t\t\tCB6D44D723B3A44400F369AA /* MMKVTodayExtensionDemo.appex in Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tname = \"Embed Foundation Extensions\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB76C5BE2C3D6A0A008B0E61 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tCB76C5BF2C3D6A11008B0E61 /* MMKV.framework in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19033243D6F5A001C82ED /* Embed Watch Content */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"$(CONTENTS_FOLDER_PATH)/Watch\";\n\t\t\tdstSubfolderSpec = 16;\n\t\t\tfiles = (\n\t\t\t\tCBF19032243D6F5A001C82ED /* WatchApp.app in Embed Watch Content */,\n\t\t\t);\n\t\t\tname = \"Embed Watch Content\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19036243D6F5A001C82ED /* Embed Foundation Extensions */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 13;\n\t\t\tfiles = (\n\t\t\t\tCBF1901F243D6F5A001C82ED /* WatchApp Extension.appex in Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tname = \"Embed Foundation Extensions\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF190AE243D78D4001C82ED /* Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tCB5C469724AB87F3001B25B8 /* MMKVWatchExtension.framework in Frameworks */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t702CE6802B7E84360015E8A4 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = Platforms/XROS.platform/Developer/SDKs/XROS1.0.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };\n\t\t70A630902B7E7EC9003AB1DF /* MMKVVisionDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MMKVVisionDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t70A630962B7E7EC9003AB1DF /* MMKVVisionDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMKVVisionDemoApp.swift; sourceTree = \"<group>\"; };\n\t\t70A630982B7E7EC9003AB1DF /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = \"<group>\"; };\n\t\t70A6309A2B7E7ECA003AB1DF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t70A6309D2B7E7ECA003AB1DF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = \"Preview Assets.xcassets\"; sourceTree = \"<group>\"; };\n\t\t70A6309F2B7E7ECA003AB1DF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB1E065E242B8E4A00F2FD42 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };\n\t\tCB1FD42020455E2D00931B5F /* MMKVDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MMKVDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB1FD42320455E2D00931B5F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\tCB1FD42420455E2D00931B5F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\tCB1FD42620455E2D00931B5F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = \"<group>\"; };\n\t\tCB1FD42720455E2D00931B5F /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = \"<group>\"; };\n\t\tCB1FD42A20455E2D00931B5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tCB1FD42C20455E2D00931B5F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCB1FD42F20455E2D00931B5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tCB1FD43120455E2D00931B5F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB1FD43220455E2D00931B5F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\tCB1FD4B02046994C00931B5F /* MMKV.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = MMKV.xcodeproj; path = ../MMKV/MMKV.xcodeproj; sourceTree = \"<group>\"; };\n\t\tCB1FD4D12046AF2300931B5F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };\n\t\tCB3166FA2A4C6B5F0003221B /* kvdemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kvdemo.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB3166FC2A4C6B5F0003221B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\tCB3166FD2A4C6B5F0003221B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\tCB3166FF2A4C6B5F0003221B /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = \"<group>\"; };\n\t\tCB3167002A4C6B5F0003221B /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = \"<group>\"; };\n\t\tCB3167022A4C6B5F0003221B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = \"<group>\"; };\n\t\tCB3167032A4C6B5F0003221B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = \"<group>\"; };\n\t\tCB3167062A4C6B5F0003221B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tCB3167082A4C6B600003221B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCB31670B2A4C6B600003221B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tCB31670D2A4C6B600003221B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB31670E2A4C6B600003221B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\tCB3167152A4C6BA90003221B /* kvdemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = kvdemo.entitlements; sourceTree = \"<group>\"; };\n\t\tCB36F24920F8A20C009AF46F /* MMKVDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MMKVDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB36F24B20F8A20C009AF46F /* MMKVDemoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MMKVDemoTests.mm; sourceTree = \"<group>\"; };\n\t\tCB36F24D20F8A20C009AF46F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB36F25920F8D728009AF46F /* MMKVPerformanceTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MMKVPerformanceTest.mm; sourceTree = \"<group>\"; };\n\t\tCB6D44C423B3924E00F369AA /* MMKVDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MMKVDemo.entitlements; sourceTree = \"<group>\"; };\n\t\tCB6D44CA23B3A44400F369AA /* MMKVTodayExtensionDemo.appex */ = {isa = PBXFileReference; explicitFileType = \"wrapper.app-extension\"; includeInIndex = 0; path = MMKVTodayExtensionDemo.appex; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB6D44CB23B3A44400F369AA /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; };\n\t\tCB6D44CE23B3A44400F369AA /* TodayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TodayViewController.h; sourceTree = \"<group>\"; };\n\t\tCB6D44CF23B3A44400F369AA /* TodayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TodayViewController.m; sourceTree = \"<group>\"; };\n\t\tCB6D44D223B3A44400F369AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = \"<group>\"; };\n\t\tCB6D44D423B3A44400F369AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB6D44DC23B3A45A00F369AA /* MMKVTodayExtensionDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MMKVTodayExtensionDemo.entitlements; sourceTree = \"<group>\"; };\n\t\tCB76C5A42C3D69A0008B0E61 /* MMKVCatalystDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MMKVCatalystDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCB76C5A62C3D69A0008B0E61 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\tCB76C5A72C3D69A0008B0E61 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\tCB76C5A92C3D69A0008B0E61 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = \"<group>\"; };\n\t\tCB76C5AA2C3D69A0008B0E61 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = \"<group>\"; };\n\t\tCB76C5AC2C3D69A0008B0E61 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = \"<group>\"; };\n\t\tCB76C5AD2C3D69A0008B0E61 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = \"<group>\"; };\n\t\tCB76C5B02C3D69A0008B0E61 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tCB76C5B22C3D69A1008B0E61 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCB76C5B52C3D69A1008B0E61 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tCB76C5B72C3D69A1008B0E61 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCB76C5B82C3D69A1008B0E61 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\tCB9C2732215E5ECC00448B6C /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };\n\t\tCB9F963720FCA7C400A1C14C /* MMKVDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"MMKVDemo-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\tCB9F963820FCA7C500A1C14C /* DemoSwiftUsage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoSwiftUsage.swift; sourceTree = \"<group>\"; };\n\t\tCBB3065F2435E8F200CB593E /* ViewController+TestCaseBad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"ViewController+TestCaseBad.h\"; sourceTree = \"<group>\"; };\n\t\tCBB306602435E8F200CB593E /* ViewController+TestCaseBad.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = \"ViewController+TestCaseBad.mm\"; sourceTree = \"<group>\"; };\n\t\tCBB6C81F215CDB9500487192 /* MMKVMacDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MMKVMacDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCBB6C821215CDB9500487192 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\tCBB6C822215CDB9500487192 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\tCBB6C824215CDB9500487192 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = \"<group>\"; };\n\t\tCBB6C825215CDB9500487192 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = \"<group>\"; };\n\t\tCBB6C827215CDB9600487192 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCBB6C82A215CDB9600487192 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tCBB6C82C215CDB9600487192 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCBB6C82D215CDB9600487192 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\tCBB6C82F215CDB9600487192 /* MMKVMacDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MMKVMacDemo.entitlements; sourceTree = \"<group>\"; };\n\t\tCBD5359823B4718200D3A404 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = \"libc++.tbd\"; path = \"usr/lib/libc++.tbd\"; sourceTree = SDKROOT; };\n\t\tCBDC22DF2C8ABDD20000DDA5 /* testReadOnly */ = {isa = PBXFileReference; lastKnownFileType = file; path = testReadOnly; sourceTree = \"<group>\"; };\n\t\tCBDC22E02C8ABDD20000DDA5 /* testReadOnly.crc */ = {isa = PBXFileReference; lastKnownFileType = file; path = testReadOnly.crc; sourceTree = \"<group>\"; };\n\t\tCBE6689D2D5C52E5005C184E /* TestMMKVCpp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TestMMKVCpp.hpp; sourceTree = \"<group>\"; };\n\t\tCBE6689E2D5C52E5005C184E /* TestMMKVCpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestMMKVCpp.cpp; sourceTree = \"<group>\"; };\n\t\tCBF19012243D6F59001C82ED /* WatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCBF19015243D6F59001C82ED /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = \"<group>\"; };\n\t\tCBF19017243D6F5A001C82ED /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCBF19019243D6F5A001C82ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCBF1901E243D6F5A001C82ED /* WatchApp Extension.appex */ = {isa = PBXFileReference; explicitFileType = \"wrapper.app-extension\"; includeInIndex = 0; path = \"WatchApp Extension.appex\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCBF19023243D6F5A001C82ED /* InterfaceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InterfaceController.h; sourceTree = \"<group>\"; };\n\t\tCBF19024243D6F5A001C82ED /* InterfaceController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InterfaceController.m; sourceTree = \"<group>\"; };\n\t\tCBF19026243D6F5A001C82ED /* ExtensionDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExtensionDelegate.h; sourceTree = \"<group>\"; };\n\t\tCBF19027243D6F5A001C82ED /* ExtensionDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ExtensionDelegate.mm; sourceTree = \"<group>\"; };\n\t\tCBF19029243D6F5A001C82ED /* NotificationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationController.h; sourceTree = \"<group>\"; };\n\t\tCBF1902A243D6F5A001C82ED /* NotificationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationController.m; sourceTree = \"<group>\"; };\n\t\tCBF1902C243D6F5A001C82ED /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCBF1902E243D6F5A001C82ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCBF1902F243D6F5A001C82ED /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t70A6308D2B7E7EC9003AB1DF /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t702CE6842B7E85070015E8A4 /* MMKV.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB1FD41D20455E2D00931B5F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB2C907E2BE9DB890052A2D7 /* MMKV.framework in Frameworks */,\n\t\t\t\tCB1E0659242B8E3F00F2FD42 /* libz.tbd in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB3166F72A4C6B5F0003221B /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB3167142A4C6B7D0003221B /* MMKV.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB36F24620F8A20C009AF46F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB6D44C723B3A44400F369AA /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB467FBC2431EE3000FD7421 /* MMKVAppExtension.framework in Frameworks */,\n\t\t\t\tCB0DCC72242B57FC009AFE59 /* libc++.tbd in Frameworks */,\n\t\t\t\tCB6D44CC23B3A44400F369AA /* NotificationCenter.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB76C5A12C3D699F008B0E61 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB76C5BD2C3D6A05008B0E61 /* MMKV.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBB6C81C215CDB9500487192 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1E065F242B8E4A00F2FD42 /* libz.tbd in Frameworks */,\n\t\t\t\tCB9C2731215E5DF000448B6C /* libMMKV.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF1901B243D6F5A001C82ED /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF1907E243D71E8001C82ED /* MMKVWatchExtension.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t70A630912B7E7EC9003AB1DF /* MMKVVisionDemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t70A630962B7E7EC9003AB1DF /* MMKVVisionDemoApp.swift */,\n\t\t\t\t70A630982B7E7EC9003AB1DF /* ContentView.swift */,\n\t\t\t\t70A6309A2B7E7ECA003AB1DF /* Assets.xcassets */,\n\t\t\t\t70A6309F2B7E7ECA003AB1DF /* Info.plist */,\n\t\t\t\t70A6309C2B7E7ECA003AB1DF /* Preview Content */,\n\t\t\t);\n\t\t\tpath = MMKVVisionDemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t70A6309C2B7E7ECA003AB1DF /* Preview Content */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t70A6309D2B7E7ECA003AB1DF /* Preview Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = \"Preview Content\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD41720455E2D00931B5F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD4B02046994C00931B5F /* MMKV.xcodeproj */,\n\t\t\t\tCB1FD42220455E2D00931B5F /* MMKVDemo */,\n\t\t\t\tCB36F24A20F8A20C009AF46F /* MMKVDemoTests */,\n\t\t\t\tCBB6C820215CDB9500487192 /* MMKVMacDemo */,\n\t\t\t\tCB6D44CD23B3A44400F369AA /* MMKVTodayExtensionDemo */,\n\t\t\t\tCBF19013243D6F59001C82ED /* WatchApp */,\n\t\t\t\tCBF19022243D6F5A001C82ED /* WatchApp Extension */,\n\t\t\t\tCB3166FB2A4C6B5F0003221B /* kvdemo */,\n\t\t\t\t70A630912B7E7EC9003AB1DF /* MMKVVisionDemo */,\n\t\t\t\tCB76C5A52C3D69A0008B0E61 /* MMKVCatalystDemo */,\n\t\t\t\tCB1FD42120455E2D00931B5F /* Products */,\n\t\t\t\tCB1FD45720455F3500931B5F /* Frameworks */,\n\t\t\t);\n\t\t\tindentWidth = 4;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD42120455E2D00931B5F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD42020455E2D00931B5F /* MMKVDemo.app */,\n\t\t\t\tCB36F24920F8A20C009AF46F /* MMKVDemoTests.xctest */,\n\t\t\t\tCBB6C81F215CDB9500487192 /* MMKVMacDemo.app */,\n\t\t\t\tCB6D44CA23B3A44400F369AA /* MMKVTodayExtensionDemo.appex */,\n\t\t\t\tCBF19012243D6F59001C82ED /* WatchApp.app */,\n\t\t\t\tCBF1901E243D6F5A001C82ED /* WatchApp Extension.appex */,\n\t\t\t\tCB3166FA2A4C6B5F0003221B /* kvdemo.app */,\n\t\t\t\t70A630902B7E7EC9003AB1DF /* MMKVVisionDemo.app */,\n\t\t\t\tCB76C5A42C3D69A0008B0E61 /* MMKVCatalystDemo.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD42220455E2D00931B5F /* MMKVDemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD42320455E2D00931B5F /* AppDelegate.h */,\n\t\t\t\tCB1FD42420455E2D00931B5F /* AppDelegate.m */,\n\t\t\t\tCBDC22DE2C8ABD880000DDA5 /* Resources */,\n\t\t\t\tCB9F963820FCA7C500A1C14C /* DemoSwiftUsage.swift */,\n\t\t\t\tCB1FD43220455E2D00931B5F /* main.m */,\n\t\t\t\tCB9F963720FCA7C400A1C14C /* MMKVDemo-Bridging-Header.h */,\n\t\t\t\tCB1FD42620455E2D00931B5F /* ViewController.h */,\n\t\t\t\tCB1FD42720455E2D00931B5F /* ViewController.mm */,\n\t\t\t\tCBB3065F2435E8F200CB593E /* ViewController+TestCaseBad.h */,\n\t\t\t\tCBB306602435E8F200CB593E /* ViewController+TestCaseBad.mm */,\n\t\t\t\tCBE6689D2D5C52E5005C184E /* TestMMKVCpp.hpp */,\n\t\t\t\tCBE6689E2D5C52E5005C184E /* TestMMKVCpp.cpp */,\n\t\t\t);\n\t\t\tpath = MMKVDemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD45720455F3500931B5F /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1E065E242B8E4A00F2FD42 /* libz.tbd */,\n\t\t\t\t702CE6802B7E84360015E8A4 /* libz.tbd */,\n\t\t\t\tCBD5359823B4718200D3A404 /* libc++.tbd */,\n\t\t\t\tCB9C2732215E5ECC00448B6C /* libz.tbd */,\n\t\t\t\tCB1FD4D12046AF2300931B5F /* libz.tbd */,\n\t\t\t\tCB6D44CB23B3A44400F369AA /* NotificationCenter.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD4B12046994C00931B5F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD4B52046994D00931B5F /* libMMKV.a */,\n\t\t\t\tCB36F25420F8A20C009AF46F /* MMKV.framework */,\n\t\t\t\tCB467FBB2431EE2D00FD7421 /* MMKVAppExtension.framework */,\n\t\t\t\tCBF1907C243D7116001C82ED /* MMKVWatchExtension.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB3166FB2A4C6B5F0003221B /* kvdemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB3167152A4C6BA90003221B /* kvdemo.entitlements */,\n\t\t\t\tCB3166FC2A4C6B5F0003221B /* AppDelegate.h */,\n\t\t\t\tCB3166FD2A4C6B5F0003221B /* AppDelegate.m */,\n\t\t\t\tCB3166FF2A4C6B5F0003221B /* SceneDelegate.h */,\n\t\t\t\tCB3167002A4C6B5F0003221B /* SceneDelegate.m */,\n\t\t\t\tCB3167022A4C6B5F0003221B /* ViewController.h */,\n\t\t\t\tCB3167032A4C6B5F0003221B /* ViewController.m */,\n\t\t\t\tCB3167052A4C6B5F0003221B /* Main.storyboard */,\n\t\t\t\tCB3167082A4C6B600003221B /* Assets.xcassets */,\n\t\t\t\tCB31670A2A4C6B600003221B /* LaunchScreen.storyboard */,\n\t\t\t\tCB31670D2A4C6B600003221B /* Info.plist */,\n\t\t\t\tCB31670E2A4C6B600003221B /* main.m */,\n\t\t\t);\n\t\t\tpath = kvdemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB36F24A20F8A20C009AF46F /* MMKVDemoTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB36F24B20F8A20C009AF46F /* MMKVDemoTests.mm */,\n\t\t\t\tCB36F25920F8D728009AF46F /* MMKVPerformanceTest.mm */,\n\t\t\t\tCB36F24D20F8A20C009AF46F /* Info.plist */,\n\t\t\t);\n\t\t\tpath = MMKVDemoTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB6D44CD23B3A44400F369AA /* MMKVTodayExtensionDemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB6D44DC23B3A45A00F369AA /* MMKVTodayExtensionDemo.entitlements */,\n\t\t\t\tCB6D44CE23B3A44400F369AA /* TodayViewController.h */,\n\t\t\t\tCB6D44CF23B3A44400F369AA /* TodayViewController.m */,\n\t\t\t\tCB6D44D123B3A44400F369AA /* MainInterface.storyboard */,\n\t\t\t\tCB6D44D423B3A44400F369AA /* Info.plist */,\n\t\t\t);\n\t\t\tpath = MMKVTodayExtensionDemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB76C5A52C3D69A0008B0E61 /* MMKVCatalystDemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCB76C5A62C3D69A0008B0E61 /* AppDelegate.h */,\n\t\t\t\tCB76C5A72C3D69A0008B0E61 /* AppDelegate.m */,\n\t\t\t\tCB76C5A92C3D69A0008B0E61 /* SceneDelegate.h */,\n\t\t\t\tCB76C5AA2C3D69A0008B0E61 /* SceneDelegate.m */,\n\t\t\t\tCB76C5AC2C3D69A0008B0E61 /* ViewController.h */,\n\t\t\t\tCB76C5AD2C3D69A0008B0E61 /* ViewController.mm */,\n\t\t\t\tCB76C5AF2C3D69A0008B0E61 /* Main.storyboard */,\n\t\t\t\tCB76C5B22C3D69A1008B0E61 /* Assets.xcassets */,\n\t\t\t\tCB76C5B42C3D69A1008B0E61 /* LaunchScreen.storyboard */,\n\t\t\t\tCB76C5B72C3D69A1008B0E61 /* Info.plist */,\n\t\t\t\tCB76C5B82C3D69A1008B0E61 /* main.m */,\n\t\t\t);\n\t\t\tpath = MMKVCatalystDemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBB6C820215CDB9500487192 /* MMKVMacDemo */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBB6C821215CDB9500487192 /* AppDelegate.h */,\n\t\t\t\tCBB6C822215CDB9500487192 /* AppDelegate.m */,\n\t\t\t\tCBB6C824215CDB9500487192 /* ViewController.h */,\n\t\t\t\tCBB6C825215CDB9500487192 /* ViewController.mm */,\n\t\t\t\tCBB6C827215CDB9600487192 /* Assets.xcassets */,\n\t\t\t\tCBB6C829215CDB9600487192 /* Main.storyboard */,\n\t\t\t\tCBB6C82C215CDB9600487192 /* Info.plist */,\n\t\t\t\tCBB6C82D215CDB9600487192 /* main.m */,\n\t\t\t\tCBB6C82F215CDB9600487192 /* MMKVMacDemo.entitlements */,\n\t\t\t);\n\t\t\tpath = MMKVMacDemo;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBDC22DE2C8ABD880000DDA5 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBDC22DF2C8ABDD20000DDA5 /* testReadOnly */,\n\t\t\t\tCBDC22E02C8ABDD20000DDA5 /* testReadOnly.crc */,\n\t\t\t\tCB6D44C423B3924E00F369AA /* MMKVDemo.entitlements */,\n\t\t\t\tCB1FD42C20455E2D00931B5F /* Assets.xcassets */,\n\t\t\t\tCB1FD43120455E2D00931B5F /* Info.plist */,\n\t\t\t\tCB1FD42E20455E2D00931B5F /* LaunchScreen.storyboard */,\n\t\t\t\tCB1FD42920455E2D00931B5F /* Main.storyboard */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBF19013243D6F59001C82ED /* WatchApp */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBF19014243D6F59001C82ED /* Interface.storyboard */,\n\t\t\t\tCBF19017243D6F5A001C82ED /* Assets.xcassets */,\n\t\t\t\tCBF19019243D6F5A001C82ED /* Info.plist */,\n\t\t\t);\n\t\t\tpath = WatchApp;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBF19022243D6F5A001C82ED /* WatchApp Extension */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCBF19023243D6F5A001C82ED /* InterfaceController.h */,\n\t\t\t\tCBF19024243D6F5A001C82ED /* InterfaceController.m */,\n\t\t\t\tCBF19026243D6F5A001C82ED /* ExtensionDelegate.h */,\n\t\t\t\tCBF19027243D6F5A001C82ED /* ExtensionDelegate.mm */,\n\t\t\t\tCBF19029243D6F5A001C82ED /* NotificationController.h */,\n\t\t\t\tCBF1902A243D6F5A001C82ED /* NotificationController.m */,\n\t\t\t\tCBF1902C243D6F5A001C82ED /* Assets.xcassets */,\n\t\t\t\tCBF1902E243D6F5A001C82ED /* Info.plist */,\n\t\t\t\tCBF1902F243D6F5A001C82ED /* PushNotificationPayload.apns */,\n\t\t\t);\n\t\t\tpath = \"WatchApp Extension\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t70A6308F2B7E7EC9003AB1DF /* MMKVVisionDemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 70A630A32B7E7ECA003AB1DF /* Build configuration list for PBXNativeTarget \"MMKVVisionDemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t70A6308C2B7E7EC9003AB1DF /* Sources */,\n\t\t\t\t70A6308D2B7E7EC9003AB1DF /* Frameworks */,\n\t\t\t\t70A6308E2B7E7EC9003AB1DF /* Resources */,\n\t\t\t\t702CE6862B7E85070015E8A4 /* Embed Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MMKVVisionDemo;\n\t\t\tpackageProductDependencies = (\n\t\t\t);\n\t\t\tproductName = MMKVVisionDemo;\n\t\t\tproductReference = 70A630902B7E7EC9003AB1DF /* MMKVVisionDemo.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCB1FD41F20455E2D00931B5F /* MMKVDemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB1FD43620455E2D00931B5F /* Build configuration list for PBXNativeTarget \"MMKVDemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB1FD41C20455E2D00931B5F /* Sources */,\n\t\t\t\tCB1FD41D20455E2D00931B5F /* Frameworks */,\n\t\t\t\tCB1FD41E20455E2D00931B5F /* Resources */,\n\t\t\t\tCB6D44DB23B3A44400F369AA /* Embed Foundation Extensions */,\n\t\t\t\tCBF19033243D6F5A001C82ED /* Embed Watch Content */,\n\t\t\t\tCB6180E926E90493003FF912 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB2C907D2BE9DB7D0052A2D7 /* PBXTargetDependency */,\n\t\t\t\tCB6D44D623B3A44400F369AA /* PBXTargetDependency */,\n\t\t\t\tCBF19031243D6F5A001C82ED /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVDemo;\n\t\t\tproductName = MMKVDemo;\n\t\t\tproductReference = CB1FD42020455E2D00931B5F /* MMKVDemo.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCB3166F92A4C6B5F0003221B /* kvdemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB3167132A4C6B600003221B /* Build configuration list for PBXNativeTarget \"kvdemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB3166F62A4C6B5F0003221B /* Sources */,\n\t\t\t\tCB3166F72A4C6B5F0003221B /* Frameworks */,\n\t\t\t\tCB3166F82A4C6B5F0003221B /* Resources */,\n\t\t\t\tCB3167162A4C6E380003221B /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = kvdemo;\n\t\t\tproductName = kvdemo;\n\t\t\tproductReference = CB3166FA2A4C6B5F0003221B /* kvdemo.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCB36F24820F8A20C009AF46F /* MMKVDemoTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB36F25520F8A20C009AF46F /* Build configuration list for PBXNativeTarget \"MMKVDemoTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB36F24520F8A20C009AF46F /* Sources */,\n\t\t\t\tCB36F24620F8A20C009AF46F /* Frameworks */,\n\t\t\t\tCB36F24720F8A20C009AF46F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB36F24F20F8A20C009AF46F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVDemoTests;\n\t\t\tproductName = MMKVDemoTests;\n\t\t\tproductReference = CB36F24920F8A20C009AF46F /* MMKVDemoTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\tCB6D44C923B3A44400F369AA /* MMKVTodayExtensionDemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB6D44D823B3A44400F369AA /* Build configuration list for PBXNativeTarget \"MMKVTodayExtensionDemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB6D44C623B3A44400F369AA /* Sources */,\n\t\t\t\tCB6D44C723B3A44400F369AA /* Frameworks */,\n\t\t\t\tCB6D44C823B3A44400F369AA /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCBB306652435EC6000CB593E /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVTodayExtensionDemo;\n\t\t\tproductName = MMKVTodayExtensionDemo;\n\t\t\tproductReference = CB6D44CA23B3A44400F369AA /* MMKVTodayExtensionDemo.appex */;\n\t\t\tproductType = \"com.apple.product-type.app-extension\";\n\t\t};\n\t\tCB76C5A32C3D699F008B0E61 /* MMKVCatalystDemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CB76C5BA2C3D69A1008B0E61 /* Build configuration list for PBXNativeTarget \"MMKVCatalystDemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCB76C5A02C3D699F008B0E61 /* Sources */,\n\t\t\t\tCB76C5A12C3D699F008B0E61 /* Frameworks */,\n\t\t\t\tCB76C5A22C3D699F008B0E61 /* Resources */,\n\t\t\t\tCB76C5BE2C3D6A0A008B0E61 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = MMKVCatalystDemo;\n\t\t\tproductName = MMKVCatalystDemo;\n\t\t\tproductReference = CB76C5A42C3D69A0008B0E61 /* MMKVCatalystDemo.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCBB6C81E215CDB9500487192 /* MMKVMacDemo */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBB6C833215CDB9600487192 /* Build configuration list for PBXNativeTarget \"MMKVMacDemo\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBB6C81B215CDB9500487192 /* Sources */,\n\t\t\t\tCBB6C81C215CDB9500487192 /* Frameworks */,\n\t\t\t\tCBB6C81D215CDB9500487192 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB9C2730215E5D2400448B6C /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = MMKVMacDemo;\n\t\t\tproductName = MMKVMacDemo;\n\t\t\tproductReference = CBB6C81F215CDB9500487192 /* MMKVMacDemo.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCBF19011243D6F59001C82ED /* WatchApp */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBF1903B243D6F5A001C82ED /* Build configuration list for PBXNativeTarget \"WatchApp\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBF19010243D6F59001C82ED /* Resources */,\n\t\t\t\tCBF19036243D6F5A001C82ED /* Embed Foundation Extensions */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCBF19021243D6F5A001C82ED /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = WatchApp;\n\t\t\tproductName = WatchApp;\n\t\t\tproductReference = CBF19012243D6F59001C82ED /* WatchApp.app */;\n\t\t\tproductType = \"com.apple.product-type.application.watchapp2\";\n\t\t};\n\t\tCBF1901D243D6F5A001C82ED /* WatchApp Extension */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CBF1903A243D6F5A001C82ED /* Build configuration list for PBXNativeTarget \"WatchApp Extension\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCBF1901A243D6F5A001C82ED /* Sources */,\n\t\t\t\tCBF1901B243D6F5A001C82ED /* Frameworks */,\n\t\t\t\tCBF1901C243D6F5A001C82ED /* Resources */,\n\t\t\t\tCBF190AE243D78D4001C82ED /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tCB5C469924AB8837001B25B8 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = \"WatchApp Extension\";\n\t\t\tproductName = \"WatchApp Extension\";\n\t\t\tproductReference = CBF1901E243D6F5A001C82ED /* WatchApp Extension.appex */;\n\t\t\tproductType = \"com.apple.product-type.watchkit2-extension\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tCB1FD41820455E2D00931B5F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastSwiftUpdateCheck = 1530;\n\t\t\t\tLastUpgradeCheck = 1530;\n\t\t\t\tORGANIZATIONNAME = Lingol;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t70A6308F2B7E7EC9003AB1DF = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 15.2;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\tCB1FD41F20455E2D00931B5F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1020;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.BackgroundModes = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\tCB3166F92A4C6B5F0003221B = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t\tCB36F24820F8A20C009AF46F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tTestTargetID = CB1FD41F20455E2D00931B5F;\n\t\t\t\t\t};\n\t\t\t\t\tCB6D44C923B3A44400F369AA = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.3;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t\tCB76C5A32C3D699F008B0E61 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 15.3;\n\t\t\t\t\t};\n\t\t\t\t\tCBB6C81E215CDB9500487192 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.0;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t\tCBF19011243D6F59001C82ED = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.4;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t\tCBF1901D243D6F5A001C82ED = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.4;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = CB1FD41B20455E2D00931B5F /* Build configuration list for PBXProject \"MMKVDemo\" */;\n\t\t\tcompatibilityVersion = \"Xcode 8.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = CB1FD41720455E2D00931B5F;\n\t\t\tproductRefGroup = CB1FD42120455E2D00931B5F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = CB1FD4B12046994C00931B5F /* Products */;\n\t\t\t\t\tProjectRef = CB1FD4B02046994C00931B5F /* MMKV.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tCB1FD41F20455E2D00931B5F /* MMKVDemo */,\n\t\t\t\tCB36F24820F8A20C009AF46F /* MMKVDemoTests */,\n\t\t\t\tCBB6C81E215CDB9500487192 /* MMKVMacDemo */,\n\t\t\t\tCB6D44C923B3A44400F369AA /* MMKVTodayExtensionDemo */,\n\t\t\t\tCBF19011243D6F59001C82ED /* WatchApp */,\n\t\t\t\tCBF1901D243D6F5A001C82ED /* WatchApp Extension */,\n\t\t\t\tCB3166F92A4C6B5F0003221B /* kvdemo */,\n\t\t\t\t70A6308F2B7E7EC9003AB1DF /* MMKVVisionDemo */,\n\t\t\t\tCB76C5A32C3D699F008B0E61 /* MMKVCatalystDemo */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\tCB1FD4B52046994D00931B5F /* libMMKV.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libMMKV.a;\n\t\t\tremoteRef = CB1FD4B42046994D00931B5F /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tCB36F25420F8A20C009AF46F /* MMKV.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = MMKV.framework;\n\t\t\tremoteRef = CB36F25320F8A20C009AF46F /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tCB467FBB2431EE2D00FD7421 /* MMKVAppExtension.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = MMKVAppExtension.framework;\n\t\t\tremoteRef = CB467FBA2431EE2D00FD7421 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tCBF1907C243D7116001C82ED /* MMKVWatchExtension.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = MMKVWatchExtension.framework;\n\t\t\tremoteRef = CBF1907B243D7116001C82ED /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t70A6308E2B7E7EC9003AB1DF /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t70A6309E2B7E7ECA003AB1DF /* Preview Assets.xcassets in Resources */,\n\t\t\t\t70A6309B2B7E7ECA003AB1DF /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB1FD41E20455E2D00931B5F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1FD43020455E2D00931B5F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tCBDC22E22C8ABDD20000DDA5 /* testReadOnly.crc in Resources */,\n\t\t\t\tCB1FD42D20455E2D00931B5F /* Assets.xcassets in Resources */,\n\t\t\t\tCBDC22E12C8ABDD20000DDA5 /* testReadOnly in Resources */,\n\t\t\t\tCB1FD42B20455E2D00931B5F /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB3166F82A4C6B5F0003221B /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB31670C2A4C6B600003221B /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tCB3167092A4C6B600003221B /* Assets.xcassets in Resources */,\n\t\t\t\tCB3167072A4C6B5F0003221B /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB36F24720F8A20C009AF46F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB6D44C823B3A44400F369AA /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB6D44D323B3A44400F369AA /* MainInterface.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB76C5A22C3D699F008B0E61 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB76C5B32C3D69A1008B0E61 /* Assets.xcassets in Resources */,\n\t\t\t\tCB76C5B62C3D69A1008B0E61 /* Base in Resources */,\n\t\t\t\tCB76C5B12C3D69A0008B0E61 /* Base in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBB6C81D215CDB9500487192 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBB6C828215CDB9600487192 /* Assets.xcassets in Resources */,\n\t\t\t\tCBB6C82B215CDB9600487192 /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF19010243D6F59001C82ED /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF19018243D6F5A001C82ED /* Assets.xcassets in Resources */,\n\t\t\t\tCBF19016243D6F59001C82ED /* Interface.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF1901C243D6F5A001C82ED /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF1902D243D6F5A001C82ED /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t70A6308C2B7E7EC9003AB1DF /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t70A630992B7E7EC9003AB1DF /* ContentView.swift in Sources */,\n\t\t\t\t70A630972B7E7EC9003AB1DF /* MMKVVisionDemoApp.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB1FD41C20455E2D00931B5F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB1FD42820455E2D00931B5F /* ViewController.mm in Sources */,\n\t\t\t\tCBB306612435E8F200CB593E /* ViewController+TestCaseBad.mm in Sources */,\n\t\t\t\tCBE6689F2D5C52E5005C184E /* TestMMKVCpp.cpp in Sources */,\n\t\t\t\tCB1FD43320455E2D00931B5F /* main.m in Sources */,\n\t\t\t\tCB9F963920FCA7C500A1C14C /* DemoSwiftUsage.swift in Sources */,\n\t\t\t\tCB1FD42520455E2D00931B5F /* AppDelegate.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB3166F62A4C6B5F0003221B /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB3167042A4C6B5F0003221B /* ViewController.m in Sources */,\n\t\t\t\tCB3166FE2A4C6B5F0003221B /* AppDelegate.m in Sources */,\n\t\t\t\tCB31670F2A4C6B600003221B /* main.m in Sources */,\n\t\t\t\tCB3167012A4C6B5F0003221B /* SceneDelegate.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB36F24520F8A20C009AF46F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB36F24C20F8A20C009AF46F /* MMKVDemoTests.mm in Sources */,\n\t\t\t\tCB36F25A20F8D728009AF46F /* MMKVPerformanceTest.mm in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB6D44C623B3A44400F369AA /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB6D44D023B3A44400F369AA /* TodayViewController.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCB76C5A02C3D699F008B0E61 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCB76C5AE2C3D69A0008B0E61 /* ViewController.mm in Sources */,\n\t\t\t\tCB76C5A82C3D69A0008B0E61 /* AppDelegate.m in Sources */,\n\t\t\t\tCB76C5B92C3D69A1008B0E61 /* main.m in Sources */,\n\t\t\t\tCB76C5AB2C3D69A0008B0E61 /* SceneDelegate.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBB6C81B215CDB9500487192 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBB6C826215CDB9500487192 /* ViewController.mm in Sources */,\n\t\t\t\tCBB6C82E215CDB9600487192 /* main.m in Sources */,\n\t\t\t\tCBB6C823215CDB9500487192 /* AppDelegate.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCBF1901A243D6F5A001C82ED /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCBF19028243D6F5A001C82ED /* ExtensionDelegate.mm in Sources */,\n\t\t\t\tCBF19025243D6F5A001C82ED /* InterfaceController.m in Sources */,\n\t\t\t\tCBF1902B243D6F5A001C82ED /* NotificationController.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tCB2C907D2BE9DB7D0052A2D7 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = MMKV;\n\t\t\ttargetProxy = CB2C907C2BE9DB7D0052A2D7 /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB36F24F20F8A20C009AF46F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = CB1FD41F20455E2D00931B5F /* MMKVDemo */;\n\t\t\ttargetProxy = CB36F24E20F8A20C009AF46F /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB5C469924AB8837001B25B8 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = MMKVWatchExtension;\n\t\t\ttargetProxy = CB5C469824AB8837001B25B8 /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB6D44D623B3A44400F369AA /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = CB6D44C923B3A44400F369AA /* MMKVTodayExtensionDemo */;\n\t\t\ttargetProxy = CB6D44D523B3A44400F369AA /* PBXContainerItemProxy */;\n\t\t};\n\t\tCB9C2730215E5D2400448B6C /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = \"MMKV Static\";\n\t\t\ttargetProxy = CB9C272F215E5D2400448B6C /* PBXContainerItemProxy */;\n\t\t};\n\t\tCBB306652435EC6000CB593E /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = MMKVAppExtension;\n\t\t\ttargetProxy = CBB306642435EC6000CB593E /* PBXContainerItemProxy */;\n\t\t};\n\t\tCBF19021243D6F5A001C82ED /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = CBF1901D243D6F5A001C82ED /* WatchApp Extension */;\n\t\t\ttargetProxy = CBF19020243D6F5A001C82ED /* PBXContainerItemProxy */;\n\t\t};\n\t\tCBF19031243D6F5A001C82ED /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = CBF19011243D6F59001C82ED /* WatchApp */;\n\t\t\ttargetProxy = CBF19030243D6F5A001C82ED /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tCB1FD42920455E2D00931B5F /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD42A20455E2D00931B5F /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB1FD42E20455E2D00931B5F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB1FD42F20455E2D00931B5F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB3167052A4C6B5F0003221B /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB3167062A4C6B5F0003221B /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB31670A2A4C6B600003221B /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB31670B2A4C6B600003221B /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB6D44D123B3A44400F369AA /* MainInterface.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB6D44D223B3A44400F369AA /* Base */,\n\t\t\t);\n\t\t\tname = MainInterface.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB76C5AF2C3D69A0008B0E61 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB76C5B02C3D69A0008B0E61 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCB76C5B42C3D69A1008B0E61 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCB76C5B52C3D69A1008B0E61 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBB6C829215CDB9600487192 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCBB6C82A215CDB9600487192 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBF19014243D6F59001C82ED /* Interface.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tCBF19015243D6F59001C82ED /* Base */,\n\t\t\t);\n\t\t\tname = Interface.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t70A630A02B7E7ECA003AB1DF /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"MMKVVisionDemo/Preview Content\\\"\";\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(TARGET_NAME)/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVVisionDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = xros;\n\t\t\t\tSUPPORTED_PLATFORMS = \"xros xrsimulator\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,7\";\n\t\t\t\tXROS_DEPLOYMENT_TARGET = 1.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t70A630A12B7E7ECA003AB1DF /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"MMKVVisionDemo/Preview Content\\\"\";\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(TARGET_NAME)/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVVisionDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = xros;\n\t\t\t\tSUPPORTED_PLATFORMS = \"xros xrsimulator\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2,7\";\n\t\t\t\tXROS_DEPLOYMENT_TARGET = 1.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB1FD43420455E2D00931B5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB1FD43520455E2D00931B5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB1FD43720455E2D00931B5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVDemo/Resources/MMKVDemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tINFOPLIST_FILE = MMKVDemo/Resources/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKVDemo;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"MMKVDemo/MMKVDemo-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB1FD43820455E2D00931B5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVDemo/Resources/MMKVDemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tINFOPLIST_FILE = MMKVDemo/Resources/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKVDemo;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"MMKVDemo/MMKVDemo-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB3167102A4C6B600003221B /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = kvdemo/kvdemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = kvdemo/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.kvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = mmkvdemo2;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB3167112A4C6B600003221B /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = kvdemo/kvdemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = kvdemo/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.kvdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = mmkvdemo2;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB36F25020F8A20C009AF46F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 52LMU3A345;\n\t\t\t\tINFOPLIST_FILE = MMKVDemoTests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemoTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/MMKVDemo.app/MMKVDemo\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB36F25120F8A20C009AF46F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 52LMU3A345;\n\t\t\t\tINFOPLIST_FILE = MMKVDemoTests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemoTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/MMKVDemo.app/MMKVDemo\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB6D44D923B3A44400F369AA /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVTodayExtensionDemo/MMKVTodayExtensionDemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tINFOPLIST_FILE = MMKVTodayExtensionDemo/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = \"$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.MMKVTodayExtensionDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVTodayExtension;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB6D44DA23B3A44400F369AA /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVTodayExtensionDemo/MMKVTodayExtensionDemo.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tINFOPLIST_FILE = MMKVTodayExtensionDemo/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = \"$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)\";\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.MMKVTodayExtensionDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVTodayExtension;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCB76C5BB2C3D69A1008B0E61 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVCatalystDemo/MMKVCatalystDemo.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = MMKVCatalystDemo/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVCatalystDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKViOSCatalystDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]\" = MMKVCatalystDemo;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCB76C5BC2C3D69A1008B0E61 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVCatalystDemo/MMKVCatalystDemo.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = MMKVCatalystDemo/Info.plist;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.MMKVCatalystDemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = MMKViOSCatalystDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]\" = MMKVCatalystDemo;\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBB6C830215CDB9600487192 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVMacDemo/MMKVMacDemo.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tINFOPLIST_FILE = MMKVMacDemo/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvmacdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVMacDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]\" = MMKVMacDemo;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBB6C831215CDB9600487192 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = MMKVMacDemo/MMKVMacDemo.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = SG5PVJM4JW;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tINFOPLIST_FILE = MMKVMacDemo/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvmacdemo;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVMacDemo;\n\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]\" = MMKVMacDemo;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBF19034243D6F5A001C82ED /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tIBSC_MODULE = WatchApp_Extension;\n\t\t\t\tINFOPLIST_FILE = WatchApp/Info.plist;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.watchkitapp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVWatchApp;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBF19035243D6F5A001C82ED /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tIBSC_MODULE = WatchApp_Extension;\n\t\t\t\tINFOPLIST_FILE = WatchApp/Info.plist;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.watchkitapp;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVWatchApp;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCBF19037243D6F5A001C82ED /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tINFOPLIST_FILE = \"WatchApp Extension/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.watchkitapp.watchkitextension;\n\t\t\t\tPRODUCT_NAME = \"${TARGET_NAME}\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVWatchExtension;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCBF19038243D6F5A001C82ED /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = SG5PVJM4JW;\n\t\t\t\tEXCLUDED_ARCHS = armv7k;\n\t\t\t\tINFOPLIST_FILE = \"WatchApp Extension/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tencent.mmkvdemo.watchkitapp.watchkitextension;\n\t\t\t\tPRODUCT_NAME = \"${TARGET_NAME}\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = MMKVWatchExtension;\n\t\t\t\tSDKROOT = watchos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 4;\n\t\t\t\tWATCHOS_DEPLOYMENT_TARGET = 6.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t70A630A32B7E7ECA003AB1DF /* Build configuration list for PBXNativeTarget \"MMKVVisionDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t70A630A02B7E7ECA003AB1DF /* Debug */,\n\t\t\t\t70A630A12B7E7ECA003AB1DF /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB1FD41B20455E2D00931B5F /* Build configuration list for PBXProject \"MMKVDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB1FD43420455E2D00931B5F /* Debug */,\n\t\t\t\tCB1FD43520455E2D00931B5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB1FD43620455E2D00931B5F /* Build configuration list for PBXNativeTarget \"MMKVDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB1FD43720455E2D00931B5F /* Debug */,\n\t\t\t\tCB1FD43820455E2D00931B5F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB3167132A4C6B600003221B /* Build configuration list for PBXNativeTarget \"kvdemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB3167102A4C6B600003221B /* Debug */,\n\t\t\t\tCB3167112A4C6B600003221B /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB36F25520F8A20C009AF46F /* Build configuration list for PBXNativeTarget \"MMKVDemoTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB36F25020F8A20C009AF46F /* Debug */,\n\t\t\t\tCB36F25120F8A20C009AF46F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB6D44D823B3A44400F369AA /* Build configuration list for PBXNativeTarget \"MMKVTodayExtensionDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB6D44D923B3A44400F369AA /* Debug */,\n\t\t\t\tCB6D44DA23B3A44400F369AA /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCB76C5BA2C3D69A1008B0E61 /* Build configuration list for PBXNativeTarget \"MMKVCatalystDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCB76C5BB2C3D69A1008B0E61 /* Debug */,\n\t\t\t\tCB76C5BC2C3D69A1008B0E61 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBB6C833215CDB9600487192 /* Build configuration list for PBXNativeTarget \"MMKVMacDemo\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBB6C830215CDB9600487192 /* Debug */,\n\t\t\t\tCBB6C831215CDB9600487192 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBF1903A243D6F5A001C82ED /* Build configuration list for PBXNativeTarget \"WatchApp Extension\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBF19037243D6F5A001C82ED /* Debug */,\n\t\t\t\tCBF19038243D6F5A001C82ED /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCBF1903B243D6F5A001C82ED /* Build configuration list for PBXNativeTarget \"WatchApp\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCBF19034243D6F5A001C82ED /* Debug */,\n\t\t\t\tCBF19035243D6F5A001C82ED /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = CB1FD41820455E2D00931B5F /* Project object */;\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:MMKVDemo.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/MMKVDemo.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n               BuildableName = \"MMKVDemo.app\"\n               BlueprintName = \"MMKVDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB36F24820F8A20C009AF46F\"\n               BuildableName = \"MMKVDemoTests.xctest\"\n               BlueprintName = \"MMKVDemoTests\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n            BuildableName = \"MMKVDemo.app\"\n            BlueprintName = \"MMKVDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n            BuildableName = \"MMKVDemo.app\"\n            BlueprintName = \"MMKVDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/MMKVMacDemo.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBB6C81E215CDB9500487192\"\n               BuildableName = \"MMKVMacDemo.app\"\n               BlueprintName = \"MMKVMacDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBB6C81E215CDB9500487192\"\n            BuildableName = \"MMKVMacDemo.app\"\n            BlueprintName = \"MMKVMacDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBB6C81E215CDB9500487192\"\n            BuildableName = \"MMKVMacDemo.app\"\n            BlueprintName = \"MMKVMacDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/MMKVTodayExtensionDemo.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   wasCreatedForAppExtension = \"YES\"\n   version = \"2.0\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB6D44C923B3A44400F369AA\"\n               BuildableName = \"MMKVTodayExtensionDemo.appex\"\n               BlueprintName = \"MMKVTodayExtensionDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n               BuildableName = \"MMKVDemo.app\"\n               BlueprintName = \"MMKVDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB36F24820F8A20C009AF46F\"\n               BuildableName = \"MMKVDemoTests.xctest\"\n               BlueprintName = \"MMKVDemoTests\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"\"\n      selectedLauncherIdentifier = \"Xcode.IDEFoundation.Launcher.PosixSpawn\"\n      launchStyle = \"0\"\n      askForAppToLaunch = \"Yes\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\"\n      launchAutomaticallySubstyle = \"2\">\n      <RemoteRunnable\n         runnableDebuggingMode = \"2\"\n         BundleIdentifier = \"com.apple.springboard\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB6D44C923B3A44400F369AA\"\n            BuildableName = \"MMKVTodayExtensionDemo.appex\"\n            BlueprintName = \"MMKVTodayExtensionDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </RemoteRunnable>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n            BuildableName = \"MMKVDemo.app\"\n            BlueprintName = \"MMKVDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      launchAutomaticallySubstyle = \"2\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n            BuildableName = \"MMKVDemo.app\"\n            BlueprintName = \"MMKVDemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/WatchApp (Notification).xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"2.0\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n               BuildableName = \"WatchApp.app\"\n               BlueprintName = \"WatchApp\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n               BuildableName = \"MMKVDemo.app\"\n               BlueprintName = \"MMKVDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB36F24820F8A20C009AF46F\"\n               BuildableName = \"MMKVDemoTests.xctest\"\n               BlueprintName = \"MMKVDemoTests\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\"\n      launchAutomaticallySubstyle = \"8\"\n      notificationPayloadFile = \"WatchApp Extension/PushNotificationPayload.apns\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n            BuildableName = \"WatchApp.app\"\n            BlueprintName = \"WatchApp\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      launchAutomaticallySubstyle = \"8\"\n      notificationPayloadFile = \"WatchApp Extension/PushNotificationPayload.apns\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n            BuildableName = \"WatchApp.app\"\n            BlueprintName = \"WatchApp\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n               BuildableName = \"WatchApp.app\"\n               BlueprintName = \"WatchApp\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB1FD41F20455E2D00931B5F\"\n               BuildableName = \"MMKVDemo.app\"\n               BlueprintName = \"MMKVDemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB36F24820F8A20C009AF46F\"\n               BuildableName = \"MMKVDemoTests.xctest\"\n               BlueprintName = \"MMKVDemoTests\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n            BuildableName = \"WatchApp.app\"\n            BlueprintName = \"WatchApp\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CBF19011243D6F59001C82ED\"\n            BuildableName = \"WatchApp.app\"\n            BlueprintName = \"WatchApp\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemo.xcodeproj/xcshareddata/xcschemes/kvdemo.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CB3166F92A4C6B5F0003221B\"\n               BuildableName = \"kvdemo.app\"\n               BlueprintName = \"kvdemo\"\n               ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Release\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB3166F92A4C6B5F0003221B\"\n            BuildableName = \"kvdemo.app\"\n            BlueprintName = \"kvdemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CB3166F92A4C6B5F0003221B\"\n            BuildableName = \"kvdemo.app\"\n            BlueprintName = \"kvdemo\"\n            ReferencedContainer = \"container:MMKVDemo.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemoTests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemoTests/MMKVDemoTests.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <MMKV/MMKV.h>\n#import <XCTest/XCTest.h>\n#import <numeric>\n\nusing namespace std;\n\n#define KeyNotExist @\"KeyNotExist\"\n\n@interface MockNSCoding : NSObject <NSCoding>\n@property NSString *string1;\n@property NSString *string2;\n@property NSUInteger integer;\n@property NSSet *set;\n\n- (BOOL)isEqualToObject:(MockNSCoding *)object;\n@end\n\n@implementation MockNSCoding\n\n#pragma mark - NSCoding\n\n- (id)initWithCoder:(NSCoder *)decoder {\n    self = [super init];\n    if (!self) {\n        return nil;\n    }\n\n    self.string1 = [decoder decodeObjectForKey:@\"string1\"];\n    self.string2 = [decoder decodeObjectForKey:@\"string2\"];\n    self.integer = [decoder decodeIntegerForKey:@\"integer\"];\n    self.set = [decoder decodeObjectForKey:@\"set\"];\n\n    return self;\n}\n\n- (void)encodeWithCoder:(NSCoder *)encoder {\n    [encoder encodeObject:self.string1 forKey:@\"string1\"];\n    [encoder encodeObject:self.string2 forKey:@\"string2\"];\n    [encoder encodeInteger:self.integer forKey:@\"integer\"];\n    [encoder encodeObject:self.set forKey:@\"set\"];\n}\n\n- (BOOL)isEqualToObject:(MockNSCoding *)object {\n    return [self.string1 isEqualToString:object.string1] &&\n           [self.string2 isEqualToString:object.string2] &&\n           self.integer == object.integer &&\n           [self.set isEqualToSet:object.set];\n}\n\n@end\n\n@interface MMKVDemoTests : XCTestCase\n\n@end\n\n@implementation MMKVDemoTests {\n    MMKV *mmkv;\n}\n\n- (void)setUp {\n    [super setUp];\n\n    mmkv = [MMKV mmkvWithID:@\"unit_test\"];\n}\n\n- (void)tearDown {\n    mmkv = nil;\n\n    [super tearDown];\n}\n\n- (void)testBool {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setBool:YES forKey:@\"bool\"];\n    XCTAssertEqual(ret, YES);\n\n    BOOL value = [mmkv getBoolForKey:@\"bool\"];\n    XCTAssertEqual(value, YES);\n\n    value = [mmkv getBoolForKey:KeyNotExist];\n    XCTAssertEqual(value, NO);\n\n    value = [mmkv getBoolForKey:KeyNotExist defaultValue:YES];\n    XCTAssertEqual(value, YES);\n}\n\n- (void)testInt32 {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setInt32:numeric_limits<int32_t>::max() forKey:@\"int32\"];\n    XCTAssertEqual(ret, YES);\n\n    int32_t value = [mmkv getInt32ForKey:@\"int32\"];\n    XCTAssertEqual(value, numeric_limits<int32_t>::max());\n\n    value = [mmkv getInt32ForKey:KeyNotExist];\n    XCTAssertEqual(value, 0);\n\n    value = [mmkv getInt32ForKey:KeyNotExist defaultValue:-1];\n    XCTAssertEqual(value, -1);\n}\n\n- (void)testInt64 {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setInt64:numeric_limits<int64_t>::max() forKey:@\"int64\"];\n    XCTAssertEqual(ret, YES);\n\n    int64_t value = [mmkv getInt64ForKey:@\"int64\"];\n    XCTAssertEqual(value, numeric_limits<int64_t>::max());\n\n    value = [mmkv getInt64ForKey:KeyNotExist];\n    XCTAssertEqual(value, 0);\n\n    value = [mmkv getInt64ForKey:KeyNotExist defaultValue:-1];\n    XCTAssertEqual(value, -1);\n}\n\n- (void)testUInt32 {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setUInt32:numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    XCTAssertEqual(ret, YES);\n\n    uint32_t value = [mmkv getUInt32ForKey:@\"uint32\"];\n    XCTAssertEqual(value, numeric_limits<uint32_t>::max());\n\n    value = [mmkv getUInt32ForKey:KeyNotExist];\n    XCTAssertEqual(value, 0);\n\n    value = [mmkv getUInt32ForKey:KeyNotExist defaultValue:-1];\n    XCTAssertEqual(value, -1);\n}\n\n- (void)testUInt64 {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setUInt64:numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    XCTAssertEqual(ret, YES);\n\n    uint64_t value = [mmkv getUInt64ForKey:@\"uint64\"];\n    XCTAssertEqual(value, numeric_limits<uint64_t>::max());\n\n    value = [mmkv getUInt64ForKey:KeyNotExist];\n    XCTAssertEqual(value, 0);\n\n    value = [mmkv getUInt64ForKey:KeyNotExist defaultValue:1024];\n    XCTAssertEqual(value, 1024);\n}\n\n- (void)testFloat {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setFloat:numeric_limits<float>::max() forKey:@\"float\"];\n    XCTAssertEqual(ret, YES);\n\n    float value = [mmkv getFloatForKey:@\"float\"];\n    XCTAssertEqualWithAccuracy(value, numeric_limits<float>::max(), 0.001);\n\n    value = [mmkv getFloatForKey:KeyNotExist];\n    XCTAssertEqualWithAccuracy(value, 0, 0.001);\n\n    value = [mmkv getFloatForKey:KeyNotExist defaultValue:-1];\n    XCTAssertEqualWithAccuracy(value, -1, 0.001);\n}\n\n- (void)testDouble {\n    // This is an example of a functional test case.\n    // Use XCTAssert and related functions to verify your tests produce the correct results.\n    BOOL ret = [mmkv setDouble:numeric_limits<double>::max() forKey:@\"double\"];\n    XCTAssertEqual(ret, YES);\n\n    double value = [mmkv getDoubleForKey:@\"double\"];\n    XCTAssertEqualWithAccuracy(value, numeric_limits<double>::max(), 0.001);\n\n    value = [mmkv getDoubleForKey:KeyNotExist];\n    XCTAssertEqualWithAccuracy(value, 0, 0.001);\n\n    value = [mmkv getDoubleForKey:KeyNotExist defaultValue:-1];\n    XCTAssertEqualWithAccuracy(value, -1, 0.001);\n}\n\n- (void)testNSString {\n    NSString *str = @\"Hello 2018 world cup 世界杯\";\n    BOOL ret = [mmkv setObject:str forKey:@\"string\"];\n    XCTAssertEqual(ret, YES);\n\n    NSString *value = [mmkv getObjectOfClass:NSString.class forKey:@\"string\"];\n    XCTAssertEqualObjects(value, str);\n\n    value = [mmkv getObjectOfClass:NSString.class forKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSStringForNewGetSet {\n    NSString *str = @\"Hello 2018 world cup 世界杯\";\n    BOOL ret = [mmkv setString:str forKey:@\"string\"];\n    XCTAssertEqual(ret, YES);\n\n    NSString *value = [mmkv getStringForKey:@\"string\"];\n    XCTAssertEqualObjects(value, str);\n\n    value = [mmkv getStringForKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSData {\n    NSString *str = @\"Hello 2018 world cup 世界杯\";\n    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];\n    BOOL ret = [mmkv setObject:data forKey:@\"data\"];\n    XCTAssertEqual(ret, YES);\n\n    NSData *value = [mmkv getObjectOfClass:NSData.class forKey:@\"data\"];\n    XCTAssertEqualObjects(value, data);\n\n    value = [mmkv getObjectOfClass:NSData.class forKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSDataForNewGetSet {\n    NSString *str = @\"Hello 2018 world cup 世界杯\";\n    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];\n    BOOL ret = [mmkv setData:data forKey:@\"data\"];\n    XCTAssertEqual(ret, YES);\n\n    NSData *value = [mmkv getDataForKey:@\"data\"];\n    XCTAssertEqualObjects(value, data);\n\n    value = [mmkv getDataForKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSDate {\n    NSDate *date = [NSDate date];\n    BOOL ret = [mmkv setObject:date forKey:@\"date\"];\n    XCTAssertEqual(ret, YES);\n\n    NSDate *value = [mmkv getObjectOfClass:NSDate.class forKey:@\"date\"];\n    [self compareDate:date withDate:value];\n\n    value = [mmkv getObjectOfClass:NSDate.class forKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSDateForNewGetSet {\n    NSDate *date = [NSDate date];\n    BOOL ret = [mmkv setDate:date forKey:@\"date\"];\n    XCTAssertEqual(ret, YES);\n\n    NSDate *value = [mmkv getDateForKey:@\"date\"];\n    [self compareDate:date withDate:value];\n\n    value = [mmkv getObjectOfClass:NSDate.class forKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n- (void)testNSDictionary {\n    NSDictionary *dic = @{@\"key1\" : @\"value1\",\n                          @\"key2\" : @(2)};\n    BOOL ret = [mmkv setObject:dic forKey:@\"dictionary\"];\n    XCTAssertTrue(ret);\n\n    NSDictionary *value = [mmkv getObjectOfClass:[NSDictionary class] forKey:@\"dictionary\"];\n    XCTAssertTrue([@\"value1\" isEqualToString:value[@\"key1\"]]);\n    XCTAssertTrue(2 == [value[@\"key2\"] intValue]);\n}\n\n- (void)testNSMutableDictionary {\n    NSMutableDictionary *dic = [@{@\"key1\" : @\"value1\",\n                                  @\"key2\" : @(2)} mutableCopy];\n    BOOL ret = [mmkv setObject:dic forKey:@\"dictionary\"];\n    XCTAssertTrue(ret);\n\n    NSDictionary *value = [mmkv getObjectOfClass:[NSDictionary class] forKey:@\"dictionary\"];\n    XCTAssertTrue([@\"value1\" isEqualToString:value[@\"key1\"]]);\n    XCTAssertTrue(2 == [value[@\"key2\"] intValue]);\n}\n\n- (void)testNSArray {\n    NSArray *array = @[ @\"0\", @\"1\", @\"2\" ];\n    BOOL ret = [mmkv setObject:array forKey:@\"array\"];\n    XCTAssertTrue(ret);\n\n    NSArray *value = [mmkv getObjectOfClass:[NSDictionary class] forKey:@\"array\"];\n    XCTAssertTrue([@\"0\" isEqualToString:value[0]]);\n    XCTAssertTrue([@\"1\" isEqualToString:value[1]]);\n    XCTAssertTrue([@\"2\" isEqualToString:value[2]]);\n}\n\n- (void)testNSMutableArray {\n    NSMutableArray *array = [@[ @\"0\", @\"1\", @\"2\" ] copy];\n    BOOL ret = [mmkv setObject:array forKey:@\"array\"];\n    XCTAssertTrue(ret);\n\n    NSArray *value = [mmkv getObjectOfClass:[NSDictionary class] forKey:@\"array\"];\n    XCTAssertTrue([@\"0\" isEqualToString:value[0]]);\n    XCTAssertTrue([@\"1\" isEqualToString:value[1]]);\n    XCTAssertTrue([@\"2\" isEqualToString:value[2]]);\n}\n\n- (void)testNSSet {\n    NSSet *set = [NSSet setWithObjects:@1, @2, @3, @4, @4, nil];\n    BOOL ret = [mmkv setObject:set forKey:@\"set\"];\n    XCTAssertTrue(ret);\n\n    NSSet *value = [mmkv getObjectOfClass:[NSSet class] forKey:@\"set\"];\n    XCTAssertTrue([value isEqualToSet:set]);\n}\n\n- (void)testNSMutableSet {\n    NSMutableSet *set = [NSMutableSet setWithObjects:@1, @2, @3, @4, @4, nil];\n    BOOL ret = [mmkv setObject:set forKey:@\"set\"];\n    XCTAssertTrue(ret);\n\n    NSSet *value = [mmkv getObjectOfClass:[NSSet class] forKey:@\"set\"];\n    XCTAssertTrue([value isEqualToSet:set]);\n}\n\n- (void)testCustomNSCodingObject {\n    MockNSCoding *object = [[MockNSCoding alloc] init];\n    object.string1 = @\"hello\";\n    object.string2 = @\"world\";\n    object.integer = 1024;\n    object.set = [NSSet setWithObjects:@1, @2, @3, @4, @4, nil];\n\n    BOOL ret = [mmkv setObject:object forKey:@\"MockNSCoding\"];\n    XCTAssertTrue(ret);\n\n    MockNSCoding *value = [mmkv getObjectOfClass:[MockNSCoding class] forKey:@\"MockNSCoding\"];\n    XCTAssertTrue([value isEqualToObject:object]);\n}\n\n- (void)testCustomNSCodingObjectInArray {\n    MockNSCoding *object1 = [[MockNSCoding alloc] init];\n    object1.string1 = @\"hello\";\n    object1.string2 = @\"world\";\n    object1.integer = 1024;\n    object1.set = [NSSet setWithObjects:@1, @2, @3, @4, @4, nil];\n\n    MockNSCoding *object2 = [[MockNSCoding alloc] init];\n    object2.string1 = @\"hello\";\n    object2.string2 = @\"100mango\";\n    object2.integer = 1023;\n    object2.set = [NSSet setWithObjects:@1, @2, @3, @4, @4, nil];\n\n    MockNSCoding *object3 = [[MockNSCoding alloc] init];\n    object3.string1 = @\"hello\";\n    object3.string2 = @\"apple\";\n    object3.integer = 1023;\n    object3.set = [NSSet setWithObjects:@1, @2, @3, @4, @4, nil];\n\n    BOOL ret = [mmkv setObject:@[ object1, object2, object3 ] forKey:@\"MockNSCoding\"];\n    XCTAssertTrue(ret);\n\n    NSArray *value = [mmkv getObjectOfClass:[NSArray class] forKey:@\"MockNSCoding\"];\n    XCTAssertTrue([value[0] isEqualToObject:object1]);\n    XCTAssertTrue([value[1] isEqualToObject:object2]);\n    XCTAssertTrue([value[2] isEqualToObject:object3]);\n}\n\n- (void)testRemove {\n    BOOL ret = [mmkv setBool:YES forKey:@\"bool_1\"];\n    ret &= [mmkv setInt32:numeric_limits<int32_t>::max() forKey:@\"int_1\"];\n    ret &= [mmkv setInt64:numeric_limits<int64_t>::max() forKey:@\"long_1\"];\n    ret &= [mmkv setFloat:numeric_limits<float>::min() forKey:@\"float_1\"];\n    ret &= [mmkv setDouble:numeric_limits<double>::min() forKey:@\"double_1\"];\n    ret &= [mmkv setObject:@\"hello\" forKey:@\"string_1\"];\n    ret &= [mmkv setObject:@{@\"key\" : @\"value\"} forKey:@\"dictionary\"];\n    XCTAssertEqual(ret, YES);\n\n    {\n        long count = mmkv.count;\n\n        [mmkv removeValueForKey:@\"bool_1\"];\n        [mmkv removeValuesForKeys:@[ @\"int_1\", @\"long_1\" ]];\n\n        long newCount = mmkv.count;\n        XCTAssertEqual(count, newCount + 3);\n    }\n\n    BOOL bValue = [mmkv getBoolForKey:@\"bool_1\"];\n    XCTAssertEqual(bValue, NO);\n\n    int32_t iValue = [mmkv getInt32ForKey:@\"int_1\"];\n    XCTAssertEqual(iValue, 0);\n\n    int64_t lValue = [mmkv getInt64ForKey:@\"long_1\"];\n    XCTAssertEqual(lValue, 0);\n\n    float fValue = [mmkv getFloatForKey:@\"float_1\"];\n    XCTAssertEqualWithAccuracy(fValue, numeric_limits<float>::min(), 0.001);\n\n    double dValue = [mmkv getDoubleForKey:@\"double_1\"];\n    XCTAssertEqualWithAccuracy(dValue, numeric_limits<double>::min(), 0.001);\n\n    NSString *sValue = [mmkv getObjectOfClass:NSString.class forKey:@\"string_1\"];\n    XCTAssertEqualObjects(sValue, @\"hello\");\n}\n\n- (void)compareDate:(NSDate *)date withDate:(NSDate *)other {\n    XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, other.timeIntervalSince1970, 0.001);\n}\n\n- (void)testImportFromNSUserDefaults {\n    NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@\"testNSUserDefaults\"];\n    [userDefault setBool:YES forKey:@\"bool\"];\n    [userDefault setInteger:std::numeric_limits<NSInteger>::max() forKey:@\"NSInteger\"];\n    [userDefault setFloat:3.14 forKey:@\"float\"];\n    [userDefault setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    [userDefault setObject:@\"hello, NSUserDefaults\" forKey:@\"string\"];\n    [userDefault setObject:[NSDate date] forKey:@\"date\"];\n    [userDefault setObject:[@\"hello, NSUserDefaults again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    [userDefault setURL:[NSURL URLWithString:@\"https://mail.qq.com\"] forKey:@\"url\"];\n\n    NSNumber *number = [NSNumber numberWithBool:YES];\n    [userDefault setObject:number forKey:@\"number_bool\"];\n\n    number = [NSNumber numberWithChar:std::numeric_limits<char>::min()];\n    [userDefault setObject:number forKey:@\"number_char\"];\n\n    number = [NSNumber numberWithUnsignedChar:std::numeric_limits<unsigned char>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_char\"];\n\n    number = [NSNumber numberWithShort:std::numeric_limits<short>::min()];\n    [userDefault setObject:number forKey:@\"number_short\"];\n\n    number = [NSNumber numberWithUnsignedShort:std::numeric_limits<unsigned short>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_short\"];\n\n    number = [NSNumber numberWithInt:std::numeric_limits<int>::min()];\n    [userDefault setObject:number forKey:@\"number_int\"];\n\n    number = [NSNumber numberWithUnsignedInt:std::numeric_limits<unsigned int>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_int\"];\n\n    number = [NSNumber numberWithLong:std::numeric_limits<long>::min()];\n    [userDefault setObject:number forKey:@\"number_long\"];\n\n    number = [NSNumber numberWithUnsignedLong:std::numeric_limits<unsigned long>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_long\"];\n\n    number = [NSNumber numberWithLongLong:std::numeric_limits<long long>::min()];\n    [userDefault setObject:number forKey:@\"number_long_long\"];\n\n    number = [NSNumber numberWithUnsignedLongLong:std::numeric_limits<unsigned long long>::max()];\n    [userDefault setObject:number forKey:@\"number_unsigned_long_long\"];\n\n    number = [NSNumber numberWithFloat:3.1415];\n    [userDefault setObject:number forKey:@\"number_float\"];\n\n    number = [NSNumber numberWithDouble:std::numeric_limits<double>::max()];\n    [userDefault setObject:number forKey:@\"number_double\"];\n\n    number = [NSNumber numberWithInteger:std::numeric_limits<NSInteger>::min()];\n    [userDefault setObject:number forKey:@\"number_NSInteger\"];\n\n    number = [NSNumber numberWithUnsignedInteger:std::numeric_limits<NSUInteger>::max()];\n    [userDefault setObject:number forKey:@\"number_NSUInteger\"];\n\n    [mmkv migrateFromUserDefaults:userDefault];\n\n    XCTAssertEqual([mmkv getBoolForKey:@\"bool\"], [userDefault boolForKey:@\"bool\"]);\n    XCTAssertEqual([mmkv getInt64ForKey:@\"NSInteger\"], [userDefault integerForKey:@\"NSInteger\"]);\n    XCTAssertEqualWithAccuracy([mmkv getFloatForKey:@\"float\"], [userDefault floatForKey:@\"float\"], 0.001);\n    XCTAssertEqualWithAccuracy([mmkv getDoubleForKey:@\"double\"], [userDefault doubleForKey:@\"double\"], 0.001);\n    XCTAssertEqualObjects([mmkv getStringForKey:@\"string\"], [userDefault stringForKey:@\"string\"]);\n    [self compareDate:[mmkv getDateForKey:@\"date\"] withDate:[userDefault objectForKey:@\"date\"]];\n    XCTAssertEqualObjects([mmkv getDataForKey:@\"data\"], [userDefault dataForKey:@\"data\"]);\n    XCTAssertEqualObjects([NSKeyedUnarchiver unarchivedObjectOfClass:NSURL.class fromData:[mmkv getDataForKey:@\"url\"] error:nil], [userDefault URLForKey:@\"url\"]);\n\n    number = [userDefault objectForKey:@\"number_bool\"];\n    XCTAssertEqual([mmkv getBoolForKey:@\"number_bool\"], number.boolValue);\n\n    number = [userDefault objectForKey:@\"number_char\"];\n    XCTAssertEqual([mmkv getInt32ForKey:@\"number_char\"], number.charValue);\n\n    number = [userDefault objectForKey:@\"number_unsigned_char\"];\n    XCTAssertEqual([mmkv getInt32ForKey:@\"number_unsigned_char\"], number.unsignedCharValue);\n\n    number = [userDefault objectForKey:@\"number_short\"];\n    XCTAssertEqual([mmkv getInt32ForKey:@\"number_short\"], number.shortValue);\n\n    number = [userDefault objectForKey:@\"number_unsigned_short\"];\n    XCTAssertEqual([mmkv getInt32ForKey:@\"number_unsigned_short\"], number.unsignedShortValue);\n\n    number = [userDefault objectForKey:@\"number_int\"];\n    XCTAssertEqual([mmkv getInt32ForKey:@\"number_int\"], number.intValue);\n\n    number = [userDefault objectForKey:@\"number_unsigned_int\"];\n    XCTAssertEqual([mmkv getUInt32ForKey:@\"number_unsigned_int\"], number.unsignedIntValue);\n\n    number = [userDefault objectForKey:@\"number_long\"];\n    XCTAssertEqual([mmkv getInt64ForKey:@\"number_long\"], number.longValue);\n\n    number = [userDefault objectForKey:@\"number_unsigned_long\"];\n    XCTAssertEqual([mmkv getUInt64ForKey:@\"number_unsigned_long\"], number.unsignedLongValue);\n\n    number = [userDefault objectForKey:@\"number_long_long\"];\n    XCTAssertEqual([mmkv getInt64ForKey:@\"number_long_long\"], number.longLongValue);\n\n    number = [userDefault objectForKey:@\"number_unsigned_long_long\"];\n    XCTAssertEqual([mmkv getUInt64ForKey:@\"number_unsigned_long_long\"], number.unsignedLongLongValue);\n\n    number = [userDefault objectForKey:@\"number_float\"];\n    XCTAssertEqualWithAccuracy([mmkv getFloatForKey:@\"number_float\"], number.floatValue, 0.001);\n\n    number = [userDefault objectForKey:@\"number_double\"];\n    XCTAssertEqualWithAccuracy([mmkv getDoubleForKey:@\"number_double\"], number.doubleValue, 0.001);\n\n    number = [userDefault objectForKey:@\"number_NSInteger\"];\n    XCTAssertEqual([mmkv getInt64ForKey:@\"number_NSInteger\"], number.integerValue);\n\n    number = [userDefault objectForKey:@\"number_NSUInteger\"];\n    XCTAssertEqual([mmkv getUInt64ForKey:@\"number_NSUInteger\"], number.unsignedIntegerValue);\n}\n\n- (void)testMultiTimesOverwriteValue {\n    NSData *data;\n    int loops = 1000000;\n    for (int index = 0; index < loops; index++) {\n        NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n        data = [str dataUsingEncoding:NSUTF8StringEncoding];\n        BOOL ret = [mmkv setData:data forKey:@\"data\"];\n        XCTAssertEqual(ret, YES);\n    }\n    NSData *value = [mmkv getObjectOfClass:NSData.class forKey:@\"data\"];\n    XCTAssertEqualObjects(value, data);\n\n    value = [mmkv getObjectOfClass:NSData.class forKey:KeyNotExist];\n    XCTAssertEqualObjects(value, nil);\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVDemoTests/MMKVPerformanceTest.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2018 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <MMKV/MMKV.h>\n#import <XCTest/XCTest.h>\n\n@interface MMKVPerformanceTest : XCTestCase\n\n@end\n\n@implementation MMKVPerformanceTest {\n    MMKV *mmkv;\n}\n\n- (void)setUp {\n    [super setUp];\n\n    mmkv = [MMKV mmkvWithID:@\"performance_test\"];\n}\n\n- (void)tearDown {\n    mmkv = nil;\n\n    [super tearDown];\n}\n\n- (void)testWriteIntPerformance {\n    int loops = 10000;\n    NSMutableArray *arrIntKeys = [NSMutableArray arrayWithCapacity:loops];\n    for (size_t index = 0; index < loops; index++) {\n        NSString *intKey = [NSString stringWithFormat:@\"int-%zu\", index];\n        [arrIntKeys addObject:intKey];\n    }\n\n    [self measureBlock:^{\n        for (int index = 0; index < loops; index++) {\n            int32_t tmp = rand();\n            NSString *intKey = arrIntKeys[index];\n            [self->mmkv setInt32:tmp forKey:intKey];\n        }\n    }];\n}\n\n- (void)testReadIntPerformance {\n    int loops = 10000;\n    NSMutableArray *arrIntKeys = [NSMutableArray arrayWithCapacity:loops];\n    for (size_t index = 0; index < loops; index++) {\n        NSString *intKey = [NSString stringWithFormat:@\"int-%zu\", index];\n        [arrIntKeys addObject:intKey];\n    }\n\n    [self measureBlock:^{\n        for (int index = 0; index < loops; index++) {\n            NSString *intKey = arrIntKeys[index];\n            [self->mmkv getInt32ForKey:intKey];\n        }\n    }];\n}\n\n- (void)testWriteStringPerformance {\n    int loops = 10000;\n    NSMutableArray *arrStrings = [NSMutableArray arrayWithCapacity:loops];\n    NSMutableArray *arrStrKeys = [NSMutableArray arrayWithCapacity:loops];\n    for (size_t index = 0; index < loops; index++) {\n        NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n        [arrStrings addObject:str];\n\n        NSString *strKey = [NSString stringWithFormat:@\"str-%zu\", index];\n        [arrStrKeys addObject:strKey];\n    }\n\n    [self measureBlock:^{\n        for (int index = 0; index < loops; index++) {\n            NSString *str = arrStrings[index];\n            NSString *strKey = arrStrKeys[index];\n            [self->mmkv setObject:str forKey:strKey];\n        }\n    }];\n}\n\n- (void)testReadStringPerformance {\n    int loops = 10000;\n    NSMutableArray *arrStrings = [NSMutableArray arrayWithCapacity:loops];\n    NSMutableArray *arrStrKeys = [NSMutableArray arrayWithCapacity:loops];\n    for (size_t index = 0; index < loops; index++) {\n        NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n        [arrStrings addObject:str];\n\n        NSString *strKey = [NSString stringWithFormat:@\"str-%zu\", index];\n        [arrStrKeys addObject:strKey];\n    }\n\n    [self measureBlock:^{\n        for (int index = 0; index < loops; index++) {\n            NSString *strKey = arrStrKeys[index];\n            [self->mmkv getObjectOfClass:NSString.class forKey:strKey];\n        }\n    }];\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/AppDelegate.h",
    "content": "//\n//  AppDelegate.h\n//  MMKVMacDemo\n//\n//  Created by Ling Guo on 2018/9/27.\n//  Copyright © 2018 Lingol. All rights reserved.\n//\n\n#import <Cocoa/Cocoa.h>\n\n@interface AppDelegate : NSObject <NSApplicationDelegate>\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/AppDelegate.m",
    "content": "//\n//  AppDelegate.m\n//  MMKVMacDemo\n//\n//  Created by Ling Guo on 2018/9/27.\n//  Copyright © 2018 Lingol. All rights reserved.\n//\n\n#import \"AppDelegate.h\"\n#import <MMKV/MMKV.h>\n\n@interface AppDelegate ()\n\n@end\n\n@implementation AppDelegate\n\n- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {\n    // Insert code here to initialize your application\n}\n\n- (void)applicationWillTerminate:(NSNotification *)aNotification {\n    // Insert code here to tear down your application\n    [MMKV onAppTerminate];\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"16x16\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"16x16\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"32x32\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"32x32\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"128x128\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"128x128\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"256x256\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"256x256\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"512x512\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"512x512\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"32700.99.1234\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" initialViewController=\"B8D-0N-5wS\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"22689\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Application-->\n        <scene sceneID=\"JPo-4y-FX3\">\n            <objects>\n                <application id=\"hnw-xV-0zn\" sceneMemberID=\"viewController\">\n                    <menu key=\"mainMenu\" title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n                        <items>\n                            <menuItem title=\"MMKVMacDemo\" id=\"1Xt-HY-uBw\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"MMKVMacDemo\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                                    <items>\n                                        <menuItem title=\"About MMKVMacDemo\" id=\"5kV-Vb-QxS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontStandardAboutPanel:\" target=\"Ady-hI-5gd\" id=\"Exp-CZ-Vem\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                                        <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                                        <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                                        <menuItem title=\"Hide MMKVMacDemo\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                            <connections>\n                                                <action selector=\"hide:\" target=\"Ady-hI-5gd\" id=\"PnN-Uc-m68\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"hideOtherApplications:\" target=\"Ady-hI-5gd\" id=\"VT4-aY-XCT\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"unhideAllApplications:\" target=\"Ady-hI-5gd\" id=\"Dhg-Le-xox\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                                        <menuItem title=\"Quit MMKVMacDemo\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                            <connections>\n                                                <action selector=\"terminate:\" target=\"Ady-hI-5gd\" id=\"Te7-pn-YzF\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"File\" id=\"dMs-cI-mzQ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"File\" id=\"bib-Uj-vzu\">\n                                    <items>\n                                        <menuItem title=\"New\" keyEquivalent=\"n\" id=\"Was-JA-tGl\">\n                                            <connections>\n                                                <action selector=\"newDocument:\" target=\"Ady-hI-5gd\" id=\"4Si-XN-c54\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open…\" keyEquivalent=\"o\" id=\"IAo-SY-fd9\">\n                                            <connections>\n                                                <action selector=\"openDocument:\" target=\"Ady-hI-5gd\" id=\"bVn-NM-KNZ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open Recent\" id=\"tXI-mr-wws\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Open Recent\" systemMenu=\"recentDocuments\" id=\"oas-Oc-fiZ\">\n                                                <items>\n                                                    <menuItem title=\"Clear Menu\" id=\"vNY-rz-j42\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"clearRecentDocuments:\" target=\"Ady-hI-5gd\" id=\"Daa-9d-B3U\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"m54-Is-iLE\"/>\n                                        <menuItem title=\"Close\" keyEquivalent=\"w\" id=\"DVo-aG-piG\">\n                                            <connections>\n                                                <action selector=\"performClose:\" target=\"Ady-hI-5gd\" id=\"HmO-Ls-i7Q\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save…\" keyEquivalent=\"s\" id=\"pxx-59-PXV\">\n                                            <connections>\n                                                <action selector=\"saveDocument:\" target=\"Ady-hI-5gd\" id=\"teZ-XB-qJY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save As…\" keyEquivalent=\"S\" id=\"Bw7-FT-i3A\">\n                                            <connections>\n                                                <action selector=\"saveDocumentAs:\" target=\"Ady-hI-5gd\" id=\"mDf-zr-I0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Revert to Saved\" keyEquivalent=\"r\" id=\"KaW-ft-85H\">\n                                            <connections>\n                                                <action selector=\"revertDocumentToSaved:\" target=\"Ady-hI-5gd\" id=\"iJ3-Pv-kwq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"aJh-i4-bef\"/>\n                                        <menuItem title=\"Page Setup…\" keyEquivalent=\"P\" id=\"qIS-W8-SiK\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" shift=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"runPageLayout:\" target=\"Ady-hI-5gd\" id=\"Din-rz-gC5\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Print…\" keyEquivalent=\"p\" id=\"aTl-1u-JFS\">\n                                            <connections>\n                                                <action selector=\"print:\" target=\"Ady-hI-5gd\" id=\"qaZ-4w-aoO\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                                    <items>\n                                        <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                            <connections>\n                                                <action selector=\"undo:\" target=\"Ady-hI-5gd\" id=\"M6e-cu-g7V\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                            <connections>\n                                                <action selector=\"redo:\" target=\"Ady-hI-5gd\" id=\"oIA-Rs-6OD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                                        <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                            <connections>\n                                                <action selector=\"cut:\" target=\"Ady-hI-5gd\" id=\"YJe-68-I9s\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                            <connections>\n                                                <action selector=\"copy:\" target=\"Ady-hI-5gd\" id=\"G1f-GL-Joy\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                            <connections>\n                                                <action selector=\"paste:\" target=\"Ady-hI-5gd\" id=\"UvS-8e-Qdg\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"pasteAsPlainText:\" target=\"Ady-hI-5gd\" id=\"cEh-KX-wJQ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"delete:\" target=\"Ady-hI-5gd\" id=\"0Mk-Ml-PaM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                            <connections>\n                                                <action selector=\"selectAll:\" target=\"Ady-hI-5gd\" id=\"VNm-Mi-diN\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                                        <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                                <items>\n                                                    <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"cD7-Qs-BN4\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"WD3-Gg-5AJ\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"NDo-RZ-v9R\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"HOh-sY-3ay\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"U76-nv-p5D\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                                        <connections>\n                                                            <action selector=\"centerSelectionInVisibleArea:\" target=\"Ady-hI-5gd\" id=\"IOG-6D-g5B\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                                <items>\n                                                    <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                                        <connections>\n                                                            <action selector=\"showGuessPanel:\" target=\"Ady-hI-5gd\" id=\"vFj-Ks-hy3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                                        <connections>\n                                                            <action selector=\"checkSpelling:\" target=\"Ady-hI-5gd\" id=\"fz7-VC-reM\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                                    <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleContinuousSpellChecking:\" target=\"Ady-hI-5gd\" id=\"7w6-Qz-0kB\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleGrammarChecking:\" target=\"Ady-hI-5gd\" id=\"muD-Qn-j4w\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"Ady-hI-5gd\" id=\"2lM-Qi-WAP\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                                <items>\n                                                    <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"orderFrontSubstitutionsPanel:\" target=\"Ady-hI-5gd\" id=\"oku-mr-iSq\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                                    <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleSmartInsertDelete:\" target=\"Ady-hI-5gd\" id=\"3IJ-Se-DZD\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"Ady-hI-5gd\" id=\"ptq-xd-QOA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDashSubstitution:\" target=\"Ady-hI-5gd\" id=\"oCt-pO-9gS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticLinkDetection:\" target=\"Ady-hI-5gd\" id=\"Gip-E3-Fov\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDataDetection:\" target=\"Ady-hI-5gd\" id=\"R1I-Nq-Kbl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticTextReplacement:\" target=\"Ady-hI-5gd\" id=\"DvP-Fe-Py6\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                                <items>\n                                                    <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"uppercaseWord:\" target=\"Ady-hI-5gd\" id=\"sPh-Tk-edu\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"lowercaseWord:\" target=\"Ady-hI-5gd\" id=\"iUZ-b5-hil\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"capitalizeWord:\" target=\"Ady-hI-5gd\" id=\"26H-TL-nsh\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                                <items>\n                                                    <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"startSpeaking:\" target=\"Ady-hI-5gd\" id=\"654-Ng-kyl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"stopSpeaking:\" target=\"Ady-hI-5gd\" id=\"dX8-6p-jy9\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Format\" id=\"jxT-CU-nIS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Format\" id=\"GEO-Iw-cKr\">\n                                    <items>\n                                        <menuItem title=\"Font\" id=\"Gi5-1S-RQB\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Font\" systemMenu=\"font\" id=\"aXa-aM-Jaq\">\n                                                <items>\n                                                    <menuItem title=\"Show Fonts\" keyEquivalent=\"t\" id=\"Q5e-8K-NDq\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontFontPanel:\" target=\"YLy-65-1bz\" id=\"WHr-nq-2xA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Bold\" tag=\"2\" keyEquivalent=\"b\" id=\"GB9-OM-e27\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"hqk-hr-sYV\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Italic\" tag=\"1\" keyEquivalent=\"i\" id=\"Vjx-xi-njq\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"IHV-OB-c03\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Underline\" keyEquivalent=\"u\" id=\"WRG-CD-K1S\">\n                                                        <connections>\n                                                            <action selector=\"underline:\" target=\"Ady-hI-5gd\" id=\"FYS-2b-JAY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"5gT-KC-WSO\"/>\n                                                    <menuItem title=\"Bigger\" tag=\"3\" keyEquivalent=\"+\" id=\"Ptp-SP-VEL\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"Uc7-di-UnL\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smaller\" tag=\"4\" keyEquivalent=\"-\" id=\"i1d-Er-qST\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"HcX-Lf-eNd\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"kx3-Dk-x3B\"/>\n                                                    <menuItem title=\"Kern\" id=\"jBQ-r6-VK2\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Kern\" id=\"tlD-Oa-oAM\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"GUa-eO-cwY\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardKerning:\" target=\"Ady-hI-5gd\" id=\"6dk-9l-Ckg\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"cDB-IK-hbR\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffKerning:\" target=\"Ady-hI-5gd\" id=\"U8a-gz-Maa\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Tighten\" id=\"46P-cB-AYj\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"tightenKerning:\" target=\"Ady-hI-5gd\" id=\"hr7-Nz-8ro\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Loosen\" id=\"ogc-rX-tC1\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"loosenKerning:\" target=\"Ady-hI-5gd\" id=\"8i4-f9-FKE\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Ligatures\" id=\"o6e-r0-MWq\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Ligatures\" id=\"w0m-vy-SC9\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"agt-UL-0e3\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardLigatures:\" target=\"Ady-hI-5gd\" id=\"7uR-wd-Dx6\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"J7y-lM-qPV\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffLigatures:\" target=\"Ady-hI-5gd\" id=\"iX2-gA-Ilz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use All\" id=\"xQD-1f-W4t\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useAllLigatures:\" target=\"Ady-hI-5gd\" id=\"KcB-kA-TuK\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Baseline\" id=\"OaQ-X3-Vso\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Baseline\" id=\"ijk-EB-dga\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"3Om-Ey-2VK\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"unscript:\" target=\"Ady-hI-5gd\" id=\"0vZ-95-Ywn\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Superscript\" id=\"Rqc-34-cIF\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"superscript:\" target=\"Ady-hI-5gd\" id=\"3qV-fo-wpU\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Subscript\" id=\"I0S-gh-46l\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"subscript:\" target=\"Ady-hI-5gd\" id=\"Q6W-4W-IGz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Raise\" id=\"2h7-ER-AoG\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"raiseBaseline:\" target=\"Ady-hI-5gd\" id=\"4sk-31-7Q9\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Lower\" id=\"1tx-W0-xDw\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"lowerBaseline:\" target=\"Ady-hI-5gd\" id=\"OF1-bc-KW4\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"Ndw-q3-faq\"/>\n                                                    <menuItem title=\"Show Colors\" keyEquivalent=\"C\" id=\"bgn-CT-cEk\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontColorPanel:\" target=\"Ady-hI-5gd\" id=\"mSX-Xz-DV3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"iMs-zA-UFJ\"/>\n                                                    <menuItem title=\"Copy Style\" keyEquivalent=\"c\" id=\"5Vv-lz-BsD\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyFont:\" target=\"Ady-hI-5gd\" id=\"GJO-xA-L4q\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Style\" keyEquivalent=\"v\" id=\"vKC-jM-MkH\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteFont:\" target=\"Ady-hI-5gd\" id=\"JfD-CL-leO\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Text\" id=\"Fal-I4-PZk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Text\" id=\"d9c-me-L2H\">\n                                                <items>\n                                                    <menuItem title=\"Align Left\" keyEquivalent=\"{\" id=\"ZM1-6Q-yy1\">\n                                                        <connections>\n                                                            <action selector=\"alignLeft:\" target=\"Ady-hI-5gd\" id=\"zUv-R1-uAa\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Center\" keyEquivalent=\"|\" id=\"VIY-Ag-zcb\">\n                                                        <connections>\n                                                            <action selector=\"alignCenter:\" target=\"Ady-hI-5gd\" id=\"spX-mk-kcS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Justify\" id=\"J5U-5w-g23\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"alignJustified:\" target=\"Ady-hI-5gd\" id=\"ljL-7U-jND\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Align Right\" keyEquivalent=\"}\" id=\"wb2-vD-lq4\">\n                                                        <connections>\n                                                            <action selector=\"alignRight:\" target=\"Ady-hI-5gd\" id=\"r48-bG-YeY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"4s2-GY-VfK\"/>\n                                                    <menuItem title=\"Writing Direction\" id=\"H1b-Si-o9J\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Writing Direction\" id=\"8mr-sm-Yjd\">\n                                                            <items>\n                                                                <menuItem title=\"Paragraph\" enabled=\"NO\" id=\"ZvO-Gk-QUH\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"YGs-j5-SAR\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"qtV-5e-UBP\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"Lbh-J2-qVU\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"S0X-9S-QSf\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"jFq-tB-4Kx\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"5fk-qB-AqJ\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem isSeparatorItem=\"YES\" id=\"swp-gr-a21\"/>\n                                                                <menuItem title=\"Selection\" enabled=\"NO\" id=\"cqv-fj-IhA\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"Nop-cj-93Q\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"lPI-Se-ZHp\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"BgM-ve-c93\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"caW-Bv-w94\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"RB4-Sm-HuC\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"EXD-6r-ZUu\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"fKy-g9-1gm\"/>\n                                                    <menuItem title=\"Show Ruler\" id=\"vLm-3I-IUL\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleRuler:\" target=\"Ady-hI-5gd\" id=\"FOx-HJ-KwY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Copy Ruler\" keyEquivalent=\"c\" id=\"MkV-Pr-PK5\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyRuler:\" target=\"Ady-hI-5gd\" id=\"71i-fW-3W2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Ruler\" keyEquivalent=\"v\" id=\"LVM-kO-fVI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteRuler:\" target=\"Ady-hI-5gd\" id=\"cSh-wd-qM2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                                    <items>\n                                        <menuItem title=\"Show Toolbar\" keyEquivalent=\"t\" id=\"snW-S8-Cw5\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleToolbarShown:\" target=\"Ady-hI-5gd\" id=\"BXY-wc-z0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Customize Toolbar…\" id=\"1UK-8n-QPP\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"runToolbarCustomizationPalette:\" target=\"Ady-hI-5gd\" id=\"pQI-g3-MTW\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"hB3-LF-h0Y\"/>\n                                        <menuItem title=\"Show Sidebar\" keyEquivalent=\"s\" id=\"kIP-vf-haE\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleSidebar:\" target=\"Ady-hI-5gd\" id=\"iwa-gc-5KM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleFullScreen:\" target=\"Ady-hI-5gd\" id=\"dU3-MA-1Rq\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                                    <items>\n                                        <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                            <connections>\n                                                <action selector=\"performMiniaturize:\" target=\"Ady-hI-5gd\" id=\"VwT-WD-YPe\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"performZoom:\" target=\"Ady-hI-5gd\" id=\"DIl-cC-cCs\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                                        <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"arrangeInFront:\" target=\"Ady-hI-5gd\" id=\"DRN-fu-gQh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Help\" id=\"wpr-3q-Mcd\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"F2S-fz-NVQ\">\n                                    <items>\n                                        <menuItem title=\"MMKVMacDemo Help\" keyEquivalent=\"?\" id=\"FKE-Sm-Kum\">\n                                            <connections>\n                                                <action selector=\"showHelp:\" target=\"Ady-hI-5gd\" id=\"y7X-2Q-9no\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                    <connections>\n                        <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"PrD-fu-P6m\"/>\n                    </connections>\n                </application>\n                <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\"/>\n                <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n                <customObject id=\"Ady-hI-5gd\" userLabel=\"First Responder\" customClass=\"NSResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"75\" y=\"0.0\"/>\n        </scene>\n        <!--Window Controller-->\n        <scene sceneID=\"R2V-B0-nI4\">\n            <objects>\n                <windowController id=\"B8D-0N-5wS\" sceneMemberID=\"viewController\">\n                    <window key=\"window\" title=\"Window\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" visibleAtLaunch=\"NO\" animationBehavior=\"default\" id=\"IQv-IB-iLA\">\n                        <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n                        <windowPositionMask key=\"initialPositionMask\" leftStrut=\"YES\" rightStrut=\"YES\" topStrut=\"YES\" bottomStrut=\"YES\"/>\n                        <rect key=\"contentRect\" x=\"196\" y=\"240\" width=\"480\" height=\"270\"/>\n                        <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"1680\" height=\"1027\"/>\n                        <connections>\n                            <outlet property=\"delegate\" destination=\"B8D-0N-5wS\" id=\"98r-iN-zZc\"/>\n                        </connections>\n                    </window>\n                    <connections>\n                        <segue destination=\"XfG-lQ-9wD\" kind=\"relationship\" relationship=\"window.shadowedContentViewController\" id=\"cq2-FE-JQM\"/>\n                    </connections>\n                </windowController>\n                <customObject id=\"Oky-zY-oP4\" userLabel=\"First Responder\" customClass=\"NSResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"75\" y=\"250\"/>\n        </scene>\n        <!--View Controller-->\n        <scene sceneID=\"hIz-AP-VOD\">\n            <objects>\n                <viewController id=\"XfG-lQ-9wD\" customClass=\"ViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" wantsLayer=\"YES\" id=\"m2S-Jp-Qdl\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"270\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </view>\n                </viewController>\n                <customObject id=\"rPt-NT-nkU\" userLabel=\"First Responder\" customClass=\"NSResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"75\" y=\"655\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Copyright © 2018 Lingol. All rights reserved.</string>\n\t<key>NSMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/MMKVMacDemo.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>com.apple.security.app-sandbox</key>\n    <true/>\n    <key>com.apple.security.files.user-selected.read-only</key>\n    <true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/ViewController.h",
    "content": "//\n//  ViewController.h\n//  MMKVMacDemo\n//\n//  Created by Ling Guo on 2018/9/27.\n//  Copyright © 2018 Lingol. All rights reserved.\n//\n\n#import <Cocoa/Cocoa.h>\n\n@interface ViewController : NSViewController\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/ViewController.mm",
    "content": "//\n//  ViewController.m\n//  MMKVMacDemo\n//\n//  Created by Ling Guo on 2018/9/27.\n//  Copyright © 2018 Lingol. All rights reserved.\n//\n\n#import \"ViewController.h\"\n#import <MMKV/MMKV.h>\n\n@implementation ViewController {\n    NSMutableArray *m_arrStrings;\n    NSMutableArray *m_arrStrKeys;\n    NSMutableArray *m_arrIntKeys;\n\n    int m_loops;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n\n    [self testCompareBeforeSet];\n    \n    [self onlyOneKeyTest];\n    [self overrideTest];\n    [self expectedCapacityTest];\n    [self testClearAllWithKeepingSpace];\n\n    [self funcionalTest:NO];\n    [self testNeedLoadFromFile];\n\n    m_loops = 10000;\n    m_arrStrings = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrStrKeys = [NSMutableArray arrayWithCapacity:m_loops];\n    m_arrIntKeys = [NSMutableArray arrayWithCapacity:m_loops];\n    for (size_t index = 0; index < m_loops; index++) {\n        NSString *str = [NSString stringWithFormat:@\"%s-%d\", __FILE__, rand()];\n        [m_arrStrings addObject:str];\n\n        NSString *strKey = [NSString stringWithFormat:@\"str-%zu\", index];\n        [m_arrStrKeys addObject:strKey];\n\n        NSString *intKey = [NSString stringWithFormat:@\"int-%zu\", index];\n        [m_arrIntKeys addObject:intKey];\n    }\n}\n\n- (void) testClearAllWithKeepingSpace {\n    {\n        auto mmkv = [MMKV mmkvWithID:@\"testClearAllWithKeepingSpace\"];\n        [mmkv setFloat:123.456f forKey:@\"key1\"];\n        for (int i = 0; i < 10000; i++) {\n            [mmkv setFloat:123.456f forKey:[NSString stringWithFormat:@\"key_%d\", i]];\n        }\n        auto previousSize =[mmkv totalSize];\n        assert(previousSize > PAGE_SIZE);\n        [mmkv clearAllWithKeepingSpace];\n        assert([mmkv totalSize] == previousSize);\n        assert([mmkv count] == 0);\n        [mmkv setFloat:123.4567f forKey:@\"key2\"];\n        [mmkv setFloat:223.47f forKey:@\"key3\"];\n        assert([mmkv count] == 2);\n    }\n    \n    {\n        NSString *crypt = [NSString stringWithFormat:@\"Crypt123\"];\n        auto mmkv = [MMKV mmkvWithID:@\"testClearAllWithKeepingSpaceCrypt\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n        [mmkv setFloat:123.456f forKey:@\"key1\"];\n        for (int i = 0; i < 10000; i++) {\n            [mmkv setFloat:123.456f forKey:[NSString stringWithFormat:@\"key_%d\", i]];\n        }\n        auto previousSize =[mmkv totalSize];\n        assert(previousSize > PAGE_SIZE);\n        [mmkv clearAllWithKeepingSpace];\n        assert([mmkv totalSize] == previousSize);\n        assert([mmkv count] == 0);\n        [mmkv setFloat:123.4567f forKey:@\"key2\"];\n        [mmkv setFloat:223.47f forKey:@\"key3\"];\n        assert([mmkv count] == 2);\n    }\n}\n\n- (void) onlyOneKeyTest {\n    {\n        auto mmkv0 = [MMKV mmkvWithID:@\"onlyOneKeyTest\"];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *value = [NSString stringWithFormat:@\"world\"];\n        auto v = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v);\n        \n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n        \n        for (int i = 0; i < 10; i++) {\n            NSString * value2 = [NSString stringWithFormat:@\"world_%d\", i];\n            [mmkv0 setString:value2 forKey:key];\n            auto v2 = [mmkv0 getStringForKey:key];\n            NSLog(@\"value = %@\", v2);\n        }\n        \n        int len = 10000;\n        NSMutableString *bigValue = [NSMutableString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n        for (int i = 0; i < len; i++) {\n            [bigValue appendString:@\"0\"];\n        }\n        [mmkv0 setString:bigValue forKey:key];\n        auto v3 = [mmkv0 getStringForKey:key];\n        // NSLog(@\"value = %@\", v3);\n        if (![bigValue isEqualToString:v3]) {\n            abort();\n        }\n\n        [mmkv0 setString:@\"OK\" forKey:key];\n        auto v4 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v4);\n        \n        [mmkv0 setInt32:12345 forKey:@\"int\"];\n        auto v5 = [mmkv0 getInt32ForKey:key];\n        NSLog(@\"int value = %d\", v5);\n        [mmkv0 removeValueForKey:@\"int\"];\n    }\n    \n    {\n        NSString *crypt = [NSString stringWithFormat:@\"fastest\"];\n        auto mmkv0 = [MMKV mmkvWithID:@\"onlyOneKeyCryptTest\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *value = [NSString stringWithFormat:@\"cryptworld\"];\n        auto v = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v);\n        \n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n        \n        for (int i = 0; i < 10; i++) {\n            NSString * value2 = [NSString stringWithFormat:@\"cryptworld_%d\", i];\n            [mmkv0 setString:value2 forKey:key];\n            auto v2 = [mmkv0 getStringForKey:key];\n            NSLog(@\"value = %@\", v2);\n        }\n    }\n}\n\n- (void) overrideTest {\n    {\n        auto mmkv0 = [MMKV mmkvWithID:@\"overrideTest\"];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *key2 = [NSString stringWithFormat:@\"hello2\"];\n        NSString *value = [NSString stringWithFormat:@\"world\"];\n        \n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n        [mmkv0 removeValueForKey:key];\n        \n        [mmkv0 setString:value forKey:key2];\n        v2 = [mmkv0 getStringForKey:key2];\n        NSLog(@\"value = %@\", v2);\n        [mmkv0 removeValueForKey:key2];\n        \n        int len = 10000;\n        NSMutableString *bigValue = [NSMutableString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n        for (int i = 0; i < len; i++) {\n            [bigValue appendString:@\"0\"];\n        }\n        [mmkv0 setString:bigValue forKey:key];\n        auto v3 = [mmkv0 getStringForKey:key];\n        // NSLog(@\"value = %@\", v3);\n        if (![bigValue isEqualToString:v3]) {\n            abort();\n        }\n\n        // rewrite\n        [mmkv0 setString:@\"OK\" forKey:key];\n        auto v4 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v4);\n        \n        [mmkv0 setInt32:12345 forKey:@\"int\"];\n        auto v5 = [mmkv0 getInt32ForKey:key];\n        NSLog(@\"int value = %d\", v5);\n        [mmkv0 removeValueForKey:@\"int\"];\n        \n        [mmkv0 clearAll];\n    \n    }\n    \n    {\n        NSString *crypt = [NSString stringWithFormat:@\"fastestCrypt\"];\n        auto mmkv0 = [MMKV mmkvWithID:@\"overrideCryptTest\" cryptKey:[crypt dataUsingEncoding:NSUTF8StringEncoding] mode:MMKVSingleProcess];\n//        [mmkv0 enableCompareBeforeSet];\n        NSString *key = [NSString stringWithFormat:@\"hello\"];\n        NSString *key2 = [NSString stringWithFormat:@\"hello2\"];\n        NSString *value = [NSString stringWithFormat:@\"cryptworld\"];\n        \n        [mmkv0 setString:value forKey:key];\n        auto v2 = [mmkv0 getStringForKey:key];\n        NSLog(@\"value = %@\", v2);\n        \n        [mmkv0 removeValueForKey:key];\n        [mmkv0 setString:value forKey:key2];\n        v2 = [mmkv0 getStringForKey:key2];\n        NSLog(@\"value = %@\", v2);\n        [mmkv0 removeValueForKey:key2];\n        \n        [mmkv0 clearAll];\n    }\n}\n\n- (void)expectedCapacityTest {\n    int len = 10000;\n    NSString *value = [NSString stringWithFormat:@\"🏊🏻®4️⃣🐅_\"];\n    for (int i = 0; i < len; i++) {\n        value = [value stringByAppendingString:@\"0\"];\n    }\n    NSLog(@\"value size = %ld\", [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);\n    NSString *key = [NSString stringWithFormat:@\"key0\"];\n    \n    // if we know exactly the sizes of key and value, set expectedCapacity for performance improvement\n    size_t expectedSize = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding]\n                        + [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];\n    auto mmkv0 = [MMKV mmkvWithID:@\"expectedCapacityTest0\" expectedCapacity:expectedSize];\n    // 0 times expand\n    [mmkv0 setString:value forKey:key];\n    \n    \n    int count = 10;\n    expectedSize *= count;\n    auto mmkv1 = [MMKV mmkvWithID:@\"expectedCapacityTest1\" expectedCapacity:expectedSize];\n    for (int i = 0; i < count; i++) {\n        // 0 times expand\n        [mmkv1 setString:value forKey:[NSString stringWithFormat:@\"key%d\", i]];\n    }\n}\n\n- (void) testCompareBeforeSet {\n    auto mmkv = [MMKV mmkvWithID:@\"testCompareBeforeSet\"];\n    [mmkv enableCompareBeforeSet];\n    [mmkv setBool:true forKey:@\"extra\"];\n    \n    {\n        NSString *key = @\"int64\";\n        int64_t v = 123456L;\n        [mmkv setInt64:v forKey:key];\n        long actualSize = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize);\n        NSLog(@\"testCompareBeforeSet v = %lld\", [mmkv getInt64ForKey:key]);\n        [mmkv setInt64:v forKey:key];\n        long actualSize2 = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize2);\n        if (actualSize != actualSize2) {\n            abort();\n        }\n        [mmkv setInt64:v << 1 forKey:key];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", [mmkv actualSize]);\n        NSLog(@\"testCompareBeforeSet v = %lld\", [mmkv getInt64ForKey:key]);\n    }\n    \n    {\n        NSString *key = @\"string\";\n        NSString *v = [NSString stringWithFormat:@\"w012A🏊🏻good\"];\n        [mmkv setString:v forKey:key];\n        long actualSize = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize);\n        NSLog(@\"testCompareBeforeSet v = %@\", [mmkv getStringForKey:key]);\n        [mmkv setString:v forKey:key];\n        long actualSize2 = [mmkv actualSize];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", actualSize2);\n        if (actualSize != actualSize2) {\n            abort();\n        }\n        [mmkv setString:@\"another string\" forKey:key];\n        NSLog(@\"testCompareBeforeSet actualSize = %ld\", [mmkv actualSize]);\n        NSLog(@\"testCompareBeforeSet v = %@\", [mmkv getStringForKey:key]);\n    }\n}\n\n- (void)funcionalTest:(BOOL)decodeOnly {\n    MMKV *mmkv = [MMKV defaultMMKV];\n\n    if (!decodeOnly) {\n        [mmkv setBool:YES forKey:@\"bool\"];\n    }\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    if (!decodeOnly) {\n        [mmkv setInt32:-1024 forKey:@\"int32\"];\n    }\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    }\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    if (!decodeOnly) {\n        [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    }\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    }\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    if (!decodeOnly) {\n        [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    }\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    if (!decodeOnly) {\n        [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    }\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    if (!decodeOnly) {\n        [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    }\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n\n    if (!decodeOnly) {\n        [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    }\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n\n    if (!decodeOnly) {\n        [mmkv setObject:[@\"hello, mmkv again and again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    }\n    NSData *data = [mmkv getObjectOfClass:NSData.class forKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n\n    if (!decodeOnly) {\n        [mmkv removeValueForKey:@\"bool\"];\n        NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    }\n}\n\n- (void)setRepresentedObject:(id)representedObject {\n    [super setRepresentedObject:representedObject];\n\n    // Update the view, if already loaded.\n}\n\n- (void)testNeedLoadFromFile {\n    auto mmkv = [MMKV mmkvWithID:@\"testNeedLoadFromFile\"];\n    [mmkv clearMemoryCache]; // or may be triggered by Memory Warning\n    [mmkv clearAll];\n    NSAssert([mmkv setString:@\"value\" forKey:@\"key\"], @\"Fail to save\");\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVMacDemo/main.m",
    "content": "//\n//  main.m\n//  MMKVMacDemo\n//\n//  Created by Ling Guo on 2018/9/27.\n//  Copyright © 2018 Lingol. All rights reserved.\n//\n\n#import <Cocoa/Cocoa.h>\n#import <MMKV/MMKV.h>\n\nint main(int argc, const char *argv[]) {\n    [MMKV initializeMMKV:nil];\n    return NSApplicationMain(argc, argv);\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVTodayExtensionDemo/Base.lproj/MainInterface.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21507\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"M4Y-Lb-cyx\">\n    <device id=\"retina6_12\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"21505\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Today View Controller-->\n        <scene sceneID=\"cwh-vc-ff4\">\n            <objects>\n                <viewController id=\"M4Y-Lb-cyx\" customClass=\"TodayViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" simulatedAppContext=\"notificationCenter\" id=\"S3S-Oj-5AN\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"37\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"top\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Hello World\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" preferredMaxLayoutWidth=\"280\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"GcN-lo-r42\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"59.000000000000007\" width=\"320\" height=\"20.333333333333336\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"ssy-KU-ocm\"/>\n                        <constraints>\n                            <constraint firstItem=\"ssy-KU-ocm\" firstAttribute=\"bottom\" secondItem=\"GcN-lo-r42\" secondAttribute=\"bottom\" symbolic=\"YES\" id=\"0Q0-KW-PJ6\"/>\n                            <constraint firstItem=\"GcN-lo-r42\" firstAttribute=\"leading\" secondItem=\"ssy-KU-ocm\" secondAttribute=\"leading\" symbolic=\"YES\" id=\"6Vq-gs-PHe\"/>\n                            <constraint firstItem=\"ssy-KU-ocm\" firstAttribute=\"trailing\" secondItem=\"GcN-lo-r42\" secondAttribute=\"trailing\" symbolic=\"YES\" id=\"L8K-9R-egU\"/>\n                            <constraint firstItem=\"GcN-lo-r42\" firstAttribute=\"top\" secondItem=\"ssy-KU-ocm\" secondAttribute=\"top\" symbolic=\"YES\" id=\"mYS-Cv-VNx\"/>\n                        </constraints>\n                    </view>\n                    <extendedEdge key=\"edgesForExtendedLayout\"/>\n                    <freeformSimulatedSizeMetrics key=\"simulatedDestinationMetrics\"/>\n                    <size key=\"freeformSize\" width=\"320\" height=\"37\"/>\n                    <connections>\n                        <outlet property=\"m_label\" destination=\"GcN-lo-r42\" id=\"AZ1-tL-pwV\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"vXp-U4-Rya\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"138.93129770992365\" y=\"-2.4647887323943665\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVTodayExtensionDemo/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>MMKVTodayExtensionDemo</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>NSExtension</key>\n\t<dict>\n\t\t<key>NSExtensionMainStoryboard</key>\n\t\t<string>MainInterface</string>\n\t\t<key>NSExtensionPointIdentifier</key>\n\t\t<string>com.apple.widget-extension</string>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVTodayExtensionDemo/MMKVTodayExtensionDemo.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.tencent.mmkv</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVTodayExtensionDemo/TodayViewController.h",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2019 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#import <UIKit/UIKit.h>\n\n@interface TodayViewController : UIViewController\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVTodayExtensionDemo/TodayViewController.m",
    "content": "/*\n* Tencent is pleased to support the open source community by making\n* MMKV available.\n*\n* Copyright (C) 2019 THL A29 Limited, a Tencent company.\n* All rights reserved.\n*\n* Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n* this file except in compliance with the License. You may obtain a copy of\n* the License at\n*\n*       https://opensource.org/licenses/BSD-3-Clause\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n#import \"TodayViewController.h\"\n#import <MMKVAppExtension/MMKV.h>\n#import <NotificationCenter/NotificationCenter.h>\n\n@interface TodayViewController () <NCWidgetProviding>\n@property (nonatomic) IBOutlet UILabel *m_label;\n@end\n\n@implementation TodayViewController {\n    NSData *m_encryptionKey;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    // Do any additional setup after loading the view.\n\n    NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@\"group.tencent.mmkv\"].path;\n    [MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogInfo];\n    m_encryptionKey = [@\"multi_process\" dataUsingEncoding:NSUTF8StringEncoding];\n\n    // [self testMultiProcess];\n}\n\n- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {\n    // Perform any setup necessary in order to update the view.\n\n    // If an error is encountered, use NCUpdateResultFailed\n    // If there's no update required, use NCUpdateResultNoData\n    // If there's an update, use NCUpdateResultNewData\n    MMKV *mmkv = [MMKV mmkvWithID:@\"multi_process\" cryptKey:m_encryptionKey mode:MMKVMultiProcess];\n    NSString *newContent = [mmkv getStringForKey:@\"content\"];\n    _m_label.text = newContent;\n\n    completionHandler(NCUpdateResultNewData);\n}\n\n- (void)testMultiProcess {\n    MMKV *mmkv = [MMKV mmkvWithID:@\"multi_process\" cryptKey:m_encryptionKey mode:MMKVMultiProcess];\n\n    [mmkv setBool:YES forKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    [mmkv setInt32:-1024 forKey:@\"int32\"];\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    [mmkv setUInt32:UINT32_MAX forKey:@\"uint32\"];\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    [mmkv setInt64:INT64_MIN forKey:@\"int64\"];\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    [mmkv setUInt64:UINT64_MAX forKey:@\"uint64\"];\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    [mmkv setDouble:DBL_MAX forKey:@\"double\"];\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n\n    [mmkv setObject:nil forKey:@\"string\"];\n    NSLog(@\"string after set nil:%@, containsKey:%d\",\n          [mmkv getObjectOfClass:NSString.class\n                          forKey:@\"string\"],\n          [mmkv containsKey:@\"string\"]);\n\n    [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n\n    [mmkv setData:[@\"hello, mmkv again and again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    NSData *data = [mmkv getDataForKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n    NSLog(@\"data length:%zu, value size consumption:%zu\", data.length, [mmkv getValueSizeForKey:@\"data\" actualSize:NO]);\n\n    [mmkv removeValueForKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    [mmkv removeValuesForKeys:@[ @\"int32\", @\"uint64\" ]];\n    NSLog(@\"allKeys %@\", [mmkv allKeys]);\n\n    [mmkv close];\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"vision\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"layers\" : [\n    {\n      \"filename\" : \"Front.solidimagestacklayer\"\n    },\n    {\n      \"filename\" : \"Middle.solidimagestacklayer\"\n    },\n    {\n      \"filename\" : \"Back.solidimagestacklayer\"\n    }\n  ]\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"vision\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"vision\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/ContentView.swift",
    "content": "//\n//  ContentView.swift\n//  MMKVVisionDemo\n//\n//  Created by Cyril Bonaccini on 15/02/2024.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\nimport SwiftUI\nimport MMKV\n\nstruct ContentView: View {\n    var body: some View {\n        VStack {\n            Text(\"Hello, world!\")\n            Button(\"Press Me\") {\n                guard let mmkv = MMKV(mmapID: \"testSwift\", mode: MMKVMode.singleProcess) else {\n                    return\n                }\n\n                mmkv.set(true, forKey: \"bool\")\n                print(\"Swift: bool = \\(mmkv.bool(forKey: \"bool\"))\")\n\n                mmkv.set(Int32(-1024), forKey: \"int32\")\n                print(\"Swift: int32 = \\(mmkv.int32(forKey: \"int32\"))\")\n\n                mmkv.set(UInt32.max, forKey: \"uint32\")\n                print(\"Swift: uint32 = \\(mmkv.uint32(forKey: \"uint32\"))\")\n\n                mmkv.set(Int64.min, forKey: \"int64\")\n                print(\"Swift: int64 = \\(mmkv.int64(forKey: \"int64\"))\")\n\n                mmkv.set(UInt64.max, forKey: \"uint64\")\n                print(\"Swift: uint64 = \\(mmkv.uint64(forKey: \"uint64\"))\")\n\n                mmkv.set(Float(-3.1415926), forKey: \"float\")\n                print(\"Swift: float = \\(mmkv.float(forKey: \"float\"))\")\n\n                mmkv.set(Double.infinity, forKey: \"double\")\n                print(\"Swift: double = \\(mmkv.double(forKey: \"double\"))\")\n                \n                mmkv.set(\"Hello from Swift\", forKey: \"string\")\n                print(\"Swift: string = \\(mmkv.string(forKey: \"string\") ?? \"\")\")\n                \n                mmkv.set(NSDate(), forKey: \"date\")\n                let date = mmkv.date(forKey: \"date\")\n                print(\"Swift: date = \\(date?.description(with: .current) ?? \"null\")\")\n                \n                mmkv.set(\"Hello from Swift\".data(using: .utf8) ?? Data(), forKey: \"data\")\n                let data = mmkv.data(forKey: \"data\")\n                let str = String(data: data ?? Data(), encoding: .utf8) ?? \"\"\n                print(\"Swift: data = \\(str)\")\n\n                let arr = [1, 0, 2, 4]\n                if let objArr = arr as NSArray? {\n                    mmkv.set(objArr, forKey:\"array\")\n                    let result = mmkv.object(of: NSArray.self, forKey: \"array\");\n                    print(\"Swift: array = \\(result as! NSArray)\")\n                }\n\n                mmkv.removeValue(forKey: \"bool\")\n                print(\"Swift: after delete bool = \\(mmkv.bool(forKey: \"bool\"))\")\n            }\n        }\n        .padding()\n    }\n}\n\n#Preview(windowStyle: .automatic) {\n    ContentView()\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationPreferredDefaultSceneSessionRole</key>\n\t\t<string>UIWindowSceneSessionRoleApplication</string>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<true/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict/>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/MMKVVisionDemoApp.swift",
    "content": "//\n//  MMKVVisionDemoApp.swift\n//  MMKVVisionDemo\n//\n//  Created by Cyril Bonaccini on 15/02/2024.\n//  Copyright © 2024 Lingol. All rights reserved.\n//\n\nimport SwiftUI\nimport MMKV\n\n@main\nstruct MMKVVisionDemoApp: App {\n    init() {\n        MMKV.initialize(rootDir: nil)\n    }\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n        }\n    }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/MMKVVisionDemo/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"notificationCenter\",\n      \"scale\" : \"2x\",\n      \"size\" : \"24x24\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"notificationCenter\",\n      \"scale\" : \"2x\",\n      \"size\" : \"27.5x27.5\",\n      \"subtype\" : \"42mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"companionSettings\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"companionSettings\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"appLauncher\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"appLauncher\",\n      \"scale\" : \"2x\",\n      \"size\" : \"44x44\",\n      \"subtype\" : \"40mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"appLauncher\",\n      \"scale\" : \"2x\",\n      \"size\" : \"50x50\",\n      \"subtype\" : \"44mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"quickLook\",\n      \"scale\" : \"2x\",\n      \"size\" : \"86x86\",\n      \"subtype\" : \"38mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"quickLook\",\n      \"scale\" : \"2x\",\n      \"size\" : \"98x98\",\n      \"subtype\" : \"42mm\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"role\" : \"quickLook\",\n      \"scale\" : \"2x\",\n      \"size\" : \"108x108\",\n      \"subtype\" : \"44mm\"\n    },\n    {\n      \"idiom\" : \"watch-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp/Base.lproj/Interface.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder.WatchKit.Storyboard\" version=\"3.0\" toolsVersion=\"14256\" targetRuntime=\"watchKit\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"AgC-eL-Hgc\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14243\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBWatchKitPlugin\" version=\"14219\"/>\n    </dependencies>\n    <scenes>\n        <!--Interface Controller-->\n        <scene sceneID=\"aou-V4-d1y\">\n            <objects>\n                <controller id=\"AgC-eL-Hgc\" customClass=\"InterfaceController\" customModuleProvider=\"\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"220\" y=\"345\"/>\n        </scene>\n        <!--Static Notification Interface Controller-->\n        <scene sceneID=\"AEw-b0-oYE\">\n            <objects>\n                <notificationController id=\"YCC-NB-fut\">\n                    <items>\n                        <label alignment=\"left\" text=\"Alert Label\" numberOfLines=\"0\" id=\"IdU-wH-bcW\"/>\n                    </items>\n                    <notificationCategory key=\"notificationCategory\" identifier=\"myCategory\" id=\"JfB-70-Muf\"/>\n                    <connections>\n                        <outlet property=\"notificationAlertLabel\" destination=\"IdU-wH-bcW\" id=\"JKC-fr-R95\"/>\n                        <segue destination=\"4sK-HA-Art\" kind=\"relationship\" relationship=\"dynamicNotificationInterface\" id=\"kXh-Jw-8B1\"/>\n                        <segue destination=\"eXb-UN-Cd0\" kind=\"relationship\" relationship=\"dynamicInteractiveNotificationInterface\" id=\"mpB-YA-K8N\"/>\n                    </connections>\n                </notificationController>\n            </objects>\n            <point key=\"canvasLocation\" x=\"220\" y=\"643\"/>\n        </scene>\n        <!--Notification Controller-->\n        <scene sceneID=\"ZPc-GJ-vnh\">\n            <objects>\n                <controller id=\"4sK-HA-Art\" customClass=\"NotificationController\" customModuleProvider=\"\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"468\" y=\"643\"/>\n        </scene>\n        <!--Notification Controller-->\n        <scene sceneID=\"Niz-AI-uX2\">\n            <objects>\n                <controller id=\"eXb-UN-Cd0\" customClass=\"NotificationController\" customModuleProvider=\"\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"468\" y=\"345\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>MMKVDemo</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t</array>\n\t<key>WKCompanionAppBundleIdentifier</key>\n\t<string>com.tencent.mmkvdemo</string>\n\t<key>WKWatchKitApp</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Contents.json",
    "content": "{\n  \"assets\" : [\n    {\n      \"filename\" : \"Circular.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"circular\"\n    },\n    {\n      \"filename\" : \"Extra Large.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"extra-large\"\n    },\n    {\n      \"filename\" : \"Graphic Bezel.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"graphic-bezel\"\n    },\n    {\n      \"filename\" : \"Graphic Circular.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"graphic-circular\"\n    },\n    {\n      \"filename\" : \"Graphic Corner.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"graphic-corner\"\n    },\n    {\n      \"filename\" : \"Graphic Extra Large.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"graphic-extra-large\"\n    },\n    {\n      \"filename\" : \"Graphic Large Rectangular.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"graphic-large-rectangular\"\n    },\n    {\n      \"filename\" : \"Modular.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"modular\"\n    },\n    {\n      \"filename\" : \"Utilitarian.imageset\",\n      \"idiom\" : \"watch\",\n      \"role\" : \"utilitarian\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"auto-scaling\" : \"auto\"\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \"<=145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">161\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">145\"\n    },\n    {\n      \"idiom\" : \"watch\",\n      \"scale\" : \"2x\",\n      \"screen-width\" : \">183\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/ExtensionDelegate.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <WatchKit/WatchKit.h>\n\n@interface ExtensionDelegate : NSObject <WKExtensionDelegate>\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/ExtensionDelegate.mm",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"ExtensionDelegate.h\"\n#import <MMKVWatchExtension/MMKV.h>\n\n@implementation ExtensionDelegate\n\n- (void)applicationDidFinishLaunching {\n    // Perform any final initialization of your application.\n    [MMKV initializeMMKV:nil];\n\n    [self funcionalTest];\n}\n\n- (void)funcionalTest {\n    auto path = [MMKV mmkvBasePath];\n    path = [path stringByDeletingLastPathComponent];\n    path = [path stringByAppendingPathComponent:@\"mmkv_2\"];\n    auto mmkv = [MMKV mmkvWithID:@\"test/case1\" rootPath:path];\n\n    [mmkv setBool:YES forKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n\n    [mmkv setInt32:-1024 forKey:@\"int32\"];\n    NSLog(@\"int32:%d\", [mmkv getInt32ForKey:@\"int32\"]);\n\n    [mmkv setUInt32:std::numeric_limits<uint32_t>::max() forKey:@\"uint32\"];\n    NSLog(@\"uint32:%u\", [mmkv getUInt32ForKey:@\"uint32\"]);\n\n    [mmkv setInt64:std::numeric_limits<int64_t>::min() forKey:@\"int64\"];\n    NSLog(@\"int64:%lld\", [mmkv getInt64ForKey:@\"int64\"]);\n\n    [mmkv setUInt64:std::numeric_limits<uint64_t>::max() forKey:@\"uint64\"];\n    NSLog(@\"uint64:%llu\", [mmkv getInt64ForKey:@\"uint64\"]);\n\n    [mmkv setFloat:-3.1415926 forKey:@\"float\"];\n    NSLog(@\"float:%f\", [mmkv getFloatForKey:@\"float\"]);\n\n    [mmkv setDouble:std::numeric_limits<double>::max() forKey:@\"double\"];\n    NSLog(@\"double:%f\", [mmkv getDoubleForKey:@\"double\"]);\n\n    [mmkv setString:@\"hello, mmkv\" forKey:@\"string\"];\n    NSLog(@\"string:%@\", [mmkv getStringForKey:@\"string\"]);\n\n    [mmkv setObject:nil forKey:@\"string\"];\n    NSLog(@\"string after set nil:%@, containsKey:%d\",\n          [mmkv getObjectOfClass:NSString.class\n                          forKey:@\"string\"],\n          [mmkv containsKey:@\"string\"]);\n\n    [mmkv setDate:[NSDate date] forKey:@\"date\"];\n    NSLog(@\"date:%@\", [mmkv getDateForKey:@\"date\"]);\n\n    [mmkv setData:[@\"hello, mmkv again and again\" dataUsingEncoding:NSUTF8StringEncoding] forKey:@\"data\"];\n    NSData *data = [mmkv getDataForKey:@\"data\"];\n    NSLog(@\"data:%@\", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);\n    NSLog(@\"data length:%zu, value size consumption:%zu\", data.length, [mmkv getValueSizeForKey:@\"data\" actualSize:NO]);\n\n    [mmkv setObject:[NSData data] forKey:@\"data\"];\n    NSLog(@\"data after set empty data:%@, containsKey:%d\",\n          [mmkv getObjectOfClass:NSData.class\n                          forKey:@\"data\"],\n          [mmkv containsKey:@\"data\"]);\n\n    [mmkv removeValueForKey:@\"bool\"];\n    NSLog(@\"bool:%d\", [mmkv getBoolForKey:@\"bool\"]);\n    [mmkv removeValuesForKeys:@[ @\"int32\", @\"uint64\" ]];\n    NSLog(@\"allKeys %@\", [mmkv allKeys]);\n\n    [mmkv close];\n}\n\n- (void)applicationDidBecomeActive {\n    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n}\n\n- (void)applicationWillResignActive {\n    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n    // Use this method to pause ongoing tasks, disable timers, etc.\n}\n\n- (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks {\n    // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one.\n    for (WKRefreshBackgroundTask * task in backgroundTasks) {\n        // Check the Class of each task to decide how to process it\n        if ([task isKindOfClass:[WKApplicationRefreshBackgroundTask class]]) {\n            // Be sure to complete the background task once you’re done.\n            WKApplicationRefreshBackgroundTask *backgroundTask = (WKApplicationRefreshBackgroundTask*)task;\n            [backgroundTask setTaskCompletedWithSnapshot:NO];\n        } else if ([task isKindOfClass:[WKSnapshotRefreshBackgroundTask class]]) {\n            // Snapshot tasks have a unique completion call, make sure to set your expiration date\n            WKSnapshotRefreshBackgroundTask *snapshotTask = (WKSnapshotRefreshBackgroundTask*)task;\n            [snapshotTask setTaskCompletedWithDefaultStateRestored:YES estimatedSnapshotExpiration:[NSDate distantFuture] userInfo:nil];\n        } else if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) {\n            // Be sure to complete the background task once you’re done.\n            WKWatchConnectivityRefreshBackgroundTask *backgroundTask = (WKWatchConnectivityRefreshBackgroundTask*)task;\n            [backgroundTask setTaskCompletedWithSnapshot:NO];\n        } else if ([task isKindOfClass:[WKURLSessionRefreshBackgroundTask class]]) {\n            // Be sure to complete the background task once you’re done.\n            WKURLSessionRefreshBackgroundTask *backgroundTask = (WKURLSessionRefreshBackgroundTask*)task;\n            [backgroundTask setTaskCompletedWithSnapshot:NO];\n        } else if ([task isKindOfClass:[WKRelevantShortcutRefreshBackgroundTask class]]) {\n            // Be sure to complete the relevant-shortcut task once you’re done.\n            WKRelevantShortcutRefreshBackgroundTask *relevantShortcutTask = (WKRelevantShortcutRefreshBackgroundTask*)task;\n            [relevantShortcutTask setTaskCompletedWithSnapshot:NO];\n        } else if ([task isKindOfClass:[WKIntentDidRunRefreshBackgroundTask class]]) {\n            // Be sure to complete the intent-did-run task once you’re done.\n            WKIntentDidRunRefreshBackgroundTask *intentDidRunTask = (WKIntentDidRunRefreshBackgroundTask*)task;\n            [intentDidRunTask setTaskCompletedWithSnapshot:NO];\n        } else {\n            // make sure to complete unhandled task types\n            [task setTaskCompletedWithSnapshot:NO];\n        }\n    }\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>WatchApp Extension</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>NSExtension</key>\n\t<dict>\n\t\t<key>NSExtensionAttributes</key>\n\t\t<dict>\n\t\t\t<key>WKAppBundleIdentifier</key>\n\t\t\t<string>com.tencent.mmkvdemo.watchkitapp</string>\n\t\t</dict>\n\t\t<key>NSExtensionPointIdentifier</key>\n\t\t<string>com.apple.watchkit</string>\n\t</dict>\n\t<key>WKExtensionDelegateClassName</key>\n\t<string>ExtensionDelegate</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/InterfaceController.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <WatchKit/WatchKit.h>\n#import <Foundation/Foundation.h>\n\n@interface InterfaceController : WKInterfaceController\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/InterfaceController.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"InterfaceController.h\"\n\n\n@interface InterfaceController ()\n\n@end\n\n\n@implementation InterfaceController\n\n- (void)awakeWithContext:(id)context {\n    [super awakeWithContext:context];\n\n    // Configure interface objects here.\n}\n\n- (void)willActivate {\n    // This method is called when watch view controller is about to be visible to user\n    [super willActivate];\n}\n\n- (void)didDeactivate {\n    // This method is called when watch view controller is no longer visible\n    [super didDeactivate];\n}\n\n@end\n\n\n\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/NotificationController.h",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import <WatchKit/WatchKit.h>\n#import <Foundation/Foundation.h>\n\n@interface NotificationController : WKUserNotificationInterfaceController\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/NotificationController.m",
    "content": "/*\n * Tencent is pleased to support the open source community by making\n * MMKV available.\n *\n * Copyright (C) 2020 THL A29 Limited, a Tencent company.\n * All rights reserved.\n *\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use\n * this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n *       https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#import \"NotificationController.h\"\n\n\n@interface NotificationController ()\n\n@end\n\n\n@implementation NotificationController\n\n- (instancetype)init {\n    self = [super init];\n    if (self){\n        // Initialize variables here.\n        // Configure interface objects here.\n        \n    }\n    return self;\n}\n\n- (void)willActivate {\n    // This method is called when watch view controller is about to be visible to user\n    [super willActivate];\n}\n\n- (void)didDeactivate {\n    // This method is called when watch view controller is no longer visible\n    [super didDeactivate];\n}\n\n- (void)didReceiveNotification:(UNNotification *)notification {\n    // This method is called when a notification needs to be presented.\n    // Implement it if you use a dynamic notification interface.\n    // Populate your dynamic notification interface as quickly as possible.\n}\n\n@end\n\n\n\n"
  },
  {
    "path": "iOS/MMKVDemo/WatchApp Extension/PushNotificationPayload.apns",
    "content": "{\n    \"aps\": {\n        \"alert\": {\n            \"body\": \"Test message\",\n            \"title\": \"Optional title\",\n            \"subtitle\": \"Optional subtitle\"\n        },\n        \"category\": \"myCategory\",\n        \"thread-id\": \"5280\"\n    },\n    \n    \"WatchKit Simulator Actions\": [\n        {\n            \"title\": \"First Button\",\n            \"identifier\": \"firstButtonAction\"\n        }\n    ],\n    \n    \"customKey\": \"Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App.\"\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/AppDelegate.h",
    "content": "//\n//  AppDelegate.h\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/AppDelegate.m",
    "content": "//\n//  AppDelegate.m\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import \"AppDelegate.h\"\n#import <MMKV/MMKV.h>\n\n@interface AppDelegate ()\n\n@end\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);\n    NSString *libraryPath = (NSString *) [paths firstObject];\n    if ([libraryPath length] > 0) {\n        NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@\"group.tencent.mmkv\"].path;\n        [MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogInfo handler:nil];\n\n        NSLog(@\"MMKV version: %@\", [MMKV version]);\n    }\n\n    return YES;\n}\n\n\n#pragma mark - UISceneSession lifecycle\n\n\n- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {\n    // Called when a new scene session is being created.\n    // Use this method to select a configuration to create the new scene with.\n    return [[UISceneConfiguration alloc] initWithName:@\"Default Configuration\" sessionRole:connectingSceneSession.role];\n}\n\n\n- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {\n    // Called when the user discards a scene session.\n    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.\n    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.\n}\n\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21507\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_12\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"21505\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"393\" height=\"852\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" fixedFrame=\"YES\" text=\"Label\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontForContentSizeCategory=\"YES\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XJ7-Q7-faQ\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"416\" width=\"393\" height=\"21\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMinX=\"YES\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\" flexibleMaxY=\"YES\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"m_label\" destination=\"XJ7-Q7-faQ\" id=\"LSj-N8-4Q0\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"138.1679389312977\" y=\"-2.1126760563380285\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>SceneDelegate</string>\n\t\t\t\t\t<key>UISceneStoryboardFile</key>\n\t\t\t\t\t<string>Main</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/SceneDelegate.h",
    "content": "//\n//  SceneDelegate.h\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>\n\n@property (strong, nonatomic) UIWindow * window;\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/SceneDelegate.m",
    "content": "//\n//  SceneDelegate.m\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import \"SceneDelegate.h\"\n\n@interface SceneDelegate ()\n\n@end\n\n@implementation SceneDelegate\n\n\n- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {\n    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\n    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\n    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\n}\n\n\n- (void)sceneDidDisconnect:(UIScene *)scene {\n    // Called as the scene is being released by the system.\n    // This occurs shortly after the scene enters the background, or when its session is discarded.\n    // Release any resources associated with this scene that can be re-created the next time the scene connects.\n    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).\n}\n\n\n- (void)sceneDidBecomeActive:(UIScene *)scene {\n    // Called when the scene has moved from an inactive state to an active state.\n    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.\n}\n\n\n- (void)sceneWillResignActive:(UIScene *)scene {\n    // Called when the scene will move from an active state to an inactive state.\n    // This may occur due to temporary interruptions (ex. an incoming phone call).\n}\n\n\n- (void)sceneWillEnterForeground:(UIScene *)scene {\n    // Called as the scene transitions from the background to the foreground.\n    // Use this method to undo the changes made on entering the background.\n}\n\n\n- (void)sceneDidEnterBackground:(UIScene *)scene {\n    // Called as the scene transitions from the foreground to the background.\n    // Use this method to save data, release shared resources, and store enough scene-specific state information\n    // to restore the scene back to its current state.\n}\n\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/ViewController.h",
    "content": "//\n//  ViewController.h\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n@interface ViewController : UIViewController\n\n\n@end\n\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/ViewController.m",
    "content": "//\n//  ViewController.m\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import \"ViewController.h\"\n#import <MMKV/MMKV.h>\n\n@interface ViewController ()\n@property(weak, nonatomic) IBOutlet UILabel *m_label;\n@end\n\n@implementation ViewController {\n    MMKV *m_kv;\n}\n\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    // Do any additional setup after loading the view.\n\n    NSData *encryptionKey = [@\"multi_process\" dataUsingEncoding:NSUTF8StringEncoding];\n    m_kv = [MMKV mmkvWithID:@\"multi_process\" cryptKey:encryptionKey mode:MMKVMultiProcess];\n\n    [[NSNotificationCenter defaultCenter] addObserver:self\n                                             selector:@selector(updateTodayContent)\n                                                 name:UIApplicationDidBecomeActiveNotification\n                                               object:nil];\n}\n\n- (void)updateTodayContent {\n    NSString *newContent = [m_kv getStringForKey:@\"content\"];\n    _m_label.text = newContent;\n}\n\n@end\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/kvdemo.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.tencent.mmkv</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "iOS/MMKVDemo/kvdemo/main.m",
    "content": "//\n//  main.m\n//  kvdemo\n//\n//  Created by lingol on 2023/6/28.\n//  Copyright © 2023 Lingol. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char * argv[]) {\n    NSString * appDelegateClassName;\n    @autoreleasepool {\n        // Setup code that might create autoreleased objects goes here.\n        appDelegateClassName = NSStringFromClass([AppDelegate class]);\n    }\n    return UIApplicationMain(argc, argv, nil, appDelegateClassName);\n}\n"
  }
]