├── Android-demo ├── .gitignore ├── .idea │ ├── .name │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── androiddemo1 │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── androiddemo1 │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── androiddemo1 │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mlnkv │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── mlnkv │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ │ ├── MLNKV-Native.cpp │ │ │ ├── MLNKVBase.cpp │ │ │ ├── MLNKVBase.h │ │ │ ├── MLNKVLog.cpp │ │ │ ├── MLNKVLog.h │ │ │ ├── MLNKVType.h │ │ │ ├── MLNKVUtility.h │ │ │ ├── MLNKVValueInfo.hpp │ │ │ └── com_mlnkv_MLNKV.h │ │ ├── java │ │ │ └── com │ │ │ │ └── mlnkv │ │ │ │ ├── MLNKV.java │ │ │ │ └── MLNKVMemoryCache.java │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── mlnkv │ │ └── ExampleUnitTest.java └── settings.gradle ├── LICENSE ├── MLNKV.podspec ├── MLNKV.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── sun.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── MLNKV.xcscheme └── xcuserdata │ └── sun.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── MLNKV.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── sun.xcuserdatad │ └── UserInterfaceState.xcuserstate ├── MLNKV ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── BaseViewController.h ├── BaseViewController.m ├── Info.plist └── main.m ├── Podfile ├── Podfile.lock ├── Pods ├── Headers │ ├── Private │ │ └── MLNKV │ │ │ ├── MLNKV.h │ │ │ ├── MLNKVBase.h │ │ │ ├── MLNKVLog.h │ │ │ ├── MLNKVMemoryCache.h │ │ │ ├── MLNKVType.h │ │ │ ├── MLNKVUtility.h │ │ │ └── MLNKVValueInfo.hpp │ └── Public │ │ └── MLNKV │ │ ├── MLNKV.h │ │ ├── MLNKVBase.h │ │ ├── MLNKVLog.h │ │ ├── MLNKVMemoryCache.h │ │ ├── MLNKVType.h │ │ ├── MLNKVUtility.h │ │ └── MLNKVValueInfo.hpp ├── Local Podspecs │ └── MLNKV.podspec.json ├── MLNKV │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── cpp │ │ ├── MLNKVBase.cpp │ │ ├── MLNKVBase.h │ │ ├── MLNKVLog.cpp │ │ ├── MLNKVLog.h │ │ ├── MLNKVType.h │ │ ├── MLNKVUtility.h │ │ └── MLNKVValueInfo.hpp │ │ └── iOS │ │ ├── MLNKV.h │ │ ├── MLNKV.mm │ │ ├── MLNKVMemoryCache.h │ │ └── MLNKVMemoryCache.m ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── sun.xcuserdatad │ │ └── xcschemes │ │ ├── MLNKV.xcscheme │ │ ├── Pods-MLNKV.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── MLNKV │ ├── MLNKV-Info.plist │ ├── MLNKV-dummy.m │ ├── MLNKV-prefix.pch │ ├── MLNKV-umbrella.h │ ├── MLNKV.modulemap │ └── MLNKV.xcconfig │ └── Pods-MLNKV │ ├── Pods-MLNKV-Info.plist │ ├── Pods-MLNKV-acknowledgements.markdown │ ├── Pods-MLNKV-acknowledgements.plist │ ├── Pods-MLNKV-dummy.m │ ├── Pods-MLNKV-frameworks-Debug-input-files.xcfilelist │ ├── Pods-MLNKV-frameworks-Debug-output-files.xcfilelist │ ├── Pods-MLNKV-frameworks-Release-input-files.xcfilelist │ ├── Pods-MLNKV-frameworks-Release-output-files.xcfilelist │ ├── Pods-MLNKV-frameworks.sh │ ├── Pods-MLNKV-umbrella.h │ ├── Pods-MLNKV.debug.xcconfig │ ├── Pods-MLNKV.modulemap │ └── Pods-MLNKV.release.xcconfig ├── README.md ├── Source ├── cpp │ ├── MLNKVBase.cpp │ ├── MLNKVBase.h │ ├── MLNKVLog.cpp │ ├── MLNKVLog.h │ ├── MLNKVType.h │ ├── MLNKVUtility.h │ └── MLNKVValueInfo.hpp └── iOS │ ├── MLNKV.h │ ├── MLNKV.mm │ ├── MLNKVMemoryCache.h │ └── MLNKVMemoryCache.m └── img ├── getString.png ├── memory.png ├── setInt.png └── setString.png /Android-demo/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /Android-demo/.idea/.name: -------------------------------------------------------------------------------- 1 | AndroidDemo1 -------------------------------------------------------------------------------- /Android-demo/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /Android-demo/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /Android-demo/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /Android-demo/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Android-demo/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Android-demo/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android-demo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "com.example.androiddemo1" 8 | minSdkVersion 21 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | // externalNativeBuild { 14 | // cmake { 15 | // cppFlags "-std=c++11" 16 | // } 17 | // } 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | // externalNativeBuild { 27 | // cmake { 28 | // path "CMakeLists.txt" 29 | // } 30 | // } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(dir: 'libs', include: ['*.jar']) 35 | implementation 'androidx.appcompat:appcompat:1.1.0' 36 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 40 | implementation project(path: ':mlnkv') 41 | 42 | // implementation "com.mlnkv:mlnkv:0.0.1" 43 | } 44 | -------------------------------------------------------------------------------- /Android-demo/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /Android-demo/app/src/androidTest/java/com/example/androiddemo1/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.androiddemo1; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.example.androiddemo1", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/java/com/example/androiddemo1/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.androiddemo1; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.app.ActivityManager; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | 9 | import com.mlnkv.MLNKV; 10 | 11 | import java.lang.reflect.Array; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.Random; 15 | import java.util.Timer; 16 | import java.util.TimerTask; 17 | 18 | 19 | public class MainActivity extends AppCompatActivity { 20 | 21 | private MLNKV mlnkv; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | 28 | 29 | long time = System.currentTimeMillis(); 30 | 31 | MLNKV.initializeBasePath(this); 32 | mlnkv = MLNKV.defaultMLNKV(); 33 | mlnkv.setInt32("key1", -11); 34 | mlnkv.setString( "key2", "value我发😁1"); 35 | 36 | Log.e("MMMM", String.valueOf(mlnkv.getInt32("key1", 0))); 37 | Log.e("MMMM", mlnkv.getString("key2", "")); 38 | 39 | for (String ss : mlnkv.allKeys()) { 40 | Log.e("MMMM", ss); 41 | } 42 | 43 | String[] sss = new String[] {"ni", "22"}; 44 | 45 | mlnkv.setObject("testKey", sss); 46 | // 47 | Log.e("MMMM", mlnkv.getObject("testKey", null).toString()); 48 | 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidDemo1 3 | 4 | -------------------------------------------------------------------------------- /Android-demo/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Android-demo/app/src/test/java/com/example/androiddemo1/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.androiddemo1; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /Android-demo/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.5.2' 11 | //加入bintray-release插件 12 | classpath 'com.novoda:bintray-release:0.9.1' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | 24 | // maven {url "https://dl.bintray.com/sunzt8801/MLNKV"} 25 | } 26 | 27 | // tasks.withType(JavaDoc) { 28 | // options{ 29 | // encoding "UTF-8" 30 | // charset "UTF-8" 31 | // links "http://docs.oracle.com/javase/7/docs/api" 32 | // } 33 | // } 34 | } 35 | 36 | task clean(type: Delete) { 37 | delete rootProject.buildDir 38 | } 39 | -------------------------------------------------------------------------------- /Android-demo/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /Android-demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Android-demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Nov 16 12:45:51 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /Android-demo/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /Android-demo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | mlnkv 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | 21 | src/main/cpp/MLNKV-Native.cpp 22 | src/main/cpp/MLNKVLog.cpp 23 | src/main/cpp/MLNKVBase.cpp 24 | ) 25 | 26 | # Searches for a specified prebuilt library and stores the path as a 27 | # variable. Because CMake includes system libraries in the search path by 28 | # default, you only need to specify the name of the public NDK library 29 | # you want to add. CMake verifies that the library exists before 30 | # completing its build. 31 | 32 | find_library( # Sets the name of the path variable. 33 | log-lib 34 | 35 | # Specifies the name of the NDK library that 36 | # you want CMake to locate. 37 | log ) 38 | 39 | # Specifies libraries CMake should link to your target library. You 40 | # can link multiple libraries, such as libraries you define in this 41 | # build script, prebuilt third-party libraries, or system libraries. 42 | 43 | target_link_libraries( # Specifies the target library. 44 | mlnkv 45 | # Links the target library to the log library 46 | # included in the NDK. 47 | ${log-lib} 48 | ) -------------------------------------------------------------------------------- /Android-demo/mlnkv/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release'//添加 bintray-release 配置 3 | 4 | android { 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.2" 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 29 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles 'consumer-rules.pro' 17 | 18 | externalNativeBuild { 19 | cmake { 20 | cppFlags "-std=c++11" 21 | } 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | 32 | externalNativeBuild { 33 | cmake { 34 | path "CMakeLists.txt" 35 | } 36 | } 37 | 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | 43 | implementation 'androidx.appcompat:appcompat:1.1.0' 44 | testImplementation 'junit:junit:4.12' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 47 | } 48 | 49 | 50 | publish { 51 | userOrg = 'sunzt8801' 52 | repoName = 'MLNKV' 53 | groupId = 'com.mlnkv' 54 | artifactId = 'mlnkv' 55 | publishVersion = '0.0.4' 56 | desc = 'MLNKV是基于mmap实现的高性能、内存占用低、跨平台(支持iOS与Android)的Key-Value组件。' 57 | website = 'https://github.com/momotech/MLNKV' 58 | } 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/Android-demo/mlnkv/consumer-rules.pro -------------------------------------------------------------------------------- /Android-demo/mlnkv/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/androidTest/java/com/example/mlnkv/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mlnkv; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.example.mlnkv.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKV-Native.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | 4 | #include "com_mlnkv_MLNKV.h" 5 | #include 6 | #include 7 | 8 | #include "MLNKVBase.h" 9 | 10 | #define MLNKV_LOG_TAG "MLNKV" 11 | #define MLNKV_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MLNKV_LOG_TAG, __VA_ARGS__) 12 | #define MLNKV_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, MLNKV_LOG_TAG, __VA_ARGS__) 13 | #define MLNKV_BRIDGE(method) ((MLNKVBase *)mlnkv)->method 14 | #define MLNKV_JSTRING_TO_STRING(value) jstringTostring(env, value) 15 | #define MLNKV_STRING_TO_JSTRING(value) stringTojstring(env, value) 16 | #define MLNKV_EXIST_MLNKV(result) if (mlnkv == -1 || !(MLNKVBase *)mlnkv) {\ 17 | MLNKV_LOGE("mlnkv obj is nil...");\ 18 | return result;\ 19 | } 20 | 21 | #define MLNKV_SETKEY_RETURN(result) if (!key) {\ 22 | MLNKV_LOGE("key can't be nil...");\ 23 | return result;\ 24 | }\ 25 | MLNKV_EXIST_MLNKV(result) 26 | 27 | //#define MLNKV_SETKEY_RETURN(result) 28 | //#define MLNKV_EXIST_MLNKV(result) 29 | 30 | using namespace std; 31 | 32 | namespace mkv { 33 | // static MLNKVBase *getmlnbaseobj(JNIEnv *env, jobject obj) { 34 | // jclass objClz = env->GetObjectClass(obj); 35 | // if (objClz == nullptr) { 36 | // return nullptr; 37 | // } 38 | // jfieldID fid = env->GetFieldID(objClz, "nativeMLNKVPtr", "J"); 39 | // if (fid == nullptr) { 40 | // LOGE("env->GetFieldID error"); 41 | // return nullptr; 42 | // } 43 | // jlong ptr = env->GetLongField(obj, fid); 44 | // return (MLNKVBase *) ptr; 45 | // } 46 | 47 | static string jstringTostring(JNIEnv *env, jstring str) { 48 | if (str) { 49 | const char *kstr = env->GetStringUTFChars(str, nullptr); 50 | if (kstr) { 51 | string result(kstr); 52 | env->ReleaseStringUTFChars(str, kstr); 53 | return result; 54 | } 55 | } 56 | return ""; 57 | } 58 | 59 | static jstring stringTojstring(JNIEnv *env, const string &str) { 60 | return env->NewStringUTF(str.c_str()); 61 | } 62 | 63 | static jobjectArray vectorTojarray(JNIEnv *env, const vector &arr) { 64 | if (!arr.empty()) { 65 | jobjectArray result = 66 | env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr); 67 | if (result) { 68 | for (size_t index = 0; index < arr.size(); index++) { 69 | jstring value = mkv::stringTojstring(env, arr[index]); 70 | env->SetObjectArrayElement(result, index, value); 71 | env->DeleteLocalRef(value); 72 | } 73 | } 74 | return result; 75 | } 76 | return nullptr; 77 | } 78 | } 79 | 80 | using namespace mkv; 81 | 82 | #ifdef __cplusplus 83 | extern "C" { 84 | #endif 85 | 86 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_initMLNKV 87 | (JNIEnv *env, jobject obj, jstring path) { 88 | const char *filePath = env->GetStringUTFChars(path, nullptr); 89 | MLNKVBase *mlnkv = new MLNKVBase(filePath); 90 | env->ReleaseStringUTFChars(path, filePath); 91 | return (jlong)mlnkv; 92 | } 93 | 94 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_destoryMLNKV 95 | (JNIEnv *env, jobject obj, jlong ptr) { 96 | if (ptr == -1) { 97 | return; 98 | } 99 | delete ((MLNKVBase *)ptr); 100 | } 101 | 102 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setBytes 103 | (JNIEnv *env, jobject obj, jlong mlnkv, jbyteArray bytes, jlong length, jstring key) { 104 | MLNKV_SETKEY_RETURN((jboolean)false); 105 | jbyte *value = env->GetByteArrayElements(bytes, nullptr); 106 | jboolean result = (jboolean)MLNKV_BRIDGE(setBytes(value, (const size_t)length, MLNKV_JSTRING_TO_STRING(key))); 107 | if (value) { 108 | env->ReleaseByteArrayElements(bytes, value, 0); 109 | } 110 | return result; 111 | } 112 | 113 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setString 114 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring value, jstring key) { 115 | MLNKV_SETKEY_RETURN((jboolean)false); 116 | if (!value) { 117 | return (jboolean)MLNKV_BRIDGE(remove(MLNKV_JSTRING_TO_STRING(key))); 118 | } 119 | return (jboolean)MLNKV_BRIDGE(setString(MLNKV_JSTRING_TO_STRING(value), MLNKV_JSTRING_TO_STRING(key))); 120 | } 121 | 122 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setBool 123 | (JNIEnv *env, jobject obj, jlong mlnkv, jboolean value, jstring key) { 124 | MLNKV_SETKEY_RETURN((jboolean)false); 125 | return (jboolean)MLNKV_BRIDGE(setBool(value, MLNKV_JSTRING_TO_STRING(key))); 126 | } 127 | 128 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setInt32 129 | (JNIEnv *env, jobject obj, jlong mlnkv, jint value, jstring key) { 130 | MLNKV_SETKEY_RETURN((jboolean)false); 131 | return (jboolean)MLNKV_BRIDGE(setInt32(value, MLNKV_JSTRING_TO_STRING(key))); 132 | } 133 | 134 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setInt64 135 | (JNIEnv *env, jobject obj, jlong mlnkv, jlong value, jstring key) { 136 | MLNKV_SETKEY_RETURN((jboolean)false); 137 | return (jboolean)MLNKV_BRIDGE(setInt64(value, MLNKV_JSTRING_TO_STRING(key))); 138 | } 139 | 140 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setFloat 141 | (JNIEnv *env, jobject obj, jlong mlnkv, jfloat value, jstring key) { 142 | MLNKV_SETKEY_RETURN((jboolean)false); 143 | return (jboolean)MLNKV_BRIDGE(setFloat(value, MLNKV_JSTRING_TO_STRING(key))); 144 | } 145 | 146 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setDouble 147 | (JNIEnv *env, jobject obj, jlong mlnkv, jdouble value, jstring key) { 148 | MLNKV_SETKEY_RETURN((jboolean)false); 149 | return (jboolean)MLNKV_BRIDGE(setDouble(value, MLNKV_JSTRING_TO_STRING(key))); 150 | } 151 | 152 | JNIEXPORT jbyteArray JNICALL Java_com_mlnkv_MLNKV_getBytes 153 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 154 | MLNKV_SETKEY_RETURN(nullptr); 155 | void *value; 156 | size_t length = 0; 157 | if (MLNKV_BRIDGE(getBytesForKey(MLNKV_JSTRING_TO_STRING(key), value, length)) && length > 0) { 158 | jbyteArray bytes = env->NewByteArray(length); 159 | env->SetByteArrayRegion(bytes, 0, length, (jbyte *)value); 160 | return bytes; 161 | } 162 | return nullptr; 163 | } 164 | 165 | JNIEXPORT jstring JNICALL Java_com_mlnkv_MLNKV_getString 166 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 167 | MLNKV_SETKEY_RETURN(nullptr); 168 | string value; 169 | if (MLNKV_BRIDGE(getStringForKey(MLNKV_JSTRING_TO_STRING(key), value))) { 170 | return MLNKV_STRING_TO_JSTRING(value); 171 | } 172 | return nullptr; 173 | } 174 | 175 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_getBool 176 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jboolean defaultValue) { 177 | MLNKV_SETKEY_RETURN((jboolean)false); 178 | return (jboolean)MLNKV_BRIDGE(getBoolForKey(MLNKV_JSTRING_TO_STRING(key),defaultValue)); 179 | } 180 | 181 | JNIEXPORT jint JNICALL Java_com_mlnkv_MLNKV_getInt32 182 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jint defaultValue) { 183 | MLNKV_SETKEY_RETURN(0); 184 | return (jint)MLNKV_BRIDGE(getInt32ForKey(MLNKV_JSTRING_TO_STRING(key), defaultValue)); 185 | } 186 | 187 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_getInt64 188 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jlong defaultValue) { 189 | MLNKV_SETKEY_RETURN(0); 190 | return (jlong)MLNKV_BRIDGE(getInt64ForKey(MLNKV_JSTRING_TO_STRING(key), defaultValue)); 191 | } 192 | 193 | JNIEXPORT jfloat JNICALL Java_com_mlnkv_MLNKV_getFloat 194 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jfloat defaultValue) { 195 | MLNKV_SETKEY_RETURN(0); 196 | return (jfloat)MLNKV_BRIDGE(getFloatForKey(MLNKV_JSTRING_TO_STRING(key), defaultValue)); 197 | } 198 | 199 | JNIEXPORT jdouble JNICALL Java_com_mlnkv_MLNKV_getDouble 200 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jdouble defaultValue) { 201 | MLNKV_SETKEY_RETURN(0); 202 | return (jdouble)MLNKV_BRIDGE(getDoubleForKey(MLNKV_JSTRING_TO_STRING(key), defaultValue)); 203 | } 204 | 205 | JNIEXPORT jint JNICALL Java_com_mlnkv_MLNKV_valueType 206 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 207 | MLNKV_SETKEY_RETURN(0); 208 | MLNKVValueType valueType = MLNKVValueType_None; 209 | MLNKV_BRIDGE(getValueSizeForKey(MLNKV_JSTRING_TO_STRING(key), valueType)); 210 | return (jint)valueType; 211 | } 212 | 213 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_getVauleSize 214 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 215 | MLNKV_SETKEY_RETURN(0); 216 | MLNKVValueType type = MLNKVValueType_None; 217 | return (jlong)MLNKV_BRIDGE(getValueSizeForKey(MLNKV_JSTRING_TO_STRING(key), type)); 218 | } 219 | 220 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_containsKey 221 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 222 | MLNKV_SETKEY_RETURN((jboolean)false); 223 | return (jboolean)MLNKV_BRIDGE(containsKey(MLNKV_JSTRING_TO_STRING(key))); 224 | } 225 | 226 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_count 227 | (JNIEnv *env, jobject obj, jlong mlnkv) { 228 | MLNKV_EXIST_MLNKV(0); 229 | return (jlong)MLNKV_BRIDGE(count()); 230 | } 231 | 232 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_totalUsedSize 233 | (JNIEnv *env, jobject obj, jlong mlnkv) { 234 | MLNKV_EXIST_MLNKV(0); 235 | return (jlong)MLNKV_BRIDGE(totalUsedSize()); 236 | } 237 | 238 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_fileSize 239 | (JNIEnv *env, jobject obj, jlong mlnkv) { 240 | MLNKV_EXIST_MLNKV(0); 241 | return (jlong)MLNKV_BRIDGE(getFileSize()); 242 | } 243 | 244 | JNIEXPORT jstring JNICALL Java_com_mlnkv_MLNKV_filePath 245 | (JNIEnv *env, jobject obj, jlong mlnkv) { 246 | MLNKV_EXIST_MLNKV(nullptr); 247 | string filePath = MLNKV_BRIDGE(getFilePath()); 248 | return MLNKV_STRING_TO_JSTRING(filePath); 249 | } 250 | 251 | JNIEXPORT jobjectArray JNICALL Java_com_mlnkv_MLNKV_allKeys 252 | (JNIEnv *env, jobject obj, jlong mlnkv) { 253 | MLNKV_EXIST_MLNKV(nullptr); 254 | vector keys = MLNKV_BRIDGE(allKeys()); 255 | return vectorTojarray(env, keys); 256 | } 257 | 258 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_remove 259 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key) { 260 | MLNKV_SETKEY_RETURN((jboolean) false); 261 | return (jboolean)MLNKV_BRIDGE(remove(MLNKV_JSTRING_TO_STRING(key))); 262 | } 263 | 264 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_clearAll 265 | (JNIEnv *env, jobject obj, jlong mlnkv) { 266 | MLNKV_EXIST_MLNKV(); 267 | MLNKV_BRIDGE(clearAll()); 268 | } 269 | 270 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_trim 271 | (JNIEnv *env, jobject obj, jlong mlnkv) { 272 | MLNKV_EXIST_MLNKV(); 273 | MLNKV_BRIDGE(trim()); 274 | } 275 | 276 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_sync 277 | (JNIEnv *env, jobject obj, jlong mlnkv, jboolean isSync) { 278 | MLNKV_EXIST_MLNKV(); 279 | MLNKV_BRIDGE(sync(isSync)); 280 | } 281 | 282 | 283 | #ifdef __cplusplus 284 | } 285 | #endif -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVBase.hpp 3 | // MLNKV 4 | // 5 | // 6 | // Copyright © 2019 . All rights reserved. 7 | // 8 | 9 | #ifndef MLNKVManager_hpp 10 | #define MLNKVManager_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "MLNKVType.h" 20 | #include "MLNKVValueInfo.hpp" 21 | 22 | 23 | typedef enum : uint8_t { 24 | MLNKVReusedAvailableStatusNone = 0, 25 | MLNKVReusedAvailableStatusReused = 1, 26 | MLNKVReusedAvailableStatusAppend = 2, 27 | } MLNKVReusedAvailableStatus; 28 | 29 | class MLNKVBase { 30 | char *filePath; 31 | size_t fileSize; 32 | size_t usedSize; 33 | size_t unavailabledSize; 34 | int file; 35 | char *filemmap; 36 | std::unordered_map keysMap; 37 | std::unordered_map > unavailablesMap; 38 | 39 | std::mutex mutex; 40 | 41 | bool loadDataFromFile(bool openFile = true); 42 | bool isFileValid(); 43 | bool ensureMemorySize(size_t newSize); 44 | bool reuseMemoryOffset(size_t size, size_t &offset); 45 | bool sortUnavailableMemory(bool force); 46 | void markUnAvailable(MLNKVValueInfo &valueInfo); 47 | 48 | bool writeUInt32(uint32_t value, MLNKVValueType valueType, const std::string &key); 49 | bool writeUInt64(uint64_t value, MLNKVValueType valueType, const std::string &key); 50 | MLNKVReusedAvailableStatus getAvailableOffset(size_t dataSize, size_t &offset); 51 | 52 | bool removeValue(const std::string &key); 53 | bool removeValue(const std::string &key, std::unordered_map::iterator &valueItera, bool returnValue); 54 | bool clear(); 55 | 56 | public: 57 | MLNKVBase(const std::string &path); 58 | ~MLNKVBase(); 59 | 60 | // write bytes 61 | bool writeBytes(const void *value, const size_t size, MLNKVValueType valueType, const std::string &key); 62 | // read bytes 63 | bool readValueBytes(const std::string &key, MLNKVValueType &valueType, void* &value, size_t &size); 64 | 65 | // set 66 | 67 | bool setBytes(const void *value, const size_t size, const std::string &key); 68 | 69 | bool setString(const std::string &value, const std::string &key); 70 | 71 | bool setBool(bool value, const std::string &key); 72 | 73 | bool setInt32(int32_t value, const std::string &key); 74 | 75 | bool setInt64(int64_t value, const std::string &key); 76 | 77 | bool setFloat(float value, const std::string &key); 78 | 79 | bool setDouble(double value, const std::string &key); 80 | 81 | // get 82 | 83 | bool getBytesForKey(const std::string &key, void* &value, size_t &size); 84 | 85 | bool getStringForKey(const std::string &key, std::string &result); 86 | 87 | bool getBoolForKey(const std::string &key, bool defaultValue = false); 88 | 89 | int32_t getInt32ForKey(const std::string &key, int32_t defaultValue = 0); 90 | 91 | int64_t getInt64ForKey(const std::string &key, int64_t defaultValue = 0); 92 | 93 | float getFloatForKey(const std::string &key, float defaultValue = 0); 94 | 95 | double getDoubleForKey(const std::string &key, double defaultValue = 0); 96 | 97 | 98 | // size 99 | size_t getValueSizeForKey(const std::string &key, MLNKVValueType &valueType); 100 | 101 | bool containsKey(const std::string &key); 102 | 103 | size_t count(); 104 | 105 | size_t totalUsedSize(); 106 | 107 | std::vector allKeys(); 108 | 109 | 110 | // remove 111 | bool remove(const std::string &key); 112 | void clearAll(); 113 | void trim(); 114 | 115 | // sync 116 | void sync(bool isSync); 117 | 118 | // 119 | std::string getFilePath(); 120 | size_t getFileSize(); 121 | 122 | }; 123 | 124 | #endif /* MLNKVManager_hpp */ 125 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVLog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MILKVLog.c 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #include "MLNKVLog.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...) { 16 | 17 | std::string message; 18 | char buffer[16]; 19 | 20 | va_list args; 21 | va_start(args, format); 22 | auto length = vsnprintf(buffer, sizeof(buffer), format, args); 23 | va_end(args); 24 | 25 | if (length < 0) { // something wrong 26 | message = {}; 27 | } else if (length < sizeof(buffer)) { 28 | message = std::string(buffer, static_cast(length)); 29 | } else { 30 | message.resize(static_cast(length), '\0'); 31 | va_start(args, format); 32 | vsnprintf(const_cast(message.data()), static_cast(length) + 1, 33 | format, args); 34 | va_end(args); 35 | } 36 | 37 | time_t t; 38 | struct tm *timeinfo; 39 | time(&t); 40 | timeinfo = localtime(&t); 41 | 42 | char timeStr[128]; 43 | strftime(timeStr, sizeof(timeStr), "%F %T", timeinfo); 44 | 45 | struct timeval tv; 46 | gettimeofday(&tv, NULL); 47 | 48 | // printf("%s.%d <%s:%s::%d> [%c] %s \n", timeStr, (int)tv.tv_usec, file, func, line, (level == MLNLogDebug)?'D':'E', message.c_str()); 49 | 50 | __android_log_print((level == MLNLogDebug)?ANDROID_LOG_DEBUG:ANDROID_LOG_ERROR, "MLNKV","%s.%d <%s:%s::%d> [%c] %s \n", timeStr, (int)tv.tv_usec, file, func, line, (level == MLNLogDebug)?'D':'E', message.c_str()); 51 | } 52 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVLog_h.h 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVLog_h 9 | #define MLNKVLog_h 10 | 11 | #include 12 | #include 13 | 14 | typedef enum { 15 | MLNLogDebug = 0, 16 | MLNLogError = 1, 17 | } MLNLogLevel; 18 | 19 | 20 | //#define MLNDebug 21 | 22 | #define __mlnkv_filename__ (strrchr(__FILE__, '/') + 1) 23 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...); 24 | 25 | #ifdef MLNDebug 26 | 27 | #define MLNKVLog(format, ...) MLNKVLogWithLevel(MLNLogDebug, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 28 | 29 | 30 | #else 31 | 32 | #define MLNKVLog(format, ...) 33 | 34 | #endif 35 | 36 | #define MLNKVError(format, ...) MLNKVLogWithLevel(MLNLogError, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 37 | 38 | 39 | #endif /* MLNKVLog_h */ 40 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVType.h 3 | // 4 | // 5 | // 6 | 7 | #ifndef MLNKVType_h 8 | #define MLNKVType_h 9 | 10 | typedef enum : uint8_t { 11 | MLNKVValueType_None = 0, 12 | MLNKVValueType_Used = 1, 13 | MLNKVValueType_Bool = 2, 14 | MLNKVValueType_Float = 3, 15 | MLNKVValueType_Int32 = 4, 16 | MLNKVValueType_Double = 5, 17 | MLNKVValueType_Int64 = 6, 18 | MLNKVValueType_String = 7, 19 | MLNKVValueType_Bytes = 8, 20 | } MLNKVValueType; 21 | 22 | 23 | #endif /* MLNKVType_h */ 24 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVUtility.hpp 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVUtility_hpp 9 | #define MLNKVUtility_hpp 10 | 11 | #include 12 | #include 13 | 14 | 15 | template 16 | union MLNKVConverter { 17 | static_assert(sizeof(T) == sizeof(P), "size not match"); 18 | T first; 19 | P second; 20 | }; 21 | 22 | static inline uint64_t MLNKVInt64ToUInt64(int64_t v) { 23 | MLNKVConverter converter; 24 | converter.first = v; 25 | return converter.second; 26 | } 27 | 28 | static inline int64_t MLNKVUInt64ToInt64(uint64_t v) { 29 | MLNKVConverter converter; 30 | converter.second = v; 31 | return converter.first; 32 | } 33 | 34 | static inline uint32_t MLNKVInt32ToUInt32(int32_t v) { 35 | MLNKVConverter converter; 36 | converter.first = v; 37 | return converter.second; 38 | } 39 | 40 | static inline int32_t MLNKVUInt32ToInt32(uint32_t v) { 41 | MLNKVConverter converter; 42 | converter.second = v; 43 | return converter.first; 44 | } 45 | 46 | static inline uint32_t MLNKVFloatToUInt32(float v){ 47 | MLNKVConverter converter; 48 | converter.first = v; 49 | return converter.second; 50 | } 51 | 52 | static inline float MLNKVUInt32ToFloat(uint32_t v) { 53 | MLNKVConverter converter; 54 | converter.first = v; 55 | return converter.second; 56 | } 57 | 58 | static inline uint64_t MLNKVDoubleToUInt64(double v) { 59 | MLNKVConverter converter; 60 | converter.first = v; 61 | return converter.second; 62 | } 63 | 64 | static inline double MLNKVUInt64ToDouble(uint64_t v) { 65 | MLNKVConverter converter; 66 | converter.first = v; 67 | return converter.second; 68 | } 69 | 70 | 71 | static inline uint8_t MLNKVRawVarUInt32Size(uint32_t value) { 72 | if ((value & (0xffffffff << 8)) == 0) { 73 | return 1; 74 | } else if ((value & (0xffffffff << 16)) == 0) { 75 | return 2; 76 | } else if ((value & (0xffffffff << 24)) == 0) { 77 | return 3; 78 | } 79 | return 4; 80 | } 81 | 82 | static inline uint8_t MLNKVRawVarUInt64Size(uint64_t value) { 83 | if ((value & (0xffffffffffffffffL << 8)) == 0) { 84 | return 1; 85 | } else if ((value & (0xffffffffffffffffL << 16)) == 0) { 86 | return 2; 87 | } else if ((value & (0xffffffffffffffffL << 24)) == 0) { 88 | return 3; 89 | } else if ((value & (0xffffffffffffffffL << 32)) == 0) { 90 | return 4; 91 | } else if ((value & (0xffffffffffffffffL << 40)) == 0) { 92 | return 5; 93 | } else if ((value & (0xffffffffffffffffL << 48)) == 0) { 94 | return 6; 95 | } else if ((value & (0xffffffffffffffffL << 56)) == 0) { 96 | return 7; 97 | } 98 | return 8; 99 | } 100 | 101 | //constexpr int16_t MLNKVBoolSize(bool value) { 102 | // return 1; 103 | //} 104 | 105 | static const uint8_t MLNKV32Size = 4; 106 | 107 | static const uint8_t MLNKV64Size = 8; 108 | // 109 | ////// 判断机器模式 大小端 110 | /////** 111 | //// 1) Little-Endian:就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 112 | //// 2) Big-Endian:就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 113 | //// */ 114 | //static inline bool MLNKVIsLittleEdian() { 115 | // union { 116 | // unsigned int a; 117 | // unsigned char b; 118 | // } c; 119 | // c.a = 1; 120 | // return (c.b == 1); 121 | //} 122 | 123 | static inline uint32_t MLNKVReadUInt32(uint8_t* ptr, uint8_t size) { 124 | uint32_t value = 0; 125 | for (uint8_t i = 0; i < size; i++) { 126 | value |= (uint32_t)(ptr[i] & 0xFF) << (8 * i); 127 | } 128 | return value; 129 | } 130 | 131 | static inline uint64_t MLNKVReadUInt64(uint8_t* ptr, uint8_t size) { 132 | uint64_t value = 0; 133 | for (uint8_t i = 0; i < size; i++) { 134 | value |= (uint64_t)(ptr[i] & 0xFF) << (8 * i); 135 | } 136 | return value; 137 | } 138 | 139 | static inline bool MLNKVWriteUInt32(uint8_t* ptr, uint32_t value, uint8_t size) { 140 | for (uint8_t i = 0; i < size; i++) { 141 | ptr[i] = (uint8_t)(value & 0xFF); 142 | value = value >> 8; 143 | } 144 | return true; 145 | } 146 | 147 | static inline bool MLNKVWriteUInt64(uint8_t* ptr, uint64_t value, uint8_t size) { 148 | for (uint8_t i = 0; i < size; i++) { 149 | ptr[i] = (uint8_t)(value & 0xFF); 150 | value = value >> 8; 151 | } 152 | return true; 153 | } 154 | 155 | 156 | #endif /* MLNKVUtility_hpp */ 157 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/MLNKVValueInfo.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVValueInfo.hpp 3 | // MLNKV 4 | // 5 | // 6 | 7 | #ifndef MLNKVValueInfo_hpp 8 | #define MLNKVValueInfo_hpp 9 | 10 | #include 11 | #include "MLNKVType.h" 12 | 13 | 14 | class MLNKVValueInfo { 15 | 16 | public: 17 | 18 | MLNKVValueType type; 19 | size_t offset; 20 | uint8_t extSize; 21 | uint32_t keySize; 22 | size_t valueSize; 23 | 24 | MLNKVValueInfo(MLNKVValueType type = MLNKVValueType_None): type(type) {} 25 | 26 | MLNKVValueInfo(MLNKVValueInfo &&other)noexcept : 27 | type(other.type), 28 | offset(other.offset), 29 | extSize(other.extSize), 30 | keySize(other.keySize), 31 | valueSize(other.valueSize) { 32 | other.type = MLNKVValueType_None; 33 | other.offset = 0; 34 | other.extSize = 0; 35 | other.keySize = 0; 36 | other.valueSize = 0; 37 | } 38 | MLNKVValueInfo &operator=(MLNKVValueInfo &&other) noexcept { 39 | std::swap(type, other.type); 40 | std::swap(offset, other.offset); 41 | std::swap(extSize, other.extSize); 42 | std::swap(keySize, other.keySize); 43 | std::swap(valueSize, other.valueSize); 44 | return *this; 45 | } 46 | 47 | ~MLNKVValueInfo() {} 48 | 49 | private: 50 | MLNKVValueInfo(const MLNKVValueInfo &other) = delete; 51 | MLNKVValueInfo &operator=(const MLNKVValueInfo &other) = delete; 52 | 53 | }; 54 | 55 | #endif /* MLNKVValueInfo_hpp */ 56 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/cpp/com_mlnkv_MLNKV.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_mlnkv_MLNKV */ 4 | 5 | #ifndef _Included_com_mlnkv_MLNKV 6 | #define _Included_com_mlnkv_MLNKV 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* 12 | * Class: com_mlnkv_MLNKV 13 | * Method: initMLNKV 14 | * Signature: (Ljava/lang/String;)J 15 | */ 16 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_initMLNKV 17 | (JNIEnv *env, jobject obj, jstring path); 18 | 19 | /* 20 | * Class: com_mlnkv_MLNKV 21 | * Method: destoryMLNKV 22 | * Signature: (J)V 23 | */ 24 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_destoryMLNKV 25 | (JNIEnv *env, jobject obj, jlong mlnkv); 26 | 27 | 28 | /* 29 | * Class: com_mlnkv_MLNKV 30 | * Method: setBytes 31 | * Signature: (J[BJLjava/lang/String;)Z 32 | */ 33 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setBytes 34 | (JNIEnv *env, jobject obj, jlong mlnkv, jbyteArray value, jlong length, jstring key); 35 | 36 | /* 37 | * Class: com_mlnkv_MLNKV 38 | * Method: setString 39 | * Signature: (JLjava/lang/String;Ljava/lang/String;)Z 40 | */ 41 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setString 42 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring value, jstring key); 43 | 44 | /* 45 | * Class: com_mlnkv_MLNKV 46 | * Method: setBool 47 | * Signature: (JLjava/lang/Boolean;Ljava/lang/String;)Z 48 | */ 49 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setBool 50 | (JNIEnv *env, jobject obj, jlong mlnkv, jboolean value, jstring key); 51 | 52 | /* 53 | * Class: com_mlnkv_MLNKV 54 | * Method: setInt32 55 | * Signature: (JILjava/lang/String;)Z 56 | */ 57 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setInt32 58 | (JNIEnv *env, jobject obj, jlong mlnkv, jint value, jstring key); 59 | 60 | /* 61 | * Class: com_mlnkv_MLNKV 62 | * Method: setInt64 63 | * Signature: (JJLjava/lang/String;)Z 64 | */ 65 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setInt64 66 | (JNIEnv *env, jobject obj, jlong mlnkv, jlong value, jstring key); 67 | 68 | /* 69 | * Class: com_mlnkv_MLNKV 70 | * Method: setFloat 71 | * Signature: (JFLjava/lang/String;)Z 72 | */ 73 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setFloat 74 | (JNIEnv *env, jobject obj, jlong mlnkv, jfloat value, jstring key); 75 | 76 | /* 77 | * Class: com_mlnkv_MLNKV 78 | * Method: setDouble 79 | * Signature: (JDLjava/lang/String;)Z 80 | */ 81 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_setDouble 82 | (JNIEnv *env, jobject obj, jlong mlnkv, jdouble value, jstring key); 83 | 84 | /* 85 | * Class: com_mlnkv_MLNKV 86 | * Method: getBytes 87 | * Signature: (JLjava/lang/String;)[B 88 | */ 89 | JNIEXPORT jbyteArray JNICALL Java_com_mlnkv_MLNKV_getBytes 90 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key); 91 | 92 | /* 93 | * Class: com_mlnkv_MLNKV 94 | * Method: getString 95 | * Signature: (JLjava/lang/String;)Ljava/lang/String; 96 | */ 97 | JNIEXPORT jstring JNICALL Java_com_mlnkv_MLNKV_getString 98 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key); 99 | 100 | /* 101 | * Class: com_mlnkv_MLNKV 102 | * Method: getBool 103 | * Signature: (JLjava/lang/String;Z)Z 104 | */ 105 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_getBool 106 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jboolean defaultValue); 107 | 108 | /* 109 | * Class: com_mlnkv_MLNKV 110 | * Method: getInt32 111 | * Signature: (JLjava/lang/String;I)I 112 | */ 113 | JNIEXPORT jint JNICALL Java_com_mlnkv_MLNKV_getInt32 114 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jint defaultValue); 115 | 116 | /* 117 | * Class: com_mlnkv_MLNKV 118 | * Method: getInt64 119 | * Signature: (JLjava/lang/String;J)J 120 | */ 121 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_getInt64 122 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jlong defaultValue); 123 | 124 | /* 125 | * Class: com_mlnkv_MLNKV 126 | * Method: getFloat 127 | * Signature: (JLjava/lang/String;F)F 128 | */ 129 | JNIEXPORT jfloat JNICALL Java_com_mlnkv_MLNKV_getFloat 130 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jfloat defaultValue); 131 | 132 | /* 133 | * Class: com_mlnkv_MLNKV 134 | * Method: getDouble 135 | * Signature: (JLjava/lang/String;D)D 136 | */ 137 | JNIEXPORT jdouble JNICALL Java_com_mlnkv_MLNKV_getDouble 138 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key, jdouble defaultValue); 139 | 140 | /* 141 | * Class: com_mlnkv_MLNKV 142 | * Method: getVauleSize 143 | * Signature: (JLjava/lang/String;)J 144 | */ 145 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_getVauleSize 146 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key); 147 | 148 | /* 149 | * Class: com_mlnkv_MLNKV 150 | * Method: containsKey 151 | * Signature: (JLjava/lang/String;)Z 152 | */ 153 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_containsKey 154 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key); 155 | 156 | /* 157 | * Class: com_mlnkv_MLNKV 158 | * Method: count 159 | * Signature: (J)J 160 | */ 161 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_count 162 | (JNIEnv *env, jobject obj, jlong mlnkv); 163 | 164 | /* 165 | * Class: com_mlnkv_MLNKV 166 | * Method: totalUserdSize 167 | * Signature: (J)J 168 | */ 169 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_totalUsedSize 170 | (JNIEnv *env, jobject obj, jlong mlnkv); 171 | 172 | /* 173 | * Class: com_mlnkv_MLNKV 174 | * Method: fileSize 175 | * Signature: (J)J 176 | */ 177 | JNIEXPORT jlong JNICALL Java_com_mlnkv_MLNKV_fileSize 178 | (JNIEnv *env, jobject obj, jlong mlnkv); 179 | 180 | /* 181 | * Class: com_mlnkv_MLNKV 182 | * Method: filePath 183 | * Signature: (J)Ljava/lang/String; 184 | */ 185 | JNIEXPORT jstring JNICALL Java_com_mlnkv_MLNKV_filePath 186 | (JNIEnv *env, jobject obj, jlong mlnkv); 187 | 188 | /* 189 | * Class: com_mlnkv_MLNKV 190 | * Method: allKeys 191 | * Signature: (J)Ljava/util/List; 192 | */ 193 | JNIEXPORT jobjectArray JNICALL Java_com_mlnkv_MLNKV_allKeys 194 | (JNIEnv *env, jobject obj, jlong mlnkv); 195 | 196 | /* 197 | * Class: com_mlnkv_MLNKV 198 | * Method: remove 199 | * Signature: (JLjava/lang/String;)Z 200 | */ 201 | JNIEXPORT jboolean JNICALL Java_com_mlnkv_MLNKV_remove 202 | (JNIEnv *env, jobject obj, jlong mlnkv, jstring key); 203 | 204 | /* 205 | * Class: com_mlnkv_MLNKV 206 | * Method: clearAll 207 | * Signature: (J)V 208 | */ 209 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_clearAll 210 | (JNIEnv *env, jobject obj, jlong mlnkv); 211 | 212 | /* 213 | * Class: com_mlnkv_MLNKV 214 | * Method: trim 215 | * Signature: (J)V 216 | */ 217 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_trim 218 | (JNIEnv *env, jobject obj, jlong mlnkv); 219 | 220 | /* 221 | * Class: com_mlnkv_MLNKV 222 | * Method: sync 223 | * Signature: (JLjava/lang/Boolean;)V 224 | */ 225 | JNIEXPORT void JNICALL Java_com_mlnkv_MLNKV_sync 226 | (JNIEnv *env, jobject obj, jlong mlnkv, jboolean isSync); 227 | 228 | #ifdef __cplusplus 229 | } 230 | #endif 231 | #endif 232 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/java/com/mlnkv/MLNKVMemoryCache.java: -------------------------------------------------------------------------------- 1 | package com.mlnkv; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import java.lang.ref.WeakReference; 10 | import java.util.HashMap; 11 | 12 | class MLNKVCacheNode { 13 | WeakReference prev; 14 | WeakReference next; 15 | String key; 16 | Object value; 17 | long size; 18 | } 19 | 20 | class MLNKVCache { 21 | private MLNKVCacheNode head; 22 | private MLNKVCacheNode tail; 23 | private HashMap dict; 24 | 25 | public int totalCount() { 26 | return dict.size(); 27 | } 28 | public long totalSize; 29 | public boolean releaseOnMainThread; 30 | 31 | public MLNKVCache() { 32 | dict = new HashMap(); 33 | } 34 | 35 | @Override 36 | protected void finalize() throws Throwable { 37 | dict.clear(); 38 | super.finalize(); 39 | } 40 | 41 | public MLNKVCacheNode nodeForKey(String key) { 42 | return dict.get(key); 43 | } 44 | 45 | public void insertNodeAtHead(MLNKVCacheNode node){ 46 | if (node == null) return; 47 | dict.put(node.key, node); 48 | totalSize += node.size; 49 | if (head != null) { 50 | node.next = new WeakReference(head); 51 | head.prev = new WeakReference(node); 52 | head = node; 53 | }else { 54 | head = tail = node; 55 | } 56 | } 57 | 58 | public void bringNodeToHead(MLNKVCacheNode node) { 59 | if (node == null) return; 60 | if (head == node) return; 61 | if (tail == node) { 62 | tail = node.prev.get(); 63 | tail.next = null; 64 | }else { 65 | node.next.get().prev = node.prev; 66 | node.prev.get().next = node.next; 67 | } 68 | node.next = new WeakReference(head); 69 | node.prev = null; 70 | head.prev = new WeakReference(node); 71 | head = node; 72 | 73 | } 74 | 75 | public void removeNode(MLNKVCacheNode node) { 76 | if (node == null) return; 77 | dict.remove(node.key); 78 | totalSize -= node.size; 79 | if (node.next != null) node.next.get().prev = node.prev; 80 | if (node.prev != null) node.prev.get().next = node.next; 81 | if (head == node) head = (node.next == null)? null: node.next.get(); 82 | if (tail == node) tail = (node.prev == null)? null: node.prev.get(); 83 | 84 | if (releaseOnMainThread) { 85 | if (Looper.getMainLooper() != Looper.myLooper()) { 86 | Message message = new Message(); 87 | message.what = 2; 88 | message.obj = node; 89 | uiHandler.sendMessage(message); 90 | } 91 | } 92 | } 93 | 94 | public MLNKVCacheNode removeTailNode() { 95 | if (tail == null) return null; 96 | MLNKVCacheNode tempTail = tail; 97 | dict.remove(tail.key); 98 | totalSize -= tail.size; 99 | if (head == tail) { 100 | head = tail = null; 101 | }else { 102 | tail = tail.prev.get(); 103 | tail.next = null; 104 | } 105 | if (releaseOnMainThread) { 106 | if (Looper.getMainLooper() != Looper.myLooper()) { 107 | Message message = new Message(); 108 | message.what = 2; 109 | message.obj = tempTail; 110 | uiHandler.sendMessage(message); 111 | } 112 | } 113 | return tempTail; 114 | } 115 | 116 | public void removeAll() { 117 | totalSize = 0; 118 | head = null; 119 | tail = null; 120 | if (dict.size() > 0 && releaseOnMainThread) { 121 | if (Looper.getMainLooper() != Looper.myLooper()) { 122 | Message message = new Message(); 123 | message.what = 1; 124 | message.obj = dict.clone(); 125 | uiHandler.sendMessage(message); 126 | } 127 | } 128 | dict.clear(); 129 | } 130 | 131 | private Handler uiHandler = new Handler(Looper.getMainLooper()) { 132 | @Override 133 | public void handleMessage(@NonNull Message msg) { 134 | super.handleMessage(msg); 135 | switch (msg.what) { 136 | case 1: 137 | HashMap nodes = (HashMap) msg.obj; 138 | nodes.clear(); 139 | msg.obj = null; 140 | break; 141 | case 2: 142 | msg.obj = null; 143 | break; 144 | 145 | default: 146 | break; 147 | } 148 | } 149 | }; 150 | } 151 | 152 | public class MLNKVMemoryCache { 153 | 154 | public String name; 155 | 156 | public int LRUCountLimit; // 10000 if == 0 don't use LRU cache 157 | public int FIFOCountLimit; // 1000 if == 0 don't use FIFO cache 158 | public long memorySizeLimit; // 100M 100 << 20 159 | 160 | public boolean useFIFO; // default YES use FIFO cache 161 | public boolean useLRU; // default YES use LRU cache 162 | 163 | public MLNKVMemoryCache(String name) { 164 | this.name = name; 165 | LRUCountLimit = 10000; 166 | FIFOCountLimit = 1000; 167 | memorySizeLimit = 100 << 20; 168 | releaseOnMainThread = false; 169 | 170 | useFIFO = true; 171 | useLRU = true; 172 | 173 | weakMap = new HashMap<>(); 174 | LRUCache = new MLNKVCache(); 175 | FIFOCache = new MLNKVCache(); 176 | 177 | LRUCache.releaseOnMainThread = releaseOnMainThread; 178 | FIFOCache.releaseOnMainThread = releaseOnMainThread; 179 | } 180 | 181 | @Override 182 | protected void finalize() throws Throwable { 183 | LRUCache.removeAll(); 184 | FIFOCache.removeAll(); 185 | weakMap.clear(); 186 | super.finalize(); 187 | } 188 | 189 | public long getTotoalCount() { 190 | synchronized (this) { 191 | return FIFOCache.totalCount() + LRUCache.totalCount(); 192 | } 193 | } 194 | 195 | public long getTotoalMemorySize() { 196 | synchronized (this) { 197 | return FIFOCache.totalSize + LRUCache.totalSize; 198 | } 199 | } 200 | 201 | public void setReleaseOnMainThread(boolean releaseOnMainThread) { 202 | synchronized (this) { 203 | this.releaseOnMainThread = releaseOnMainThread; 204 | FIFOCache.releaseOnMainThread = releaseOnMainThread; 205 | LRUCache.releaseOnMainThread = releaseOnMainThread; 206 | } 207 | } 208 | 209 | public boolean isReleaseOnMainThread() { 210 | return this.releaseOnMainThread; 211 | } 212 | 213 | public boolean containsObjectForKey(String key) { 214 | if (key == null) return false; 215 | boolean contains = false; 216 | synchronized (this) { 217 | contains = weakMap.containsKey(key) || FIFOCache.nodeForKey(key) != null || LRUCache.nodeForKey(key) != null; 218 | } 219 | return contains; 220 | } 221 | 222 | public Object objectForKey(String key) { 223 | if (key == null) return null; 224 | synchronized (this) { 225 | MLNKVCacheNode node = (useFIFO == true && FIFOCountLimit > 0)? FIFOCache.nodeForKey(key): null; 226 | if (node != null) { 227 | FIFOCache.removeNode(node); 228 | if (useLRU == true && LRUCountLimit > 0) { 229 | LRUCache.insertNodeAtHead(node); 230 | } 231 | }else { 232 | if (useLRU == true && LRUCountLimit > 0) { 233 | node = LRUCache.nodeForKey(key); 234 | LRUCache.bringNodeToHead(node); 235 | } 236 | } 237 | if (useLRU == true && LRUCache.totalCount() > LRUCountLimit) { 238 | LRUCache.removeTailNode(); 239 | } 240 | Object value = null; 241 | if (node != null) { 242 | value = node.value; 243 | }else { 244 | WeakReference reference = weakMap.get(key); 245 | if (reference != null) { 246 | value = reference.get(); 247 | if (value == null) { 248 | weakMap.remove(key); 249 | } 250 | } 251 | } 252 | return value; 253 | } 254 | } 255 | 256 | public void setObject(String key, Object obj, long size) { 257 | if (key == null) return; 258 | if (obj == null) { 259 | removeObjectForKey(key); 260 | return; 261 | } 262 | 263 | synchronized (this) { 264 | weakMap.put(key, new WeakReference(obj)); 265 | MLNKVCacheNode node = (useLRU == true && LRUCountLimit > 0)? LRUCache.nodeForKey(key): null; 266 | if (node != null) { 267 | LRUCache.totalSize = (LRUCache.totalSize < node.size)? 0: (LRUCache.totalSize - node.size); 268 | LRUCache.totalSize += size; 269 | node.value = obj; 270 | node.size = size; 271 | LRUCache.bringNodeToHead(node); 272 | }else { 273 | if (useFIFO == true && FIFOCountLimit > 0) { 274 | node = FIFOCache.nodeForKey(key); 275 | if (node != null) { 276 | FIFOCache.totalSize = (FIFOCache.totalSize < node.size)? 0: (FIFOCache.totalSize - node.size); 277 | FIFOCache.totalSize += size; 278 | node.value = obj; 279 | node.size = size; 280 | FIFOCache.bringNodeToHead(node); 281 | }else { 282 | node = new MLNKVCacheNode(); 283 | node.key = key; 284 | node.value = obj; 285 | node.size = size; 286 | FIFOCache.insertNodeAtHead(node); 287 | } 288 | if (FIFOCache.totalCount() > FIFOCountLimit) { 289 | FIFOCache.removeTailNode(); 290 | } 291 | } 292 | } 293 | while (FIFOCache.totalSize + LRUCache.totalSize > memorySizeLimit) { 294 | if (LRUCache.removeTailNode() == null) { 295 | if (FIFOCache.removeTailNode() == null) { 296 | break; 297 | } 298 | } 299 | } 300 | } 301 | } 302 | 303 | public void setWeakObject(String key, Object obj) { 304 | if (key == null) return; 305 | synchronized (this) { 306 | if (obj == null) { 307 | weakMap.remove(key); 308 | return; 309 | } 310 | weakMap.put(key, new WeakReference(obj)); 311 | } 312 | } 313 | 314 | public void removeObjectForKey(String key) { 315 | if (key == null) return; 316 | synchronized (this) { 317 | if (useFIFO == true && FIFOCountLimit > 0) { 318 | FIFOCache.removeNode(FIFOCache.nodeForKey(key)); 319 | } 320 | if (useLRU == true && LRUCountLimit > 0) { 321 | LRUCache.removeNode(LRUCache.nodeForKey(key)); 322 | } 323 | weakMap.remove(key); 324 | } 325 | } 326 | 327 | public void removeAllObjects() { 328 | synchronized (this) { 329 | FIFOCache.removeAll(); 330 | LRUCache.removeAll(); 331 | weakMap.clear(); 332 | } 333 | } 334 | 335 | // private 336 | private boolean releaseOnMainThread; 337 | private HashMap> weakMap; 338 | private MLNKVCache FIFOCache; 339 | private MLNKVCache LRUCache; 340 | 341 | } 342 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | mlnkv 3 | 4 | -------------------------------------------------------------------------------- /Android-demo/mlnkv/src/test/java/com/example/mlnkv/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mlnkv; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /Android-demo/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':mlnkv' 2 | rootProject.name='AndroidDemo1' 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 陌陌科技 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MLNKV.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint MLNKV.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "MLNKV" 12 | spec.version = "0.0.3" 13 | spec.summary = "MLNKV is a key-value storage framework ." 14 | 15 | spec.description = <<-DESC 16 | MLNKV is a key-value storage framework !! 17 | DESC 18 | 19 | spec.homepage = "https://github.com/momotech/MLNKV" 20 | spec.license = { :type => "MIT", :file => "FILE_LICENSE" } 21 | spec.author = "" 22 | 23 | spec.ios.deployment_target = "9.0" 24 | spec.requires_arc = true 25 | 26 | spec.source = { :git => "https://github.com/momotech/MLNKV.git", :tag => spec.version.to_s } 27 | 28 | spec.source_files = "Source/iOS/*.{h,m,mm}" 29 | spec.public_header_files = "Source/iOS/*.h" 30 | 31 | spec.subspec 'cpp' do |ss| 32 | ss.source_files = "Source/cpp/*.{h,hpp,cpp}" 33 | ss.public_header_files = "Source/cpp/*.{h,hpp}" 34 | end 35 | 36 | spec.ios.frameworks = "UIKit" 37 | 38 | spec.libraries = "z", "c++" 39 | spec.pod_target_xcconfig = { 40 | 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11', 41 | 'CLANG_CXX_LIBRARY' => 'libc++' 42 | } 43 | 44 | end 45 | -------------------------------------------------------------------------------- /MLNKV.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MLNKV.xcodeproj/project.xcworkspace/xcuserdata/sun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/MLNKV.xcodeproj/project.xcworkspace/xcuserdata/sun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MLNKV.xcodeproj/xcshareddata/xcschemes/MLNKV.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /MLNKV.xcodeproj/xcuserdata/sun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | A4A6D6012376757800127295 8 | 9 | primary 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MLNKV.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MLNKV.xcworkspace/xcuserdata/sun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/MLNKV.xcworkspace/xcuserdata/sun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MLNKV/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /MLNKV/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "MLNKV.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | 21 | // [[MLNKV defaultMLNKV] setKVString:@"试数据" forKey:@"key1"]; 22 | // NSLog(@"%@", [[MLNKV defaultMLNKV] getKVStringForKey:@"key1" defaultValue:nil]); 23 | // 24 | // [[MLNKV defaultMLNKV] setKVString:@"测试你好(* ̄︶ ̄)😊" forKey:@"key2"]; 25 | // NSLog(@"%@", [[MLNKV defaultMLNKV] getKVStringForKey:@"key2" defaultValue:nil]); 26 | // 27 | MLNKV *mlnkv = [MLNKV defaultMLNKV]; 28 | // 29 | // // set 30 | // [mlnkv setKVString:@"value" forKey:@"key1"]; 31 | // [mlnkv setKVBool:YES forKey:@"key2"]; 32 | // [mlnkv setKVInt32:66666 forKey:@"key3"]; 33 | // [mlnkv setKVInt64:88888888 forKey:@"key4"]; 34 | // [mlnkv setKVFloat:66.666 forKey:@"key5"]; 35 | // [mlnkv setKVDouble:8888888.888 forKey:@"key6"]; 36 | // [mlnkv setKVObject:@{@"key":@"value"} forKey:@"key7"]; 37 | // 38 | // // get 39 | // int value = [mlnkv getKVInt32ForKey:@"key3"]; 40 | // ... 41 | // ... 42 | // 43 | // // obj 44 | // [mlnkv setKVObject:obj forKey:@"key" archiveBlock:^NSData * _Nullable(id _Nonnull obj) { 45 | // // ...archive 46 | // }]; 47 | // [mlnkv getKVObjectForKey:@"key" ofClass:clz unarchiveBlock:^id _Nullable(NSData * _Nonnull data) { 48 | // // ...unarchive 49 | // }]; 50 | // 51 | // NSLog(@"---%f", [mlnkv getKVFloatForKey:@"key5"]); 52 | // NSLog(@"---%f", [mlnkv getKVDoubleForKey:@"key6"]); 53 | 54 | [mlnkv setKVString:@"123" forKey:@"keysss"]; 55 | [mlnkv removeValueForKey:@"keysss"]; 56 | [mlnkv setKVString:@"123" forKey:@"keysss"]; 57 | 58 | return YES; 59 | } 60 | 61 | 62 | - (void)applicationWillResignActive:(UIApplication *)application { 63 | // 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. 64 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 65 | } 66 | 67 | 68 | - (void)applicationDidEnterBackground:(UIApplication *)application { 69 | // 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. 70 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 71 | 72 | [application beginBackgroundTaskWithExpirationHandler:^{ 73 | 74 | }]; 75 | } 76 | 77 | 78 | - (void)applicationWillEnterForeground:(UIApplication *)application { 79 | // 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. 80 | } 81 | 82 | 83 | - (void)applicationDidBecomeActive:(UIApplication *)application { 84 | // 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. 85 | } 86 | 87 | 88 | - (void)applicationWillTerminate:(UIApplication *)application { 89 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 90 | } 91 | 92 | 93 | 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /MLNKV/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /MLNKV/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MLNKV/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MLNKV/BaseViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.h 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface BaseViewController : UIViewController 13 | 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /MLNKV/BaseViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.m 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #import "BaseViewController.h" 9 | #import "MLNKV.h" 10 | #import 11 | #import 12 | 13 | 14 | @interface BaseViewController () 15 | @property (weak, nonatomic) IBOutlet UISegmentedControl *SoureTypeSegmented; 16 | @property (weak, nonatomic) IBOutlet UITextField *numTextField; 17 | @property (weak, nonatomic) IBOutlet UITextField *lengthTextField; 18 | @property (weak, nonatomic) IBOutlet UISegmentedControl *typeSegmented; 19 | 20 | @property (weak, nonatomic) IBOutlet UILabel *setActionTimeLabel; 21 | @property (weak, nonatomic) IBOutlet UILabel *getActionTimeLabel; 22 | @property (weak, nonatomic) IBOutlet UILabel *removeActionTimeLabel; 23 | @property (weak, nonatomic) IBOutlet UILabel *clearActionTimeLabel; 24 | @property (weak, nonatomic) IBOutlet UILabel *trimTimeLabel; 25 | @property (weak, nonatomic) IBOutlet UILabel *syncActionTimeLabel; 26 | 27 | @property (weak, nonatomic) IBOutlet UILabel *setupTimeLabel; 28 | @property (weak, nonatomic) IBOutlet UILabel *saveCountLabel; 29 | 30 | @property (nonatomic, strong) NSArray *preArray; 31 | 32 | @property (nonatomic, strong) NSMutableArray *keysArray; 33 | @property (nonatomic, strong) NSMutableArray *valuesArray; 34 | @property (nonatomic, strong) NSMutableArray *hasSetKeys; 35 | 36 | @property (nonatomic, strong) MLNKV *mlnkv; 37 | 38 | @end 39 | 40 | @implementation BaseViewController 41 | 42 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 43 | [super touchesEnded:touches withEvent:event]; 44 | 45 | [self.view endEditing:YES]; 46 | } 47 | 48 | - (void)viewDidLoad { 49 | [super viewDidLoad]; 50 | // Do any additional setup after loading the view. 51 | _preArray = @[@"mlnkv"]; 52 | 53 | _keysArray = NSMutableArray.new; 54 | _valuesArray = NSMutableArray.new; 55 | _hasSetKeys = NSMutableArray.new; 56 | 57 | [self mlnkv]; 58 | } 59 | 60 | - (double)usedMemory { 61 | int64_t memoryUsageInByte = 0; 62 | task_vm_info_data_t vmInfo; 63 | mach_msg_type_number_t count = TASK_VM_INFO_COUNT; 64 | kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count); 65 | if(kernelReturn == KERN_SUCCESS) { 66 | memoryUsageInByte = (int64_t) vmInfo.phys_footprint; 67 | // NSLog(@"Memory in use (in bytes): %lld", memoryUsageInByte); 68 | } else { 69 | // NSLog(@"Error with task_info(): %s", mach_error_string(kernelReturn)); 70 | } 71 | return memoryUsageInByte / 1024.0 / 1024.0; 72 | } 73 | 74 | - (MLNKV *)mlnkv { 75 | if (!_mlnkv) { 76 | NSLog(@"initmlnkv-memory-b:%f", [self usedMemory]); 77 | CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent(); 78 | _mlnkv = [MLNKV defaultMLNKV]; 79 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - begin; 80 | self.setupTimeLabel.text = [NSString stringWithFormat:@"启动耗时:%f ms", space * 1000]; 81 | NSLog(@">>启动耗时:%f", space); 82 | NSLog(@"initmlnkv-memory-e:%f", [self usedMemory]); 83 | } 84 | return _mlnkv; 85 | } 86 | 87 | 88 | #pragma mark - action 89 | 90 | - (IBAction)SourceSegmentedAction:(UISegmentedControl *)sender { 91 | [self valueTypeSegmentedAction:nil]; 92 | if (sender.selectedSegmentIndex == 0) { 93 | [self mlnkv]; 94 | } 95 | } 96 | 97 | - (IBAction)valueTypeSegmentedAction:(UISegmentedControl *)sender { 98 | self.setActionTimeLabel.text = @"耗时:"; 99 | self.getActionTimeLabel.text = @"耗时:"; 100 | self.removeActionTimeLabel.text = @"耗时:"; 101 | self.clearActionTimeLabel.text = @"耗时:"; 102 | self.trimTimeLabel.text = @"耗时:"; 103 | self.syncActionTimeLabel.text = @"耗时:"; 104 | self.setupTimeLabel.text = @"启动耗时";; 105 | self.saveCountLabel.text = @"数据存储量"; 106 | } 107 | - (IBAction)reloadDataAction:(UIButton *)sender { 108 | @autoreleasepool { 109 | [_valuesArray removeAllObjects]; 110 | [_keysArray removeAllObjects]; 111 | } 112 | 113 | [self.view endEditing:YES]; 114 | 115 | NSUInteger count = self.numTextField.text.integerValue ? : 100000; 116 | NSUInteger length = self.lengthTextField.text.integerValue? : 10; 117 | NSInteger selected = self.typeSegmented.selectedSegmentIndex; 118 | [sender setTitle:@"生成中..." forState:UIControlStateDisabled]; 119 | sender.enabled = NO; 120 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 121 | NSLog(@"reload-memory-b:%f", [self usedMemory]); 122 | @autoreleasepool { 123 | for (int i = 0; i < count; i++) { 124 | if (selected == 0) { 125 | int len = arc4random() % length +1; 126 | NSMutableString *st = [NSMutableString stringWithCapacity:len]; 127 | for (int s = 0; s < length; s++) { 128 | [st appendFormat:@"%c", arc4random()/30 + 'A']; 129 | } 130 | if (st.length <= 0) { 131 | continue; 132 | } 133 | [self.valuesArray addObject:st]; 134 | }else if (selected == 1) { 135 | [self.valuesArray addObject:@(arc4random() % length +1)]; 136 | } 137 | 138 | // int key = arc4random() % 100000; 139 | [self.keysArray addObject:[NSString stringWithFormat:@"p%d", i]]; 140 | } 141 | } 142 | NSLog(@"reload-memory-e:%f", [self usedMemory]); 143 | dispatch_async(dispatch_get_main_queue(), ^{ 144 | sender.enabled = YES; 145 | }); 146 | }); 147 | } 148 | 149 | 150 | - (IBAction)setValueAction:(id)sender { 151 | NSString *cmd = [self.preArray objectAtIndex:0]; 152 | cmd = [cmd stringByAppendingString:@"SetValueAction"]; 153 | [self performSelector:NSSelectorFromString(cmd)]; 154 | } 155 | 156 | - (IBAction)getValueAction:(id)sender { 157 | NSString *cmd = [self.preArray objectAtIndex:0]; 158 | cmd = [cmd stringByAppendingString:@"GetValueAction"]; 159 | [self performSelector:NSSelectorFromString(cmd)]; 160 | } 161 | 162 | - (IBAction)removeAction:(id)sender { 163 | NSString *cmd = [self.preArray objectAtIndex:0]; 164 | cmd = [cmd stringByAppendingString:@"RemoveAction"]; 165 | [self performSelector:NSSelectorFromString(cmd)]; 166 | } 167 | 168 | - (IBAction)clearAction:(id)sender { 169 | NSString *cmd = [self.preArray objectAtIndex:0]; 170 | cmd = [cmd stringByAppendingString:@"ClearAction"]; 171 | [self performSelector:NSSelectorFromString(cmd)]; 172 | } 173 | 174 | - (IBAction)trimAction:(id)sender { 175 | NSString *cmd = [self.preArray objectAtIndex:0]; 176 | cmd = [cmd stringByAppendingString:@"TrimAction"]; 177 | [self performSelector:NSSelectorFromString(cmd)]; 178 | } 179 | 180 | - (IBAction)syncAction:(id)sender { 181 | NSString *cmd = [self.preArray objectAtIndex:0]; 182 | cmd = [cmd stringByAppendingString:@"SyncAction"]; 183 | [self performSelector:NSSelectorFromString(cmd)]; 184 | } 185 | 186 | #pragma mark - MLNKV 187 | - (void)mlnkvSetValueAction { 188 | NSLog(@"setmlnkv-memory-b:%f", [self usedMemory]); 189 | @autoreleasepool { 190 | int index = 0; 191 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 192 | if (self.typeSegmented.selectedSegmentIndex == 0) { 193 | for (NSString *key in _keysArray) { 194 | NSString *value = _valuesArray[index]; 195 | index ++; 196 | [_mlnkv setKVString:value forKey:key]; 197 | } 198 | }else if (self.typeSegmented.selectedSegmentIndex == 1) { 199 | for (NSString *key in _keysArray) { 200 | NSNumber *value = _valuesArray[index]; 201 | index ++; 202 | [_mlnkv setKVInt32:value.intValue forKey:key]; 203 | } 204 | } 205 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 206 | self.setActionTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 207 | NSLog(@">>写入时间:%f", space); 208 | self.saveCountLabel.text = [NSString stringWithFormat:@"数据存储量:%ld", [_mlnkv count]]; 209 | } 210 | NSLog(@"setmlnkv-memory-e:%f", [self usedMemory]); 211 | NSLog(@"--mem count:%zi", self.mlnkv.memoryCache.totalCount); 212 | } 213 | 214 | - (void)mlnkvGetValueAction { 215 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 216 | if (self.typeSegmented.selectedSegmentIndex == 0) { 217 | for (NSString *key in _keysArray) { 218 | [_mlnkv getKVStringForKey:key]; 219 | // NSLog(@"%@", [_mlnkv getKVStringForKey:key]); 220 | } 221 | }else if (self.typeSegmented.selectedSegmentIndex == 1){ 222 | for (NSString *key in _keysArray) { 223 | [_mlnkv getKVInt32ForKey:key]; 224 | // NSLog(@"%@", [_mlnkv getKVStringForKey:key]); 225 | } 226 | } 227 | 228 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 229 | self.getActionTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 230 | NSLog(@">>读取时间:%f", space); 231 | 232 | } 233 | 234 | - (void)mlnkvRemoveAction { 235 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 236 | [_mlnkv removeValuesForKeys:_keysArray]; 237 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 238 | self.removeActionTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 239 | NSLog(@">>删除时间:%f", space); 240 | } 241 | 242 | - (void)mlnkvClearAction { 243 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 244 | [_mlnkv clearAll]; 245 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 246 | self.clearActionTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 247 | NSLog(@">>清空时间:%f", space); 248 | } 249 | 250 | - (void)mlnkvTrimAction { 251 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 252 | [_mlnkv trim]; 253 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 254 | self.trimTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 255 | NSLog(@">>整理时间:%f", space); 256 | } 257 | 258 | - (void)mlnkvSyncAction { 259 | CFAbsoluteTime beginTime = CFAbsoluteTimeGetCurrent(); 260 | [_mlnkv sync]; 261 | CFAbsoluteTime space = CFAbsoluteTimeGetCurrent() - beginTime; 262 | self.syncActionTimeLabel.text = [NSString stringWithFormat:@"耗时:%.4f ms", space * 1000]; 263 | NSLog(@">>同步时间:%f", space); 264 | } 265 | 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /MLNKV/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | UILaunchStoryboardName 22 | LaunchScreen 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MLNKV/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'MLNKV' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | #use_frameworks! 7 | 8 | # Pods for MLNKV 9 | 10 | pod "MLNKV", :path => './MLNKV.podspec' 11 | #pod "MLNKV" 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MLNKV (0.0.3): 3 | - MLNKV/cpp (= 0.0.3) 4 | - MLNKV/cpp (0.0.3) 5 | 6 | DEPENDENCIES: 7 | - MLNKV (from `./MLNKV.podspec`) 8 | 9 | EXTERNAL SOURCES: 10 | MLNKV: 11 | :path: "./MLNKV.podspec" 12 | 13 | SPEC CHECKSUMS: 14 | MLNKV: bdab9e05b3e408151188a28e87d529162cdc2d93 15 | 16 | PODFILE CHECKSUM: 4068777ec4b8e48b9bc30e3f6e41f68bed99a2cb 17 | 18 | COCOAPODS: 1.8.4 19 | -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKV.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/iOS/MLNKV.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVBase.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVBase.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVLog.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVLog.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVMemoryCache.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/iOS/MLNKVMemoryCache.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVType.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVType.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVUtility.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVUtility.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MLNKV/MLNKVValueInfo.hpp: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVValueInfo.hpp -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKV.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/iOS/MLNKV.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVBase.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVBase.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVLog.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVLog.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVMemoryCache.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/iOS/MLNKVMemoryCache.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVType.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVType.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVUtility.h: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVUtility.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MLNKV/MLNKVValueInfo.hpp: -------------------------------------------------------------------------------- 1 | ../../../../Source/cpp/MLNKVValueInfo.hpp -------------------------------------------------------------------------------- /Pods/Local Podspecs/MLNKV.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MLNKV", 3 | "version": "0.0.3", 4 | "summary": "MLNKV is a key-value storage framework .", 5 | "description": "MLNKV is a key-value storage framework !!", 6 | "homepage": "https://github.com/momotech/MLNKV", 7 | "license": { 8 | "type": "MIT", 9 | "file": "FILE_LICENSE" 10 | }, 11 | "authors": "", 12 | "platforms": { 13 | "ios": "9.0" 14 | }, 15 | "requires_arc": true, 16 | "source": { 17 | "git": "https://github.com/momotech/MLNKV.git", 18 | "tag": "0.0.3" 19 | }, 20 | "source_files": "Source/iOS/*.{h,m,mm}", 21 | "public_header_files": "Source/iOS/*.h", 22 | "ios": { 23 | "frameworks": "UIKit" 24 | }, 25 | "libraries": [ 26 | "z", 27 | "c++" 28 | ], 29 | "pod_target_xcconfig": { 30 | "CLANG_CXX_LANGUAGE_STANDARD": "c++11", 31 | "CLANG_CXX_LIBRARY": "libc++" 32 | }, 33 | "subspecs": [ 34 | { 35 | "name": "cpp", 36 | "source_files": "Source/cpp/*.{h,hpp,cpp}", 37 | "public_header_files": "Source/cpp/*.{h,hpp}" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /Pods/MLNKV/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 陌陌科技 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pods/MLNKV/README.md: -------------------------------------------------------------------------------- 1 | MLNKV 2 | ======= 3 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/momotech/MLNKV/blob/master/LICENSE) 4 | 5 | MLNKV是基于mmap实现的高性能、内存占用低、跨平台(支持iOS与Android)的Key-Value组件。 6 |
7 | 8 | # 性能对比 9 | `测试机型:iPhone Xs 13.1.3 64G`
10 | ![img](https://github.com/momotech/MLNKV/blob/master/img/setString.png)
11 | ![img](https://github.com/momotech/MLNKV/blob/master/img/setInt.png)
12 | ![img](https://github.com/momotech/MLNKV/blob/master/img/getString.png)
13 | ===== 14 | 内存占用对比:
15 | ![img](https://github.com/momotech/MLNKV/blob/master/img/memory.png) 16 | 17 | 18 | # 用法 19 | ### iOS 基本用法
20 | `pod 'MLNKV'` 21 | ``` 22 | 23 | // init 24 | MLNKV *mlnkv = [MLNKV defaultMLNKV]; 25 | // NSString *path = [MLNKVDEFAULTPATH stringByAppendingPathComponent:@".test"]; 26 | // MLNKV *mlnkv = [MLNKV mlnkvWithPath:path]; 27 | 28 | // set 29 | [mlnkv setKVString:@"value" forKey:@"key1"]; 30 | [mlnkv setKVBool:YES forKey:@"key2"]; 31 | [mlnkv setKVInt32:66666 forKey:@"key3"]; 32 | [mlnkv setKVInt64:88888888 forKey:@"key4"]; 33 | [mlnkv setKVFloat:66.666 forKey:@"key5"]; 34 | [mlnkv setKVDouble:8888888.888 forKey:@"key6"]; 35 | [mlnkv setKVObject:@{@"key":@"value"} forKey:@"key7"]; 36 | [mlnkv setKVData:data forKey:@"key8"]; 37 | 38 | // get 39 | int value = [mlnkv getKVInt32ForKey:@"key3"]; 40 | ... 41 | ... 42 | 43 | // obj 自己实现序列化 or 使用NSKeyedArchiver 44 | [mlnkv setKVObject:obj forKey:@"key" archiveBlock:^NSData * _Nullable(id _Nonnull obj) { 45 | // ...archive 46 | }]; 47 | [mlnkv getKVObjectForKey:@"key" ofClass:clz unarchiveBlock:^id _Nullable(NSData * _Nonnull data) { 48 | // ...unarchive 49 | }]; 50 | 51 | ``` 52 | ### Android 基本用法
53 | `maven {url "https://dl.bintray.com/sunzt8801/MLNKV"}`
54 | ` implementation "com.mlnkv:mlnkv:0.0.3" ` 55 | ``` 56 | 57 | // must call this in MainActivity 58 | MLNKV.initializeBasePath(this); 59 | 60 | // init 61 | MLNKV mlnkv = MLNKV.defaultMLNKV(); 62 | // String path = MLNKV.basPath() + "/.test"; 63 | // MLNKV mlnkv = new MLNKV(path); 64 | 65 | // set 66 | mlnkv.setBool(true, "key1"); 67 | mlnkv.setInt32(1, "key2"); 68 | mlnkv.setInt64(88888888, "key3"); 69 | mlnkv.setDouble(8888.888, "key4"); 70 | mlnkv.setString("value", "key5"); 71 | mlnkv.setBytes(bytes, "key6"); 72 | 73 | // get 74 | boolean value = mlnkv.getBool("key1"); 75 | ... 76 | ... 77 | 78 | // obj 使用java Serializable 79 | mlnkv.setObject(obj, "key"); 80 | mlnkv.getObject(obj, clz); 81 | 82 | ``` 83 | 84 | # 许可证 85 | MLNKV 使用 MIT 许可证,详情见 LICENSE 文件。 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVBase.hpp 3 | // MLNKV 4 | // 5 | // 6 | // Copyright © 2019 . All rights reserved. 7 | // 8 | 9 | #ifndef MLNKVManager_hpp 10 | #define MLNKVManager_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "MLNKVType.h" 20 | #include "MLNKVValueInfo.hpp" 21 | 22 | 23 | typedef enum : uint8_t { 24 | MLNKVReusedAvailableStatusNone = 0, 25 | MLNKVReusedAvailableStatusReused = 1, 26 | MLNKVReusedAvailableStatusAppend = 2, 27 | } MLNKVReusedAvailableStatus; 28 | 29 | class MLNKVBase { 30 | char *filePath; 31 | size_t fileSize; 32 | size_t usedSize; 33 | size_t unavailabledSize; 34 | int file; 35 | char *filemmap; 36 | std::unordered_map keysMap; 37 | std::unordered_map > unavailablesMap; 38 | 39 | std::mutex mutex; 40 | 41 | bool loadDataFromFile(bool openFile = true); 42 | bool isFileValid(); 43 | bool ensureMemorySize(size_t newSize); 44 | bool reuseMemoryOffset(size_t size, size_t &offset); 45 | bool sortUnavailableMemory(bool force); 46 | void markUnAvailable(MLNKVValueInfo &valueInfo); 47 | 48 | bool writeUInt32(uint32_t value, MLNKVValueType valueType, const std::string &key); 49 | bool writeUInt64(uint64_t value, MLNKVValueType valueType, const std::string &key); 50 | MLNKVReusedAvailableStatus getAvailableOffset(size_t dataSize, size_t &offset); 51 | 52 | bool removeValue(const std::string &key); 53 | bool removeValue(const std::string &key, std::unordered_map::iterator &valueItera, bool returnValue); 54 | bool clear(); 55 | 56 | public: 57 | MLNKVBase(const std::string &path); 58 | ~MLNKVBase(); 59 | 60 | // write bytes 61 | bool writeBytes(const void *value, const size_t size, MLNKVValueType valueType, const std::string &key); 62 | // read bytes 63 | bool readValueBytes(const std::string &key, MLNKVValueType &valueType, void* &value, size_t &size); 64 | 65 | // set 66 | 67 | bool setBytes(const void *value, const size_t size, const std::string &key); 68 | 69 | bool setString(const std::string &value, const std::string &key); 70 | 71 | bool setBool(bool value, const std::string &key); 72 | 73 | bool setInt32(int32_t value, const std::string &key); 74 | 75 | bool setInt64(int64_t value, const std::string &key); 76 | 77 | bool setFloat(float value, const std::string &key); 78 | 79 | bool setDouble(double value, const std::string &key); 80 | 81 | // get 82 | 83 | bool getBytesForKey(const std::string &key, void* &value, size_t &size); 84 | 85 | bool getStringForKey(const std::string &key, std::string &result); 86 | 87 | bool getBoolForKey(const std::string &key, bool defaultValue = false); 88 | 89 | int32_t getInt32ForKey(const std::string &key, int32_t defaultValue = 0); 90 | 91 | int64_t getInt64ForKey(const std::string &key, int64_t defaultValue = 0); 92 | 93 | float getFloatForKey(const std::string &key, float defaultValue = 0); 94 | 95 | double getDoubleForKey(const std::string &key, double defaultValue = 0); 96 | 97 | 98 | // size 99 | size_t getValueSizeForKey(const std::string &key, MLNKVValueType &valueType); 100 | 101 | bool containsKey(const std::string &key); 102 | 103 | size_t count(); 104 | 105 | size_t totalUsedSize(); 106 | 107 | std::vector allKeys(); 108 | 109 | 110 | // remove 111 | bool remove(const std::string &key); 112 | void clearAll(); 113 | void trim(); 114 | 115 | // sync 116 | void sync(bool isSync); 117 | 118 | // 119 | std::string getFilePath(); 120 | size_t getFileSize(); 121 | 122 | }; 123 | 124 | #endif /* MLNKVManager_hpp */ 125 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVLog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MILKVLog.c 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #include "MLNKVLog.h" 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | static MLNKVLogCallbackHandler *kMLNKVLogCallbackHandler; 15 | void MLNKVSetLogCallbackHandler(MLNKVLogCallbackHandler *callbackHandler) { 16 | kMLNKVLogCallbackHandler = callbackHandler; 17 | } 18 | MLNKVLogCallbackHandler *MLNKVGetLogCallbackHandler(void) { 19 | return kMLNKVLogCallbackHandler; 20 | } 21 | 22 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...) { 23 | 24 | if (kMLNKVLogCallbackHandler) { 25 | va_list args; 26 | va_start(args, format); 27 | kMLNKVLogCallbackHandler(level, file, func, line, format, args); 28 | va_end(args); 29 | return; 30 | } 31 | 32 | std::string message; 33 | char buffer[16]; 34 | 35 | va_list args; 36 | va_start(args, format); 37 | auto length = vsnprintf(buffer, sizeof(buffer), format, args); 38 | va_end(args); 39 | 40 | if (length < 0) { // something wrong 41 | message = {}; 42 | } else if (length < sizeof(buffer)) { 43 | message = std::string(buffer, static_cast(length)); 44 | } else { 45 | message.resize(static_cast(length), '\0'); 46 | va_start(args, format); 47 | vsnprintf(const_cast(message.data()), static_cast(length) + 1, 48 | format, args); 49 | va_end(args); 50 | } 51 | 52 | time_t t; 53 | struct tm *timeinfo; 54 | time(&t); 55 | timeinfo = localtime(&t); 56 | 57 | char timeStr[128]; 58 | strftime(timeStr, sizeof(timeStr), "%F %T", timeinfo); 59 | 60 | struct timeval tv; 61 | gettimeofday(&tv, NULL); 62 | 63 | printf("%s.%d <%s:%s::%d> [%c] %s \n", timeStr, (int)tv.tv_usec, file, func, line, (level == MLNLogDebug)?'D':'E', message.c_str()); 64 | } 65 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVLog_h.h 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVLog_h 9 | #define MLNKVLog_h 10 | 11 | #include 12 | #include 13 | 14 | typedef enum { 15 | MLNLogDebug = 0, 16 | MLNLogError = 1, 17 | } MLNLogLevel; 18 | 19 | typedef void MLNKVLogCallbackHandler(MLNLogLevel level, const char *file, const char *func, int line, const char *format, va_list args); 20 | 21 | extern void MLNKVSetLogCallbackHandler(MLNKVLogCallbackHandler *callbackHandler); 22 | extern MLNKVLogCallbackHandler *MLNKVGetLogCallbackHandler(void); 23 | 24 | //#define MLNDebug 25 | 26 | #define __mlnkv_filename__ (strrchr(__FILE__, '/') + 1) 27 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...); 28 | 29 | #ifdef MLNDebug 30 | 31 | #define MLNKVLog(format, ...) MLNKVLogWithLevel(MLNLogDebug, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 32 | 33 | #else 34 | 35 | #define MLNKVLog(format, ...) 36 | 37 | #endif 38 | 39 | #define MLNKVError(format, ...) MLNKVLogWithLevel(MLNLogError, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 40 | 41 | 42 | 43 | #endif /* MLNKVLog_h */ 44 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVType.h 3 | // 4 | // 5 | // 6 | 7 | #ifndef MLNKVType_h 8 | #define MLNKVType_h 9 | 10 | typedef enum : uint8_t { 11 | MLNKVValueType_None = 0, 12 | MLNKVValueType_Used = 1, 13 | MLNKVValueType_Bool = 2, 14 | MLNKVValueType_Float = 3, 15 | MLNKVValueType_Int32 = 4, 16 | MLNKVValueType_Double = 5, 17 | MLNKVValueType_Int64 = 6, 18 | MLNKVValueType_String = 7, 19 | MLNKVValueType_Bytes = 8, 20 | } MLNKVValueType; 21 | 22 | 23 | #endif /* MLNKVType_h */ 24 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVUtility.hpp 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVUtility_hpp 9 | #define MLNKVUtility_hpp 10 | 11 | #include 12 | #include 13 | 14 | 15 | template 16 | union MLNKVConverter { 17 | static_assert(sizeof(T) == sizeof(P), "size not match"); 18 | T first; 19 | P second; 20 | }; 21 | 22 | static inline uint64_t MLNKVInt64ToUInt64(int64_t v) { 23 | MLNKVConverter converter; 24 | converter.first = v; 25 | return converter.second; 26 | } 27 | 28 | static inline int64_t MLNKVUInt64ToInt64(uint64_t v) { 29 | MLNKVConverter converter; 30 | converter.second = v; 31 | return converter.first; 32 | } 33 | 34 | static inline uint32_t MLNKVInt32ToUInt32(int32_t v) { 35 | MLNKVConverter converter; 36 | converter.first = v; 37 | return converter.second; 38 | } 39 | 40 | static inline int32_t MLNKVUInt32ToInt32(uint32_t v) { 41 | MLNKVConverter converter; 42 | converter.second = v; 43 | return converter.first; 44 | } 45 | 46 | static inline uint32_t MLNKVFloatToUInt32(float v){ 47 | MLNKVConverter converter; 48 | converter.first = v; 49 | return converter.second; 50 | } 51 | 52 | static inline float MLNKVUInt32ToFloat(uint32_t v) { 53 | MLNKVConverter converter; 54 | converter.first = v; 55 | return converter.second; 56 | } 57 | 58 | static inline uint64_t MLNKVDoubleToUInt64(double v) { 59 | MLNKVConverter converter; 60 | converter.first = v; 61 | return converter.second; 62 | } 63 | 64 | static inline double MLNKVUInt64ToDouble(uint64_t v) { 65 | MLNKVConverter converter; 66 | converter.first = v; 67 | return converter.second; 68 | } 69 | 70 | 71 | static inline uint8_t MLNKVRawVarUInt32Size(uint32_t value) { 72 | if ((value & (0xffffffff << 8)) == 0) { 73 | return 1; 74 | } else if ((value & (0xffffffff << 16)) == 0) { 75 | return 2; 76 | } else if ((value & (0xffffffff << 24)) == 0) { 77 | return 3; 78 | } 79 | return 4; 80 | } 81 | 82 | static inline uint8_t MLNKVRawVarUInt64Size(uint64_t value) { 83 | if ((value & (0xffffffffffffffffL << 8)) == 0) { 84 | return 1; 85 | } else if ((value & (0xffffffffffffffffL << 16)) == 0) { 86 | return 2; 87 | } else if ((value & (0xffffffffffffffffL << 24)) == 0) { 88 | return 3; 89 | } else if ((value & (0xffffffffffffffffL << 32)) == 0) { 90 | return 4; 91 | } else if ((value & (0xffffffffffffffffL << 40)) == 0) { 92 | return 5; 93 | } else if ((value & (0xffffffffffffffffL << 48)) == 0) { 94 | return 6; 95 | } else if ((value & (0xffffffffffffffffL << 56)) == 0) { 96 | return 7; 97 | } 98 | return 8; 99 | } 100 | 101 | //constexpr int16_t MLNKVBoolSize(bool value) { 102 | // return 1; 103 | //} 104 | 105 | static const uint8_t MLNKV32Size = 4; 106 | 107 | static const uint8_t MLNKV64Size = 8; 108 | // 109 | ////// 判断机器模式 大小端 110 | /////** 111 | //// 1) Little-Endian:就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 112 | //// 2) Big-Endian:就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 113 | //// */ 114 | //static inline bool MLNKVIsLittleEdian() { 115 | // union { 116 | // unsigned int a; 117 | // unsigned char b; 118 | // } c; 119 | // c.a = 1; 120 | // return (c.b == 1); 121 | //} 122 | 123 | static inline uint32_t MLNKVReadUInt32(uint8_t* ptr, uint8_t size) { 124 | uint32_t value = 0; 125 | for (uint8_t i = 0; i < size; i++) { 126 | value |= (uint32_t)(ptr[i] & 0xFF) << (8 * i); 127 | } 128 | return value; 129 | } 130 | 131 | static inline uint64_t MLNKVReadUInt64(uint8_t* ptr, uint8_t size) { 132 | uint64_t value = 0; 133 | for (uint8_t i = 0; i < size; i++) { 134 | value |= (uint64_t)(ptr[i] & 0xFF) << (8 * i); 135 | } 136 | return value; 137 | } 138 | 139 | static inline bool MLNKVWriteUInt32(uint8_t* ptr, uint32_t value, uint8_t size) { 140 | for (uint8_t i = 0; i < size; i++) { 141 | ptr[i] = (uint8_t)(value & 0xFF); 142 | value = value >> 8; 143 | } 144 | return true; 145 | } 146 | 147 | static inline bool MLNKVWriteUInt64(uint8_t* ptr, uint64_t value, uint8_t size) { 148 | for (uint8_t i = 0; i < size; i++) { 149 | ptr[i] = (uint8_t)(value & 0xFF); 150 | value = value >> 8; 151 | } 152 | return true; 153 | } 154 | 155 | 156 | #endif /* MLNKVUtility_hpp */ 157 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/cpp/MLNKVValueInfo.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVValueInfo.hpp 3 | // MLNKV 4 | // 5 | // 6 | 7 | #ifndef MLNKVValueInfo_hpp 8 | #define MLNKVValueInfo_hpp 9 | 10 | #include 11 | #include "MLNKVType.h" 12 | 13 | 14 | class MLNKVValueInfo { 15 | 16 | public: 17 | 18 | MLNKVValueType type; 19 | size_t offset; 20 | uint8_t extSize; 21 | uint32_t keySize; 22 | size_t valueSize; 23 | 24 | MLNKVValueInfo(MLNKVValueType type = MLNKVValueType_None): type(type) {} 25 | 26 | MLNKVValueInfo(MLNKVValueInfo &&other)noexcept : 27 | type(other.type), 28 | offset(other.offset), 29 | extSize(other.extSize), 30 | keySize(other.keySize), 31 | valueSize(other.valueSize) { 32 | other.type = MLNKVValueType_None; 33 | other.offset = 0; 34 | other.extSize = 0; 35 | other.keySize = 0; 36 | other.valueSize = 0; 37 | } 38 | MLNKVValueInfo &operator=(MLNKVValueInfo &&other) noexcept { 39 | std::swap(type, other.type); 40 | std::swap(offset, other.offset); 41 | std::swap(extSize, other.extSize); 42 | std::swap(keySize, other.keySize); 43 | std::swap(valueSize, other.valueSize); 44 | return *this; 45 | } 46 | 47 | ~MLNKVValueInfo() {} 48 | 49 | private: 50 | MLNKVValueInfo(const MLNKVValueInfo &other) = delete; 51 | MLNKVValueInfo &operator=(const MLNKVValueInfo &other) = delete; 52 | 53 | }; 54 | 55 | #endif /* MLNKVValueInfo_hpp */ 56 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/iOS/MLNKV.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKV.h 3 | // MLNKV 4 | // 5 | // 6 | // 7 | // Copyright © 2019 All rights reserved. 8 | // 9 | 10 | #import 11 | #import "MLNKVMemoryCache.h" 12 | #import "MLNKVType.h" 13 | 14 | #define MLNKVDEFAULTPATH [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@".mlnkv"] 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | typedef NSData *_Nullable(^MLNKVCustomArchiveBlock)(id obj); 19 | typedef id _Nullable(^MLNKVCustomUnarchiveBlock)(NSData *data); 20 | 21 | @class MLNKVMemoryCache; 22 | 23 | @interface MLNKV : NSObject 24 | 25 | + (instancetype)defaultMLNKV; 26 | 27 | + (instancetype)mlnkvWithPath:(NSString *)path; 28 | 29 | - (instancetype)initMLNKVWithPath:(NSString *)path; 30 | 31 | @property (nonatomic, copy, readonly) NSString *path; 32 | @property (nonatomic, strong, readonly) MLNKVMemoryCache *memoryCache; 33 | 34 | @property (nullable, copy) MLNKVCustomArchiveBlock customArchiveBlock; 35 | @property (nullable, copy) MLNKVCustomUnarchiveBlock customUnarchiveBlock; 36 | 37 | // set 38 | 39 | - (BOOL)setKVObject:(nullable NSObject *)value forKey:(NSString *)aKey archiveBlock:(nullable MLNKVCustomArchiveBlock)archiveBlock; 40 | 41 | - (BOOL)setKVObject:(nullable NSObject *)value forKey:(NSString *)aKey; 42 | 43 | - (BOOL)setKVData:(nullable NSData *)value forKey:(NSString *)aKey; 44 | 45 | - (BOOL)setKVString:(nullable NSString *)value forKey:(NSString *)aKey; 46 | 47 | - (BOOL)setKVInt32:(int32_t)value forKey:(NSString *)aKey; 48 | 49 | - (BOOL)setKVInt64:(int64_t)value forKey:(NSString *)aKey; 50 | 51 | - (BOOL)setKVFloat:(float)value forKey:(NSString *)aKey; 52 | 53 | - (BOOL)setKVDouble:(double)value forKey:(NSString *)aKey; 54 | 55 | - (BOOL)setKVBool:(BOOL)value forKey:(NSString *)aKey; 56 | 57 | // get 58 | 59 | - (nullable id)getKVObjectForKey:(NSString *)aKey ofClass:(nullable Class)clz unarchiveBlock:(nullable MLNKVCustomUnarchiveBlock)unarchiveBlock; 60 | 61 | - (nullable id)getKVObjectForKey:(NSString *)aKey ofClass:(nullable Class)clz; 62 | 63 | - (nullable NSData *)getKVDataForKey:(NSString *)aKey; 64 | - (nullable NSData *)getKVDataForKey:(NSString *)aKey defaultValue:(nullable NSData *)defaultValue; 65 | 66 | - (nullable NSString *)getKVStringForKey:(NSString *)aKey; 67 | - (nullable NSString *)getKVStringForKey:(NSString *)aKey defaultValue:(nullable NSString *)defaultValue; 68 | 69 | - (int32_t)getKVInt32ForKey:(NSString *)aKey; 70 | - (int32_t)getKVInt32ForKey:(NSString *)aKey defaultValue:(int32_t)defaultValue; 71 | 72 | - (int64_t)getKVInt64ForKey:(NSString *)aKey; 73 | - (int64_t)getKVInt64ForKey:(NSString *)aKey defaultValue:(int64_t)defaultValue; 74 | 75 | - (float)getKVFloatForKey:(NSString *)aKey; 76 | - (float)getKVFloatForKey:(NSString *)aKey defaultValue:(float)defaultValue; 77 | 78 | - (double)getKVDoubleForKey:(NSString *)aKey; 79 | - (double)getKVDoubleForKey:(NSString *)aKey defaultValue:(double)defaultValue; 80 | 81 | - (BOOL)getKVBoolForKey:(NSString *)aKey; 82 | - (BOOL)getKVBoolForKey:(NSString *)aKey defaultValue:(BOOL)defaultValue; 83 | 84 | // ... 85 | - (MLNKVValueType)getValueType:(NSString *)aKey; 86 | - (size_t)getValueSizeForKey:(NSString *)aKey; 87 | - (BOOL)containsKey:(NSString *)aKey ; 88 | - (size_t)count; 89 | - (size_t)fileSize; 90 | - (size_t)usedSize; 91 | - (NSArray *)allKeys; 92 | 93 | - (void)removeValueForKey:(NSString *)key; 94 | - (void)removeValuesForKeys:(NSArray *)arrKeys; 95 | 96 | - (void)clearAll; 97 | - (void)trim; 98 | - (void)clearMemoryCache; 99 | 100 | - (void)sync; 101 | - (void)async; 102 | 103 | // unavailable 104 | + (instancetype)new NS_UNAVAILABLE; 105 | - (instancetype)init NS_UNAVAILABLE; 106 | - (id)copy NS_UNAVAILABLE; 107 | - (id)mutableCopy NS_UNAVAILABLE; 108 | 109 | @end 110 | 111 | NS_ASSUME_NONNULL_END 112 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/iOS/MLNKVMemoryCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVMemoryCache.h 3 | // MLNKV 4 | // 5 | // 内存缓存层 6 | // FIFO + LRU + NSMapTable 7 | // 8 | // Copyright © 2019 . All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface MLNKVMemoryCache : NSObject 16 | @property (copy, nullable) NSString *name; 17 | @property (readonly) NSUInteger totalCount; 18 | @property (readonly) NSUInteger totalMemorySize; 19 | 20 | @property NSUInteger LRUCountLimit; // default 10000 if == 0 don't use LRU cache 21 | @property NSUInteger FIFOCountLimit; // default 1000 if == 0 don't use FIFO cache 22 | @property NSUInteger memorySizeLimit; // default 100M 100 << 20 23 | 24 | @property (nonatomic) BOOL releaseOnMainThread; // default NO 25 | 26 | @property BOOL shouldRemoveAllObjectsOnMemoryWarning; // default YES 27 | @property BOOL shouldRemoveAllObjectsWhenEnteringBackground; // default YES 28 | 29 | @property BOOL usedLRU; // default YES use LRU cache 30 | @property BOOL usedFIFO; // default YES use FIFO cache 31 | 32 | #pragma mark - method 33 | - (BOOL)containsObjectForKey:(NSString *)aKey; 34 | - (nullable id)objectForKey:(NSString *)aKey; 35 | - (void)setObject:(nullable id)object forKey:(NSString *)aKey; 36 | - (void)setObject:(nullable id)object forKey:(NSString *)aKey withSize:(NSUInteger)size; 37 | - (void)setWeakObj:(nullable id)weakObj forKey:(NSString *)aKey; 38 | - (void)removeObjectForKey:(NSString *)aKey; 39 | - (void)removeAllObjects; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /Pods/MLNKV/Source/iOS/MLNKVMemoryCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVMemoryCache.m 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #import "MLNKVMemoryCache.h" 9 | #import 10 | #import 11 | 12 | @interface MLNKVCacheNode : NSObject { 13 | @package 14 | __weak MLNKVCacheNode *_prev; 15 | __weak MLNKVCacheNode *_next; 16 | NSString *_key; 17 | id _value; 18 | NSUInteger _size; 19 | } 20 | @end 21 | 22 | @implementation MLNKVCacheNode 23 | @end 24 | 25 | @interface MLNKVCache : NSObject { 26 | MLNKVCacheNode *_head; 27 | MLNKVCacheNode *_tail; 28 | 29 | NSMutableDictionary *_dict; 30 | } 31 | 32 | @property (nonatomic, assign) NSUInteger totalCount; 33 | @property (nonatomic, assign) NSUInteger totalSize; 34 | 35 | @property BOOL releaseOnMainThread; 36 | 37 | - (MLNKVCacheNode *)nodeForKey:(NSString *)aKey; 38 | 39 | - (void)insertNodeAtHead:(MLNKVCacheNode *)node; 40 | 41 | - (void)bringNodeToHead:(MLNKVCacheNode *)node; 42 | 43 | - (void)removeNode:(MLNKVCacheNode *)node; 44 | 45 | - (MLNKVCacheNode *)removeTailNode; 46 | 47 | - (void)removeAll; 48 | 49 | @end 50 | 51 | @implementation MLNKVCache 52 | 53 | - (instancetype)init { 54 | if (self = [super init]) { 55 | _dict = [[NSMutableDictionary alloc] init]; 56 | } 57 | return self; 58 | } 59 | 60 | - (NSUInteger)totalCount { 61 | return _dict.count; 62 | } 63 | 64 | - (MLNKVCacheNode *)nodeForKey:(NSString *)aKey { 65 | return [_dict objectForKey:aKey]; 66 | } 67 | 68 | - (void)insertNodeAtHead:(MLNKVCacheNode *)node { 69 | if (!node) return; 70 | [_dict setObject:node forKey:node->_key]; 71 | _totalSize += node->_size; 72 | if (_head) { 73 | node->_next = _head; 74 | _head->_prev = node; 75 | _head = node; 76 | } else { 77 | _head = _tail = node; 78 | } 79 | } 80 | 81 | - (void)bringNodeToHead:(MLNKVCacheNode *)node { 82 | if (!node) return; 83 | if (_head == node) return; 84 | if (_tail == node) { 85 | _tail = node->_prev; 86 | _tail->_next = nil; 87 | } else { 88 | node->_next->_prev = node->_prev; 89 | node->_prev->_next = node->_next; 90 | } 91 | node->_next = _head; 92 | node->_prev = nil; 93 | _head->_prev = node; 94 | _head = node; 95 | } 96 | 97 | - (void)removeNode:(MLNKVCacheNode *)node { 98 | if (!node) return; 99 | [_dict removeObjectForKey:node->_key]; 100 | _totalSize -= node->_size; 101 | if (node->_next) node->_next->_prev = node->_prev; 102 | if (node->_prev) node->_prev->_next = node->_next; 103 | if (_head == node) _head = node->_next; 104 | if (_tail == node) _tail = node->_prev; 105 | 106 | if (_releaseOnMainThread && !pthread_main_np()) { 107 | dispatch_async(dispatch_get_main_queue(), ^{ 108 | [node class]; 109 | }); 110 | } 111 | } 112 | 113 | - (MLNKVCacheNode *)removeTailNode { 114 | if (!_tail) return nil; 115 | MLNKVCacheNode *tail = _tail; 116 | [_dict removeObjectForKey:tail->_key]; 117 | _totalSize -= _tail->_size; 118 | if (_head == _tail) { 119 | _head = _tail = nil; 120 | } else { 121 | _tail = _tail->_prev; 122 | _tail->_next = nil; 123 | } 124 | if (_releaseOnMainThread && !pthread_main_np()) { 125 | dispatch_async(dispatch_get_main_queue(), ^{ 126 | [tail class]; 127 | }); 128 | } 129 | return tail; 130 | } 131 | 132 | - (void)removeAll { 133 | _totalSize = 0; 134 | _totalCount = 0; 135 | _head = nil; 136 | _tail = nil; 137 | if (_dict.count > 0 && _releaseOnMainThread && !pthread_main_np()) { 138 | NSArray *nodes = [_dict allValues]; 139 | dispatch_async(dispatch_get_main_queue(), ^{ 140 | [nodes class]; 141 | }); 142 | } 143 | [_dict removeAllObjects]; 144 | } 145 | 146 | @end 147 | 148 | 149 | @interface MLNKVMemoryCache () 150 | 151 | @property (nonatomic, strong) NSMapTable *weakMap; 152 | 153 | @property (nonatomic, strong) MLNKVCache *LRUCache; 154 | 155 | @property (nonatomic, strong) MLNKVCache *FIFOCache; 156 | 157 | @end 158 | @implementation MLNKVMemoryCache { 159 | pthread_mutex_t _lock; 160 | } 161 | 162 | - (NSString *)description { 163 | if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@) [totalCount:%lu, memorySize:%lu]", self.class, self, _name,(unsigned long)(_LRUCache.totalCount + _FIFOCache.totalCount), (unsigned long)(_LRUCache.totalSize + _FIFOCache.totalSize)]; 164 | else return [NSString stringWithFormat:@"<%@: %p> [totalCount:%lu, memorySize:%lu]", self.class, self, (unsigned long)(_LRUCache.totalCount + _FIFOCache.totalCount), (unsigned long)(_LRUCache.totalSize + _FIFOCache.totalSize)]; 165 | } 166 | 167 | - (void)dealloc { 168 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 169 | [_LRUCache removeAll]; 170 | [_FIFOCache removeAll]; 171 | pthread_mutex_destroy(&_lock); 172 | } 173 | 174 | - (instancetype)init { 175 | if (self = [super init]) { 176 | pthread_mutex_init(&_lock, NULL); 177 | _LRUCountLimit = 10000; 178 | _FIFOCountLimit = 1000; 179 | _memorySizeLimit = 100 << 20; 180 | _releaseOnMainThread = NO; 181 | _shouldRemoveAllObjectsOnMemoryWarning = YES; 182 | _shouldRemoveAllObjectsWhenEnteringBackground = YES; 183 | _usedLRU = YES; 184 | _usedFIFO = YES; 185 | 186 | _weakMap = [NSMapTable strongToWeakObjectsMapTable]; 187 | _LRUCache = [[MLNKVCache alloc] init]; 188 | _FIFOCache = [[MLNKVCache alloc] init]; 189 | _LRUCache.releaseOnMainThread = _releaseOnMainThread; 190 | _FIFOCache.releaseOnMainThread = _releaseOnMainThread; 191 | 192 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 193 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil]; 194 | } 195 | return self; 196 | } 197 | 198 | - (BOOL)containsObjectForKey:(NSString *)akey { 199 | if (!akey) return NO; 200 | pthread_mutex_lock(&_lock); 201 | BOOL contains = [_weakMap objectForKey:akey] || [_FIFOCache nodeForKey:akey] || [_LRUCache nodeForKey:akey]; 202 | pthread_mutex_unlock(&_lock); 203 | return contains; 204 | } 205 | 206 | - (id)objectForKey:(NSString *)akey { 207 | if (!akey) return nil; 208 | pthread_mutex_lock(&_lock); 209 | MLNKVCacheNode *node = (_usedFIFO && _FIFOCountLimit > 0)? [_FIFOCache nodeForKey:akey]: nil; 210 | if (node) { 211 | [_FIFOCache removeNode:node]; 212 | if (_usedLRU && _LRUCountLimit > 0) { 213 | [_LRUCache insertNodeAtHead:node]; 214 | } 215 | }else { 216 | if (_usedLRU && _LRUCountLimit > 0) { 217 | node = [_LRUCache nodeForKey:akey]; 218 | [_LRUCache bringNodeToHead:node]; 219 | } 220 | } 221 | if (_usedLRU && _LRUCache.totalCount > _LRUCountLimit) { 222 | [_LRUCache removeTailNode]; 223 | } 224 | id value = nil; 225 | if (node) { 226 | value = node->_value; 227 | }else { 228 | value = [_weakMap objectForKey:akey]; 229 | } 230 | pthread_mutex_unlock(&_lock); 231 | return value; 232 | } 233 | 234 | - (void)setObject:(id)object forKey:(NSString *)akey { 235 | [self setObject:object forKey:akey withSize:0]; 236 | } 237 | 238 | - (void)setObject:(id)object forKey:(NSString *)akey withSize:(NSUInteger)size { 239 | if (!akey) return; 240 | if (!object) { 241 | [self removeObjectForKey:akey]; 242 | return; 243 | } 244 | pthread_mutex_lock(&_lock); 245 | [_weakMap setObject:object forKey:akey]; 246 | 247 | MLNKVCacheNode *node = (_usedLRU && _LRUCountLimit > 0)? [_LRUCache nodeForKey:akey]: nil; 248 | if (node) { 249 | _LRUCache.totalSize = (_LRUCache.totalSize < node->_size)? 0: (_LRUCache.totalSize - node->_size); 250 | _LRUCache.totalSize += size; 251 | node->_value = object; 252 | node->_size = size; 253 | [_LRUCache bringNodeToHead:node]; 254 | }else { 255 | if (_usedFIFO && _FIFOCountLimit > 0) { 256 | node = [_FIFOCache nodeForKey:akey]; 257 | if (node) { 258 | _FIFOCache.totalSize = (_FIFOCache.totalSize < node->_size)? 0: (_FIFOCache.totalSize - node->_size); 259 | _FIFOCache.totalSize += size; 260 | node->_value = object; 261 | node->_size = size; 262 | [_FIFOCache bringNodeToHead:node]; 263 | }else { 264 | node = [[MLNKVCacheNode alloc] init]; 265 | node->_key = akey; 266 | node->_value = object; 267 | node->_size = size; 268 | [_FIFOCache insertNodeAtHead:node]; 269 | } 270 | if (_FIFOCache.totalCount > _FIFOCountLimit) { 271 | [_FIFOCache removeTailNode]; 272 | } 273 | } 274 | } 275 | 276 | while (_FIFOCache.totalSize + _LRUCache.totalSize > _memorySizeLimit) { 277 | if (![_LRUCache removeTailNode]) { 278 | if (![_FIFOCache removeTailNode]) { 279 | break; 280 | } 281 | } 282 | } 283 | pthread_mutex_unlock(&_lock); 284 | } 285 | 286 | - (void)setWeakObj:(id)weakObj forKey:(NSString *)aKey { 287 | if (!aKey) return; 288 | pthread_mutex_lock(&_lock); 289 | [_weakMap setObject:weakObj forKey:aKey]; 290 | pthread_mutex_unlock(&_lock); 291 | } 292 | 293 | - (void)removeObjectForKey:(NSString *)akey { 294 | if (!akey) return; 295 | pthread_mutex_lock(&_lock); 296 | if (_usedFIFO && _FIFOCountLimit > 0) { 297 | [_FIFOCache removeNode:[_FIFOCache nodeForKey:akey]]; 298 | } 299 | if (_usedLRU && _LRUCountLimit > 0) { 300 | [_LRUCache removeNode:[_LRUCache nodeForKey:akey]]; 301 | } 302 | [_weakMap removeObjectForKey:akey]; 303 | pthread_mutex_unlock(&_lock); 304 | } 305 | 306 | - (void)removeAllObjects { 307 | pthread_mutex_lock(&_lock); 308 | [_FIFOCache removeAll]; 309 | [_LRUCache removeAll]; 310 | [_weakMap removeAllObjects]; 311 | pthread_mutex_unlock(&_lock); 312 | } 313 | 314 | #pragma mark - set 315 | 316 | - (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread { 317 | pthread_mutex_lock(&_lock); 318 | _releaseOnMainThread = releaseOnMainThread; 319 | _FIFOCache.releaseOnMainThread = releaseOnMainThread; 320 | _LRUCache.releaseOnMainThread = releaseOnMainThread; 321 | pthread_mutex_unlock(&_lock); 322 | } 323 | 324 | - (NSUInteger)totalCount { 325 | pthread_mutex_lock(&_lock); 326 | NSUInteger count = _FIFOCache.totalCount + _LRUCache.totalCount; 327 | pthread_mutex_unlock(&_lock); 328 | return count; 329 | } 330 | 331 | - (NSUInteger)totalMemorySize { 332 | pthread_mutex_lock(&_lock); 333 | NSUInteger size = _FIFOCache.totalSize + _LRUCache.totalSize; 334 | pthread_mutex_unlock(&_lock); 335 | return size; 336 | } 337 | 338 | #pragma mark - --- 339 | 340 | - (void)applicationDidReceiveMemoryWarningNotification { 341 | if (_shouldRemoveAllObjectsOnMemoryWarning) { 342 | [self removeAllObjects]; 343 | } 344 | } 345 | 346 | - (void)applicationDidEnterBackgroundNotification { 347 | if (_shouldRemoveAllObjectsWhenEnteringBackground) { 348 | [self removeAllObjects]; 349 | } 350 | } 351 | 352 | @end 353 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MLNKV (0.0.3): 3 | - MLNKV/cpp (= 0.0.3) 4 | - MLNKV/cpp (0.0.3) 5 | 6 | DEPENDENCIES: 7 | - MLNKV (from `./MLNKV.podspec`) 8 | 9 | EXTERNAL SOURCES: 10 | MLNKV: 11 | :path: "./MLNKV.podspec" 12 | 13 | SPEC CHECKSUMS: 14 | MLNKV: bdab9e05b3e408151188a28e87d529162cdc2d93 15 | 16 | PODFILE CHECKSUM: 4068777ec4b8e48b9bc30e3f6e41f68bed99a2cb 17 | 18 | COCOAPODS: 1.8.4 19 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/sun.xcuserdatad/xcschemes/MLNKV.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/sun.xcuserdatad/xcschemes/Pods-MLNKV.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/sun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MLNKV.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-MLNKV.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.0.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MLNKV : NSObject 3 | @end 4 | @implementation PodsDummy_MLNKV 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "MLNKV.h" 14 | #import "MLNKVMemoryCache.h" 15 | #import "MLNKVBase.h" 16 | #import "MLNKVLog.h" 17 | #import "MLNKVType.h" 18 | #import "MLNKVUtility.h" 19 | #import "MLNKVValueInfo.hpp" 20 | 21 | FOUNDATION_EXPORT double MLNKVVersionNumber; 22 | FOUNDATION_EXPORT const unsigned char MLNKVVersionString[]; 23 | 24 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV.modulemap: -------------------------------------------------------------------------------- 1 | framework module MLNKV { 2 | umbrella header "MLNKV-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MLNKV/MLNKV.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_CXX_LANGUAGE_STANDARD = c++11 2 | CLANG_CXX_LIBRARY = libc++ 3 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MLNKV 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MLNKV" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MLNKV" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/.. 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_MLNKV : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_MLNKV 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/MLNKV/MLNKV.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MLNKV.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/MLNKV/MLNKV.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MLNKV.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/MLNKV/MLNKV.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/MLNKV/MLNKV.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_MLNKVVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_MLNKVVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MLNKV" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MLNKV" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MLNKV" -l"c++" -l"z" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_MLNKV { 2 | umbrella header "Pods-MLNKV-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MLNKV/Pods-MLNKV.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MLNKV" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MLNKV" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MLNKV" -l"c++" -l"z" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MLNKV 2 | ======= 3 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/momotech/MLNKV/blob/master/LICENSE) 4 | 5 | MLNKV是基于mmap实现的高性能、内存占用低、跨平台(支持iOS与Android)的Key-Value组件。 6 |
7 | 8 | # 性能对比 9 | `测试机型:iPhone Xs 13.1.3 64G`
10 | ![img](https://github.com/momotech/MLNKV/blob/master/img/setString.png)
11 | ![img](https://github.com/momotech/MLNKV/blob/master/img/setInt.png)
12 | ![img](https://github.com/momotech/MLNKV/blob/master/img/getString.png)
13 | ===== 14 | 内存占用对比:
15 | ![img](https://github.com/momotech/MLNKV/blob/master/img/memory.png) 16 | 17 | 18 | # 用法 19 | ### iOS 基本用法
20 | `pod 'MLNKV'` 21 | ``` 22 | 23 | // init 24 | MLNKV *mlnkv = [MLNKV defaultMLNKV]; 25 | // NSString *path = [MLNKVDEFAULTPATH stringByAppendingPathComponent:@".test"]; 26 | // MLNKV *mlnkv = [MLNKV mlnkvWithPath:path]; 27 | 28 | // set 29 | [mlnkv setKVString:@"value" forKey:@"key1"]; 30 | [mlnkv setKVBool:YES forKey:@"key2"]; 31 | [mlnkv setKVInt32:66666 forKey:@"key3"]; 32 | [mlnkv setKVInt64:88888888 forKey:@"key4"]; 33 | [mlnkv setKVFloat:66.666 forKey:@"key5"]; 34 | [mlnkv setKVDouble:8888888.888 forKey:@"key6"]; 35 | [mlnkv setKVObject:@{@"key":@"value"} forKey:@"key7"]; 36 | [mlnkv setKVData:data forKey:@"key8"]; 37 | 38 | // get 39 | int value = [mlnkv getKVInt32ForKey:@"key3"]; 40 | ... 41 | ... 42 | 43 | // obj 自己实现序列化 or 使用NSKeyedArchiver 44 | [mlnkv setKVObject:obj forKey:@"key" archiveBlock:^NSData * _Nullable(id _Nonnull obj) { 45 | // ...archive 46 | }]; 47 | [mlnkv getKVObjectForKey:@"key" ofClass:clz unarchiveBlock:^id _Nullable(NSData * _Nonnull data) { 48 | // ...unarchive 49 | }]; 50 | 51 | ``` 52 | ### Android 基本用法
53 | `maven {url "https://dl.bintray.com/sunzt8801/MLNKV"}`
54 | ` implementation "com.mlnkv:mlnkv:0.0.4" ` 55 | ``` 56 | 57 | // must call this in MainActivity 58 | MLNKV.initializeBasePath(this); 59 | 60 | // init 61 | MLNKV mlnkv = MLNKV.defaultMLNKV(); 62 | // String path = MLNKV.basPath() + "/.test"; 63 | // MLNKV mlnkv = new MLNKV(path); 64 | 65 | // set 66 | mlnkv.setBool("key1", true); 67 | mlnkv.setInt32("key2", 1); 68 | mlnkv.setInt64("key3", 88888888); 69 | mlnkv.setDouble("key4", 8888.888); 70 | mlnkv.setString("key5", "value"); 71 | mlnkv.setBytes("key6", bytes); 72 | 73 | // get 74 | boolean value = mlnkv.getBool("key1"); 75 | ... 76 | ... 77 | 78 | // obj 使用java Serializable 79 | mlnkv.setObject("key", obj); 80 | mlnkv.getObject("key", clz); 81 | 82 | ``` 83 | 84 | # 许可证 85 | MLNKV 使用 MIT 许可证,详情见 LICENSE 文件。 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVBase.hpp 3 | // MLNKV 4 | // 5 | // 6 | // Copyright © 2019 . All rights reserved. 7 | // 8 | 9 | #ifndef MLNKVManager_hpp 10 | #define MLNKVManager_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "MLNKVType.h" 20 | #include "MLNKVValueInfo.hpp" 21 | 22 | 23 | typedef enum : uint8_t { 24 | MLNKVReusedAvailableStatusNone = 0, 25 | MLNKVReusedAvailableStatusReused = 1, 26 | MLNKVReusedAvailableStatusAppend = 2, 27 | } MLNKVReusedAvailableStatus; 28 | 29 | class MLNKVBase { 30 | char *filePath; 31 | size_t fileSize; 32 | size_t usedSize; 33 | size_t unavailabledSize; 34 | int file; 35 | char *filemmap; 36 | std::unordered_map keysMap; 37 | std::unordered_map > unavailablesMap; 38 | 39 | std::mutex mutex; 40 | 41 | bool loadDataFromFile(bool openFile = true); 42 | bool isFileValid(); 43 | bool ensureMemorySize(size_t newSize); 44 | bool reuseMemoryOffset(size_t size, size_t &offset); 45 | bool sortUnavailableMemory(bool force); 46 | void markUnAvailable(MLNKVValueInfo &valueInfo); 47 | 48 | bool writeUInt32(uint32_t value, MLNKVValueType valueType, const std::string &key); 49 | bool writeUInt64(uint64_t value, MLNKVValueType valueType, const std::string &key); 50 | MLNKVReusedAvailableStatus getAvailableOffset(size_t dataSize, size_t &offset); 51 | 52 | bool removeValue(const std::string &key); 53 | bool removeValue(const std::string &key, std::unordered_map::iterator &valueItera, bool returnValue); 54 | bool clear(); 55 | 56 | public: 57 | MLNKVBase(const std::string &path); 58 | ~MLNKVBase(); 59 | 60 | // write bytes 61 | bool writeBytes(const void *value, const size_t size, MLNKVValueType valueType, const std::string &key); 62 | // read bytes 63 | bool readValueBytes(const std::string &key, MLNKVValueType &valueType, void* &value, size_t &size); 64 | 65 | // set 66 | 67 | bool setBytes(const void *value, const size_t size, const std::string &key); 68 | 69 | bool setString(const std::string &value, const std::string &key); 70 | 71 | bool setBool(bool value, const std::string &key); 72 | 73 | bool setInt32(int32_t value, const std::string &key); 74 | 75 | bool setInt64(int64_t value, const std::string &key); 76 | 77 | bool setFloat(float value, const std::string &key); 78 | 79 | bool setDouble(double value, const std::string &key); 80 | 81 | // get 82 | 83 | bool getBytesForKey(const std::string &key, void* &value, size_t &size); 84 | 85 | bool getStringForKey(const std::string &key, std::string &result); 86 | 87 | bool getBoolForKey(const std::string &key, bool defaultValue = false); 88 | 89 | int32_t getInt32ForKey(const std::string &key, int32_t defaultValue = 0); 90 | 91 | int64_t getInt64ForKey(const std::string &key, int64_t defaultValue = 0); 92 | 93 | float getFloatForKey(const std::string &key, float defaultValue = 0); 94 | 95 | double getDoubleForKey(const std::string &key, double defaultValue = 0); 96 | 97 | 98 | // size 99 | size_t getValueSizeForKey(const std::string &key, MLNKVValueType &valueType); 100 | 101 | bool containsKey(const std::string &key); 102 | 103 | size_t count(); 104 | 105 | size_t totalUsedSize(); 106 | 107 | std::vector allKeys(); 108 | 109 | 110 | // remove 111 | bool remove(const std::string &key); 112 | void clearAll(); 113 | void trim(); 114 | 115 | // sync 116 | void sync(bool isSync); 117 | 118 | // 119 | std::string getFilePath(); 120 | size_t getFileSize(); 121 | 122 | }; 123 | 124 | #endif /* MLNKVManager_hpp */ 125 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVLog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MILKVLog.c 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #include "MLNKVLog.h" 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | static MLNKVLogCallbackHandler *kMLNKVLogCallbackHandler; 15 | void MLNKVSetLogCallbackHandler(MLNKVLogCallbackHandler *callbackHandler) { 16 | kMLNKVLogCallbackHandler = callbackHandler; 17 | } 18 | MLNKVLogCallbackHandler *MLNKVGetLogCallbackHandler(void) { 19 | return kMLNKVLogCallbackHandler; 20 | } 21 | 22 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...) { 23 | 24 | if (kMLNKVLogCallbackHandler) { 25 | va_list args; 26 | va_start(args, format); 27 | kMLNKVLogCallbackHandler(level, file, func, line, format, args); 28 | va_end(args); 29 | return; 30 | } 31 | 32 | std::string message; 33 | char buffer[16]; 34 | 35 | va_list args; 36 | va_start(args, format); 37 | auto length = vsnprintf(buffer, sizeof(buffer), format, args); 38 | va_end(args); 39 | 40 | if (length < 0) { // something wrong 41 | message = {}; 42 | } else if (length < sizeof(buffer)) { 43 | message = std::string(buffer, static_cast(length)); 44 | } else { 45 | message.resize(static_cast(length), '\0'); 46 | va_start(args, format); 47 | vsnprintf(const_cast(message.data()), static_cast(length) + 1, 48 | format, args); 49 | va_end(args); 50 | } 51 | 52 | time_t t; 53 | struct tm *timeinfo; 54 | time(&t); 55 | timeinfo = localtime(&t); 56 | 57 | char timeStr[128]; 58 | strftime(timeStr, sizeof(timeStr), "%F %T", timeinfo); 59 | 60 | struct timeval tv; 61 | gettimeofday(&tv, NULL); 62 | 63 | printf("%s.%d <%s:%s::%d> [%c] %s \n", timeStr, (int)tv.tv_usec, file, func, line, (level == MLNLogDebug)?'D':'E', message.c_str()); 64 | } 65 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVLog_h.h 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVLog_h 9 | #define MLNKVLog_h 10 | 11 | #include 12 | #include 13 | 14 | typedef enum { 15 | MLNLogDebug = 0, 16 | MLNLogError = 1, 17 | } MLNLogLevel; 18 | 19 | typedef void MLNKVLogCallbackHandler(MLNLogLevel level, const char *file, const char *func, int line, const char *format, va_list args); 20 | 21 | extern void MLNKVSetLogCallbackHandler(MLNKVLogCallbackHandler *callbackHandler); 22 | extern MLNKVLogCallbackHandler *MLNKVGetLogCallbackHandler(void); 23 | 24 | //#define MLNDebug 25 | 26 | #define __mlnkv_filename__ (strrchr(__FILE__, '/') + 1) 27 | void MLNKVLogWithLevel(MLNLogLevel level, const char *file, const char *func, int line, const char *format, ...); 28 | 29 | #ifdef MLNDebug 30 | 31 | #define MLNKVLog(format, ...) MLNKVLogWithLevel(MLNLogDebug, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 32 | 33 | #else 34 | 35 | #define MLNKVLog(format, ...) 36 | 37 | #endif 38 | 39 | #define MLNKVError(format, ...) MLNKVLogWithLevel(MLNLogError, __mlnkv_filename__, __func__, __LINE__, format, ##__VA_ARGS__) 40 | 41 | 42 | 43 | #endif /* MLNKVLog_h */ 44 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVType.h 3 | // 4 | // 5 | // 6 | 7 | #ifndef MLNKVType_h 8 | #define MLNKVType_h 9 | 10 | typedef enum : uint8_t { 11 | MLNKVValueType_None = 0, 12 | MLNKVValueType_Used = 1, 13 | MLNKVValueType_Bool = 2, 14 | MLNKVValueType_Float = 3, 15 | MLNKVValueType_Int32 = 4, 16 | MLNKVValueType_Double = 5, 17 | MLNKVValueType_Int64 = 6, 18 | MLNKVValueType_String = 7, 19 | MLNKVValueType_Bytes = 8, 20 | } MLNKVValueType; 21 | 22 | 23 | #endif /* MLNKVType_h */ 24 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVUtility.hpp 3 | // MLNKV 4 | // 5 | // Copyright © 2019 . All rights reserved. 6 | // 7 | 8 | #ifndef MLNKVUtility_hpp 9 | #define MLNKVUtility_hpp 10 | 11 | #include 12 | #include 13 | 14 | 15 | template 16 | union MLNKVConverter { 17 | static_assert(sizeof(T) == sizeof(P), "size not match"); 18 | T first; 19 | P second; 20 | }; 21 | 22 | static inline uint64_t MLNKVInt64ToUInt64(int64_t v) { 23 | MLNKVConverter converter; 24 | converter.first = v; 25 | return converter.second; 26 | } 27 | 28 | static inline int64_t MLNKVUInt64ToInt64(uint64_t v) { 29 | MLNKVConverter converter; 30 | converter.second = v; 31 | return converter.first; 32 | } 33 | 34 | static inline uint32_t MLNKVInt32ToUInt32(int32_t v) { 35 | MLNKVConverter converter; 36 | converter.first = v; 37 | return converter.second; 38 | } 39 | 40 | static inline int32_t MLNKVUInt32ToInt32(uint32_t v) { 41 | MLNKVConverter converter; 42 | converter.second = v; 43 | return converter.first; 44 | } 45 | 46 | static inline uint32_t MLNKVFloatToUInt32(float v){ 47 | MLNKVConverter converter; 48 | converter.first = v; 49 | return converter.second; 50 | } 51 | 52 | static inline float MLNKVUInt32ToFloat(uint32_t v) { 53 | MLNKVConverter converter; 54 | converter.first = v; 55 | return converter.second; 56 | } 57 | 58 | static inline uint64_t MLNKVDoubleToUInt64(double v) { 59 | MLNKVConverter converter; 60 | converter.first = v; 61 | return converter.second; 62 | } 63 | 64 | static inline double MLNKVUInt64ToDouble(uint64_t v) { 65 | MLNKVConverter converter; 66 | converter.first = v; 67 | return converter.second; 68 | } 69 | 70 | 71 | static inline uint8_t MLNKVRawVarUInt32Size(uint32_t value) { 72 | if ((value & (0xffffffff << 8)) == 0) { 73 | return 1; 74 | } else if ((value & (0xffffffff << 16)) == 0) { 75 | return 2; 76 | } else if ((value & (0xffffffff << 24)) == 0) { 77 | return 3; 78 | } 79 | return 4; 80 | } 81 | 82 | static inline uint8_t MLNKVRawVarUInt64Size(uint64_t value) { 83 | if ((value & (0xffffffffffffffffL << 8)) == 0) { 84 | return 1; 85 | } else if ((value & (0xffffffffffffffffL << 16)) == 0) { 86 | return 2; 87 | } else if ((value & (0xffffffffffffffffL << 24)) == 0) { 88 | return 3; 89 | } else if ((value & (0xffffffffffffffffL << 32)) == 0) { 90 | return 4; 91 | } else if ((value & (0xffffffffffffffffL << 40)) == 0) { 92 | return 5; 93 | } else if ((value & (0xffffffffffffffffL << 48)) == 0) { 94 | return 6; 95 | } else if ((value & (0xffffffffffffffffL << 56)) == 0) { 96 | return 7; 97 | } 98 | return 8; 99 | } 100 | 101 | //constexpr int16_t MLNKVBoolSize(bool value) { 102 | // return 1; 103 | //} 104 | 105 | static const uint8_t MLNKV32Size = 4; 106 | 107 | static const uint8_t MLNKV64Size = 8; 108 | // 109 | ////// 判断机器模式 大小端 110 | /////** 111 | //// 1) Little-Endian:就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 112 | //// 2) Big-Endian:就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 113 | //// */ 114 | //static inline bool MLNKVIsLittleEdian() { 115 | // union { 116 | // unsigned int a; 117 | // unsigned char b; 118 | // } c; 119 | // c.a = 1; 120 | // return (c.b == 1); 121 | //} 122 | 123 | static inline uint32_t MLNKVReadUInt32(uint8_t* ptr, uint8_t size) { 124 | uint32_t value = 0; 125 | for (uint8_t i = 0; i < size; i++) { 126 | value |= (uint32_t)(ptr[i] & 0xFF) << (8 * i); 127 | } 128 | return value; 129 | } 130 | 131 | static inline uint64_t MLNKVReadUInt64(uint8_t* ptr, uint8_t size) { 132 | uint64_t value = 0; 133 | for (uint8_t i = 0; i < size; i++) { 134 | value |= (uint64_t)(ptr[i] & 0xFF) << (8 * i); 135 | } 136 | return value; 137 | } 138 | 139 | static inline bool MLNKVWriteUInt32(uint8_t* ptr, uint32_t value, uint8_t size) { 140 | for (uint8_t i = 0; i < size; i++) { 141 | ptr[i] = (uint8_t)(value & 0xFF); 142 | value = value >> 8; 143 | } 144 | return true; 145 | } 146 | 147 | static inline bool MLNKVWriteUInt64(uint8_t* ptr, uint64_t value, uint8_t size) { 148 | for (uint8_t i = 0; i < size; i++) { 149 | ptr[i] = (uint8_t)(value & 0xFF); 150 | value = value >> 8; 151 | } 152 | return true; 153 | } 154 | 155 | 156 | #endif /* MLNKVUtility_hpp */ 157 | -------------------------------------------------------------------------------- /Source/cpp/MLNKVValueInfo.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVValueInfo.hpp 3 | // MLNKV 4 | // 5 | // 6 | 7 | #ifndef MLNKVValueInfo_hpp 8 | #define MLNKVValueInfo_hpp 9 | 10 | #include 11 | #include "MLNKVType.h" 12 | 13 | 14 | class MLNKVValueInfo { 15 | 16 | public: 17 | 18 | MLNKVValueType type; 19 | size_t offset; 20 | uint8_t extSize; 21 | uint32_t keySize; 22 | size_t valueSize; 23 | 24 | MLNKVValueInfo(MLNKVValueType type = MLNKVValueType_None): type(type) {} 25 | 26 | MLNKVValueInfo(MLNKVValueInfo &&other)noexcept : 27 | type(other.type), 28 | offset(other.offset), 29 | extSize(other.extSize), 30 | keySize(other.keySize), 31 | valueSize(other.valueSize) { 32 | other.type = MLNKVValueType_None; 33 | other.offset = 0; 34 | other.extSize = 0; 35 | other.keySize = 0; 36 | other.valueSize = 0; 37 | } 38 | MLNKVValueInfo &operator=(MLNKVValueInfo &&other) noexcept { 39 | std::swap(type, other.type); 40 | std::swap(offset, other.offset); 41 | std::swap(extSize, other.extSize); 42 | std::swap(keySize, other.keySize); 43 | std::swap(valueSize, other.valueSize); 44 | return *this; 45 | } 46 | 47 | ~MLNKVValueInfo() {} 48 | 49 | private: 50 | MLNKVValueInfo(const MLNKVValueInfo &other) = delete; 51 | MLNKVValueInfo &operator=(const MLNKVValueInfo &other) = delete; 52 | 53 | }; 54 | 55 | #endif /* MLNKVValueInfo_hpp */ 56 | -------------------------------------------------------------------------------- /Source/iOS/MLNKV.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKV.h 3 | // MLNKV 4 | // 5 | // 6 | // 7 | // Copyright © 2019 All rights reserved. 8 | // 9 | 10 | #import 11 | #import "MLNKVMemoryCache.h" 12 | #import "MLNKVType.h" 13 | 14 | #define MLNKVDEFAULTPATH [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@".mlnkv"] 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | typedef NSData *_Nullable(^MLNKVCustomArchiveBlock)(id obj); 19 | typedef id _Nullable(^MLNKVCustomUnarchiveBlock)(NSData *data); 20 | 21 | @class MLNKVMemoryCache; 22 | 23 | @interface MLNKV : NSObject 24 | 25 | + (instancetype)defaultMLNKV; 26 | 27 | + (instancetype)mlnkvWithPath:(NSString *)path; 28 | 29 | - (instancetype)initMLNKVWithPath:(NSString *)path; 30 | 31 | @property (nonatomic, copy, readonly) NSString *path; 32 | @property (nonatomic, strong, readonly) MLNKVMemoryCache *memoryCache; 33 | 34 | @property (nullable, copy) MLNKVCustomArchiveBlock customArchiveBlock; 35 | @property (nullable, copy) MLNKVCustomUnarchiveBlock customUnarchiveBlock; 36 | 37 | // set 38 | 39 | - (BOOL)setKVObject:(nullable NSObject *)value forKey:(NSString *)aKey archiveBlock:(nullable MLNKVCustomArchiveBlock)archiveBlock; 40 | 41 | - (BOOL)setKVObject:(nullable NSObject *)value forKey:(NSString *)aKey; 42 | 43 | - (BOOL)setKVData:(nullable NSData *)value forKey:(NSString *)aKey; 44 | 45 | - (BOOL)setKVString:(nullable NSString *)value forKey:(NSString *)aKey; 46 | 47 | - (BOOL)setKVInt32:(int32_t)value forKey:(NSString *)aKey; 48 | 49 | - (BOOL)setKVInt64:(int64_t)value forKey:(NSString *)aKey; 50 | 51 | - (BOOL)setKVFloat:(float)value forKey:(NSString *)aKey; 52 | 53 | - (BOOL)setKVDouble:(double)value forKey:(NSString *)aKey; 54 | 55 | - (BOOL)setKVBool:(BOOL)value forKey:(NSString *)aKey; 56 | 57 | // get 58 | 59 | - (nullable id)getKVObjectForKey:(NSString *)aKey ofClass:(nullable Class)clz unarchiveBlock:(nullable MLNKVCustomUnarchiveBlock)unarchiveBlock; 60 | 61 | - (nullable id)getKVObjectForKey:(NSString *)aKey ofClass:(nullable Class)clz; 62 | 63 | - (nullable NSData *)getKVDataForKey:(NSString *)aKey; 64 | - (nullable NSData *)getKVDataForKey:(NSString *)aKey defaultValue:(nullable NSData *)defaultValue; 65 | 66 | - (nullable NSString *)getKVStringForKey:(NSString *)aKey; 67 | - (nullable NSString *)getKVStringForKey:(NSString *)aKey defaultValue:(nullable NSString *)defaultValue; 68 | 69 | - (int32_t)getKVInt32ForKey:(NSString *)aKey; 70 | - (int32_t)getKVInt32ForKey:(NSString *)aKey defaultValue:(int32_t)defaultValue; 71 | 72 | - (int64_t)getKVInt64ForKey:(NSString *)aKey; 73 | - (int64_t)getKVInt64ForKey:(NSString *)aKey defaultValue:(int64_t)defaultValue; 74 | 75 | - (float)getKVFloatForKey:(NSString *)aKey; 76 | - (float)getKVFloatForKey:(NSString *)aKey defaultValue:(float)defaultValue; 77 | 78 | - (double)getKVDoubleForKey:(NSString *)aKey; 79 | - (double)getKVDoubleForKey:(NSString *)aKey defaultValue:(double)defaultValue; 80 | 81 | - (BOOL)getKVBoolForKey:(NSString *)aKey; 82 | - (BOOL)getKVBoolForKey:(NSString *)aKey defaultValue:(BOOL)defaultValue; 83 | 84 | // ... 85 | - (MLNKVValueType)getValueType:(NSString *)aKey; 86 | - (size_t)getValueSizeForKey:(NSString *)aKey; 87 | - (BOOL)containsKey:(NSString *)aKey ; 88 | - (size_t)count; 89 | - (size_t)fileSize; 90 | - (size_t)usedSize; 91 | - (NSArray *)allKeys; 92 | 93 | - (void)removeValueForKey:(NSString *)key; 94 | - (void)removeValuesForKeys:(NSArray *)arrKeys; 95 | 96 | - (void)clearAll; 97 | - (void)trim; 98 | - (void)clearMemoryCache; 99 | 100 | - (void)sync; 101 | - (void)async; 102 | 103 | // unavailable 104 | + (instancetype)new NS_UNAVAILABLE; 105 | - (instancetype)init NS_UNAVAILABLE; 106 | - (id)copy NS_UNAVAILABLE; 107 | - (id)mutableCopy NS_UNAVAILABLE; 108 | 109 | @end 110 | 111 | NS_ASSUME_NONNULL_END 112 | -------------------------------------------------------------------------------- /Source/iOS/MLNKVMemoryCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVMemoryCache.h 3 | // MLNKV 4 | // 5 | // 内存缓存层 6 | // FIFO + LRU + NSMapTable 7 | // 8 | // Copyright © 2019 . All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface MLNKVMemoryCache : NSObject 16 | @property (copy, nullable) NSString *name; 17 | @property (readonly) NSUInteger totalCount; 18 | @property (readonly) NSUInteger totalMemorySize; 19 | 20 | @property NSUInteger LRUCountLimit; // default 10000 if == 0 don't use LRU cache 21 | @property NSUInteger FIFOCountLimit; // default 1000 if == 0 don't use FIFO cache 22 | @property NSUInteger memorySizeLimit; // default 100M 100 << 20 23 | 24 | @property (nonatomic) BOOL releaseOnMainThread; // default NO 25 | 26 | @property BOOL shouldRemoveAllObjectsOnMemoryWarning; // default YES 27 | @property BOOL shouldRemoveAllObjectsWhenEnteringBackground; // default YES 28 | 29 | @property BOOL usedLRU; // default YES use LRU cache 30 | @property BOOL usedFIFO; // default YES use FIFO cache 31 | 32 | #pragma mark - method 33 | - (BOOL)containsObjectForKey:(NSString *)aKey; 34 | - (nullable id)objectForKey:(NSString *)aKey; 35 | - (void)setObject:(nullable id)object forKey:(NSString *)aKey; 36 | - (void)setObject:(nullable id)object forKey:(NSString *)aKey withSize:(NSUInteger)size; 37 | - (void)setWeakObj:(nullable id)weakObj forKey:(NSString *)aKey; 38 | - (void)removeObjectForKey:(NSString *)aKey; 39 | - (void)removeAllObjects; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /Source/iOS/MLNKVMemoryCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // MLNKVMemoryCache.m 3 | // MLNKV 4 | // 5 | // Copyright © 2019. All rights reserved. 6 | // 7 | 8 | #import "MLNKVMemoryCache.h" 9 | #import 10 | #import 11 | 12 | @interface MLNKVCacheNode : NSObject { 13 | @package 14 | __weak MLNKVCacheNode *_prev; 15 | __weak MLNKVCacheNode *_next; 16 | NSString *_key; 17 | id _value; 18 | NSUInteger _size; 19 | } 20 | @end 21 | 22 | @implementation MLNKVCacheNode 23 | @end 24 | 25 | @interface MLNKVCache : NSObject { 26 | MLNKVCacheNode *_head; 27 | MLNKVCacheNode *_tail; 28 | 29 | NSMutableDictionary *_dict; 30 | } 31 | 32 | @property (nonatomic, assign) NSUInteger totalCount; 33 | @property (nonatomic, assign) NSUInteger totalSize; 34 | 35 | @property BOOL releaseOnMainThread; 36 | 37 | - (MLNKVCacheNode *)nodeForKey:(NSString *)aKey; 38 | 39 | - (void)insertNodeAtHead:(MLNKVCacheNode *)node; 40 | 41 | - (void)bringNodeToHead:(MLNKVCacheNode *)node; 42 | 43 | - (void)removeNode:(MLNKVCacheNode *)node; 44 | 45 | - (MLNKVCacheNode *)removeTailNode; 46 | 47 | - (void)removeAll; 48 | 49 | @end 50 | 51 | @implementation MLNKVCache 52 | 53 | - (instancetype)init { 54 | if (self = [super init]) { 55 | _dict = [[NSMutableDictionary alloc] init]; 56 | } 57 | return self; 58 | } 59 | 60 | - (NSUInteger)totalCount { 61 | return _dict.count; 62 | } 63 | 64 | - (MLNKVCacheNode *)nodeForKey:(NSString *)aKey { 65 | return [_dict objectForKey:aKey]; 66 | } 67 | 68 | - (void)insertNodeAtHead:(MLNKVCacheNode *)node { 69 | if (!node) return; 70 | [_dict setObject:node forKey:node->_key]; 71 | _totalSize += node->_size; 72 | if (_head) { 73 | node->_next = _head; 74 | _head->_prev = node; 75 | _head = node; 76 | } else { 77 | _head = _tail = node; 78 | } 79 | } 80 | 81 | - (void)bringNodeToHead:(MLNKVCacheNode *)node { 82 | if (!node) return; 83 | if (_head == node) return; 84 | if (_tail == node) { 85 | _tail = node->_prev; 86 | _tail->_next = nil; 87 | } else { 88 | node->_next->_prev = node->_prev; 89 | node->_prev->_next = node->_next; 90 | } 91 | node->_next = _head; 92 | node->_prev = nil; 93 | _head->_prev = node; 94 | _head = node; 95 | } 96 | 97 | - (void)removeNode:(MLNKVCacheNode *)node { 98 | if (!node) return; 99 | [_dict removeObjectForKey:node->_key]; 100 | _totalSize -= node->_size; 101 | if (node->_next) node->_next->_prev = node->_prev; 102 | if (node->_prev) node->_prev->_next = node->_next; 103 | if (_head == node) _head = node->_next; 104 | if (_tail == node) _tail = node->_prev; 105 | 106 | if (_releaseOnMainThread && !pthread_main_np()) { 107 | dispatch_async(dispatch_get_main_queue(), ^{ 108 | [node class]; 109 | }); 110 | } 111 | } 112 | 113 | - (MLNKVCacheNode *)removeTailNode { 114 | if (!_tail) return nil; 115 | MLNKVCacheNode *tail = _tail; 116 | [_dict removeObjectForKey:tail->_key]; 117 | _totalSize -= _tail->_size; 118 | if (_head == _tail) { 119 | _head = _tail = nil; 120 | } else { 121 | _tail = _tail->_prev; 122 | _tail->_next = nil; 123 | } 124 | if (_releaseOnMainThread && !pthread_main_np()) { 125 | dispatch_async(dispatch_get_main_queue(), ^{ 126 | [tail class]; 127 | }); 128 | } 129 | return tail; 130 | } 131 | 132 | - (void)removeAll { 133 | _totalSize = 0; 134 | _totalCount = 0; 135 | _head = nil; 136 | _tail = nil; 137 | if (_dict.count > 0 && _releaseOnMainThread && !pthread_main_np()) { 138 | NSArray *nodes = [_dict allValues]; 139 | dispatch_async(dispatch_get_main_queue(), ^{ 140 | [nodes class]; 141 | }); 142 | } 143 | [_dict removeAllObjects]; 144 | } 145 | 146 | @end 147 | 148 | 149 | @interface MLNKVMemoryCache () 150 | 151 | @property (nonatomic, strong) NSMapTable *weakMap; 152 | 153 | @property (nonatomic, strong) MLNKVCache *LRUCache; 154 | 155 | @property (nonatomic, strong) MLNKVCache *FIFOCache; 156 | 157 | @end 158 | @implementation MLNKVMemoryCache { 159 | pthread_mutex_t _lock; 160 | } 161 | 162 | - (NSString *)description { 163 | if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@) [totalCount:%lu, memorySize:%lu]", self.class, self, _name,(unsigned long)(_LRUCache.totalCount + _FIFOCache.totalCount), (unsigned long)(_LRUCache.totalSize + _FIFOCache.totalSize)]; 164 | else return [NSString stringWithFormat:@"<%@: %p> [totalCount:%lu, memorySize:%lu]", self.class, self, (unsigned long)(_LRUCache.totalCount + _FIFOCache.totalCount), (unsigned long)(_LRUCache.totalSize + _FIFOCache.totalSize)]; 165 | } 166 | 167 | - (void)dealloc { 168 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 169 | [_LRUCache removeAll]; 170 | [_FIFOCache removeAll]; 171 | pthread_mutex_destroy(&_lock); 172 | } 173 | 174 | - (instancetype)init { 175 | if (self = [super init]) { 176 | pthread_mutex_init(&_lock, NULL); 177 | _LRUCountLimit = 10000; 178 | _FIFOCountLimit = 1000; 179 | _memorySizeLimit = 100 << 20; 180 | _releaseOnMainThread = NO; 181 | _shouldRemoveAllObjectsOnMemoryWarning = YES; 182 | _shouldRemoveAllObjectsWhenEnteringBackground = YES; 183 | _usedLRU = YES; 184 | _usedFIFO = YES; 185 | 186 | _weakMap = [NSMapTable strongToWeakObjectsMapTable]; 187 | _LRUCache = [[MLNKVCache alloc] init]; 188 | _FIFOCache = [[MLNKVCache alloc] init]; 189 | _LRUCache.releaseOnMainThread = _releaseOnMainThread; 190 | _FIFOCache.releaseOnMainThread = _releaseOnMainThread; 191 | 192 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 193 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil]; 194 | } 195 | return self; 196 | } 197 | 198 | - (BOOL)containsObjectForKey:(NSString *)akey { 199 | if (!akey) return NO; 200 | pthread_mutex_lock(&_lock); 201 | BOOL contains = [_weakMap objectForKey:akey] || [_FIFOCache nodeForKey:akey] || [_LRUCache nodeForKey:akey]; 202 | pthread_mutex_unlock(&_lock); 203 | return contains; 204 | } 205 | 206 | - (id)objectForKey:(NSString *)akey { 207 | if (!akey) return nil; 208 | pthread_mutex_lock(&_lock); 209 | MLNKVCacheNode *node = (_usedFIFO && _FIFOCountLimit > 0)? [_FIFOCache nodeForKey:akey]: nil; 210 | if (node) { 211 | [_FIFOCache removeNode:node]; 212 | if (_usedLRU && _LRUCountLimit > 0) { 213 | [_LRUCache insertNodeAtHead:node]; 214 | } 215 | }else { 216 | if (_usedLRU && _LRUCountLimit > 0) { 217 | node = [_LRUCache nodeForKey:akey]; 218 | [_LRUCache bringNodeToHead:node]; 219 | } 220 | } 221 | if (_usedLRU && _LRUCache.totalCount > _LRUCountLimit) { 222 | [_LRUCache removeTailNode]; 223 | } 224 | id value = nil; 225 | if (node) { 226 | value = node->_value; 227 | }else { 228 | value = [_weakMap objectForKey:akey]; 229 | } 230 | pthread_mutex_unlock(&_lock); 231 | return value; 232 | } 233 | 234 | - (void)setObject:(id)object forKey:(NSString *)akey { 235 | [self setObject:object forKey:akey withSize:0]; 236 | } 237 | 238 | - (void)setObject:(id)object forKey:(NSString *)akey withSize:(NSUInteger)size { 239 | if (!akey) return; 240 | if (!object) { 241 | [self removeObjectForKey:akey]; 242 | return; 243 | } 244 | pthread_mutex_lock(&_lock); 245 | [_weakMap setObject:object forKey:akey]; 246 | 247 | MLNKVCacheNode *node = (_usedLRU && _LRUCountLimit > 0)? [_LRUCache nodeForKey:akey]: nil; 248 | if (node) { 249 | _LRUCache.totalSize = (_LRUCache.totalSize < node->_size)? 0: (_LRUCache.totalSize - node->_size); 250 | _LRUCache.totalSize += size; 251 | node->_value = object; 252 | node->_size = size; 253 | [_LRUCache bringNodeToHead:node]; 254 | }else { 255 | if (_usedFIFO && _FIFOCountLimit > 0) { 256 | node = [_FIFOCache nodeForKey:akey]; 257 | if (node) { 258 | _FIFOCache.totalSize = (_FIFOCache.totalSize < node->_size)? 0: (_FIFOCache.totalSize - node->_size); 259 | _FIFOCache.totalSize += size; 260 | node->_value = object; 261 | node->_size = size; 262 | [_FIFOCache bringNodeToHead:node]; 263 | }else { 264 | node = [[MLNKVCacheNode alloc] init]; 265 | node->_key = akey; 266 | node->_value = object; 267 | node->_size = size; 268 | [_FIFOCache insertNodeAtHead:node]; 269 | } 270 | if (_FIFOCache.totalCount > _FIFOCountLimit) { 271 | [_FIFOCache removeTailNode]; 272 | } 273 | } 274 | } 275 | 276 | while (_FIFOCache.totalSize + _LRUCache.totalSize > _memorySizeLimit) { 277 | if (![_LRUCache removeTailNode]) { 278 | if (![_FIFOCache removeTailNode]) { 279 | break; 280 | } 281 | } 282 | } 283 | pthread_mutex_unlock(&_lock); 284 | } 285 | 286 | - (void)setWeakObj:(id)weakObj forKey:(NSString *)aKey { 287 | if (!aKey) return; 288 | pthread_mutex_lock(&_lock); 289 | [_weakMap setObject:weakObj forKey:aKey]; 290 | pthread_mutex_unlock(&_lock); 291 | } 292 | 293 | - (void)removeObjectForKey:(NSString *)akey { 294 | if (!akey) return; 295 | pthread_mutex_lock(&_lock); 296 | if (_usedFIFO && _FIFOCountLimit > 0) { 297 | [_FIFOCache removeNode:[_FIFOCache nodeForKey:akey]]; 298 | } 299 | if (_usedLRU && _LRUCountLimit > 0) { 300 | [_LRUCache removeNode:[_LRUCache nodeForKey:akey]]; 301 | } 302 | [_weakMap removeObjectForKey:akey]; 303 | pthread_mutex_unlock(&_lock); 304 | } 305 | 306 | - (void)removeAllObjects { 307 | pthread_mutex_lock(&_lock); 308 | [_FIFOCache removeAll]; 309 | [_LRUCache removeAll]; 310 | [_weakMap removeAllObjects]; 311 | pthread_mutex_unlock(&_lock); 312 | } 313 | 314 | #pragma mark - set 315 | 316 | - (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread { 317 | pthread_mutex_lock(&_lock); 318 | _releaseOnMainThread = releaseOnMainThread; 319 | _FIFOCache.releaseOnMainThread = releaseOnMainThread; 320 | _LRUCache.releaseOnMainThread = releaseOnMainThread; 321 | pthread_mutex_unlock(&_lock); 322 | } 323 | 324 | - (NSUInteger)totalCount { 325 | pthread_mutex_lock(&_lock); 326 | NSUInteger count = _FIFOCache.totalCount + _LRUCache.totalCount; 327 | pthread_mutex_unlock(&_lock); 328 | return count; 329 | } 330 | 331 | - (NSUInteger)totalMemorySize { 332 | pthread_mutex_lock(&_lock); 333 | NSUInteger size = _FIFOCache.totalSize + _LRUCache.totalSize; 334 | pthread_mutex_unlock(&_lock); 335 | return size; 336 | } 337 | 338 | #pragma mark - --- 339 | 340 | - (void)applicationDidReceiveMemoryWarningNotification { 341 | if (_shouldRemoveAllObjectsOnMemoryWarning) { 342 | [self removeAllObjects]; 343 | } 344 | } 345 | 346 | - (void)applicationDidEnterBackgroundNotification { 347 | if (_shouldRemoveAllObjectsWhenEnteringBackground) { 348 | [self removeAllObjects]; 349 | } 350 | } 351 | 352 | @end 353 | -------------------------------------------------------------------------------- /img/getString.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/img/getString.png -------------------------------------------------------------------------------- /img/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/img/memory.png -------------------------------------------------------------------------------- /img/setInt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/img/setInt.png -------------------------------------------------------------------------------- /img/setString.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momotech/MLNKV/a98d5f9596fcf8a9107e8b5aae4f0c5cbd61a0ab/img/setString.png --------------------------------------------------------------------------------