├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── litl │ │ └── leveldb │ │ └── test │ │ └── DBTests.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── litl │ │ └── leveldb │ │ ├── Chunk.java │ │ ├── DB.java │ │ ├── DatabaseCorruptException.java │ │ ├── Iterator.java │ │ ├── LevelDBException.java │ │ ├── NativeObject.java │ │ ├── NotFoundException.java │ │ └── WriteBatch.java │ └── jni │ ├── Android.mk │ ├── Application.mk │ ├── Chunk.cc │ ├── Chunk.h │ ├── blocknames.cc │ ├── blocknames.h │ ├── com_litl_leveldb_Chunk.cc │ ├── com_litl_leveldb_DB.cc │ ├── com_litl_leveldb_Iterator.cc │ ├── com_litl_leveldb_WriteBatch.cc │ ├── debug_conf.h │ ├── leveldbjni.cc │ ├── leveldbjni.h │ ├── mapkey.h │ ├── qstr.h │ ├── subchunk.cc │ └── subchunk.h └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | gen 3 | obj 4 | libs 5 | local.properties 6 | .DS_Store 7 | .metadata 8 | .settings 9 | *~ 10 | 11 | 12 | ### Gradle template 13 | .gradle 14 | build/ 15 | 16 | # Ignore Gradle GUI config 17 | gradle-app.setting 18 | 19 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 20 | !gradle-wrapper.jar 21 | 22 | # Cache of project 23 | .gradletasknamecache 24 | 25 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 26 | # gradle/wrapper/gradle-wrapper.properties 27 | 28 | 29 | ### Android template 30 | # Built application files 31 | *.apk 32 | *.ap_ 33 | 34 | # Files for the ART/Dalvik VM 35 | *.dex 36 | 37 | # Java class files 38 | *.class 39 | 40 | # Generated files 41 | bin/ 42 | gen/ 43 | out/ 44 | 45 | # Gradle files 46 | .gradle/ 47 | 48 | # Local configuration file (sdk path, etc) 49 | 50 | # Proguard folder generated by Eclipse 51 | proguard/ 52 | 53 | # Log Files 54 | *.log 55 | 56 | # Android Studio Navigation editor temp files 57 | .navigation/ 58 | 59 | # Android Studio captures folder 60 | captures/ 61 | 62 | # Intellij 63 | *.iml 64 | .idea 65 | 66 | # Keystore files 67 | *.jks 68 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "library/src/main/jni/leveldb"] 2 | path = library/src/main/jni/leveldb 3 | url = https://github.com/Mojang/leveldb-mcpe.git 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor guidelines 2 | 3 | TODO: guidelines 4 | 5 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Maintainer 2 | 3 | @litl 4 | 5 | 6 | # Forks 7 | 8 | @zhuowei 9 | 10 | > Added first support for the leveldb-mcpe fork. 11 | 12 | @protolambda 13 | 14 | > Updated android-leveldb to a less archaic, more modern android standard. 15 | 16 | 17 | # Contributors 18 | 19 | Feel free to add yourself if you contribute. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 litl, LLC 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of the copyright holder nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | ========================== 30 | 31 | The Google LevelDB library is included as a git-submodule under the following BSD-style license: 32 | 33 | Copyright (c) 2011 The LevelDB Authors. All rights reserved. 34 | 35 | Redistribution and use in source and binary forms, with or without 36 | modification, are permitted provided that the following conditions are 37 | met: 38 | 39 | * Redistributions of source code must retain the above copyright 40 | notice, this list of conditions and the following disclaimer. 41 | * Redistributions in binary form must reproduce the above 42 | copyright notice, this list of conditions and the following disclaimer 43 | in the documentation and/or other materials provided with the 44 | distribution. 45 | * Neither the name of Google Inc. nor the names of its 46 | contributors may be used to endorse or promote products derived from 47 | this software without specific prior written permission. 48 | 49 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 52 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 53 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 55 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 59 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # leveldb-android 2 | 3 | [leveldb](https://github.com/google/leveldb) for Android. 4 | 5 | Very simple bindings for using Leveldb from android. 6 | There is an ~~excellent~~ outdated project [leveldbjni](https://github.com/fusesource/leveldbjni) 7 | out there for using leveldb from Java but seems to be a bit too much. 8 | It has a bunch of dependencies, including a code generator, 9 | which might or might not work on Android. At least it is not trivial to get started. 10 | 11 | 12 | ## Dependencies 13 | 14 | The only external dependencies are Android SDK and NDK. 15 | 16 | 17 | ## Building. 18 | 19 | 1. Clone the project with git. (Including the leveldb git-submodule, change it to a different fork if you want) 20 | 2. Open it with Android Studio. (Easiest way, but building could also be done with just gradle) 21 | 3. Build the project with gradle (if it was not already started automatically) 22 | 4. Make the project (Build > Make project) 23 | 5. Library should be ready now! 24 | 25 | 26 | ## Installation 27 | 28 | After building, include the library module in your own project like this: 29 | 30 | `your-project/settings.gradle` 31 | 32 | include ':android-leveldb' 33 | project(':android-leveldb').projectDir = new File(rootProject.projectDir, '../android-leveldb/library') 34 | 35 | This assumes that you cloned the android-leveldb git repository in the parent-folder of your-project. 36 | 37 | And add the module as dependency in `your-project/app/build.gradle`: 38 | 39 | //include .so files in the build (native libraries) 40 | compile fileTree(include: ['*.jar', '*.so'], dir: 'libs') 41 | 42 | //include the android-leveldb library (see settings.gradle) 43 | compile project(':android-leveldb') 44 | 45 | 46 | 47 | ## License 48 | 49 | Please see the [LICENSE](LICENSE) file. 50 | 51 | 52 | ## Contributing 53 | 54 | Contributions are always welcome. 55 | 56 | See [CONTRIBUTORS.md](CONTRIBUTORS.md) for a list of contributors, forks, etc. 57 | 58 | ### TODO 59 | 60 | Contributor guidelines are not set up yet. 61 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | url 'https://maven.google.com/' 6 | name 'Google' 7 | } 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.2.1' 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | maven { 18 | url 'https://maven.google.com/' 19 | name 'Google' 20 | } 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MithrilMania/android-leveldb/6e44cd947dc61064182d9cf16f5c994b48d12b9e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 08 22:03:20 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-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | 6 | defaultConfig { 7 | minSdkVersion 16 8 | targetSdkVersion 26 9 | } 10 | 11 | buildTypes { 12 | debug { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 15 | ndk { 16 | abiFilters 'armeabi-v7a' 17 | } 18 | } 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | ndk { 23 | abiFilters 'armeabi-v7a' 24 | } 25 | } 26 | } 27 | 28 | externalNativeBuild { 29 | ndkBuild { 30 | path "src/main/jni/Android.mk" 31 | } 32 | } 33 | 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(include: ['*.jar', '*.so'], dir: 'libs') 38 | testImplementation 'junit:junit:4.12' 39 | } -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in *android*/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Keep LevelDB classes, native methods may not be obfuscated! 20 | -keep public class com.litl.leveldb.** { 21 | public private protected *; 22 | } 23 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/litl/leveldb/test/DBTests.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb.test; 2 | 3 | import java.io.File; 4 | import java.io.UnsupportedEncodingException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Arrays; 7 | 8 | import android.test.AndroidTestCase; 9 | 10 | import com.litl.leveldb.DB; 11 | import com.litl.leveldb.Iterator; 12 | import com.litl.leveldb.WriteBatch; 13 | 14 | public class DBTests extends AndroidTestCase { 15 | private File mPath; 16 | private DB mDb; 17 | 18 | @Override 19 | protected void setUp() throws Exception { 20 | super.setUp(); 21 | mPath = new File(getContext().getCacheDir(), "db-tests"); 22 | mDb = new DB(mPath); 23 | mDb.open(); 24 | } 25 | 26 | @Override 27 | protected void tearDown() throws Exception { 28 | super.tearDown(); 29 | mDb.close(); 30 | DB.destroy(mPath); 31 | } 32 | 33 | private byte[] bytes(String str) { 34 | try { 35 | return str.getBytes("UTF-8"); 36 | } catch (UnsupportedEncodingException e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | 41 | public void testBasics() { 42 | mDb.put(bytes("hello"), bytes("world")); 43 | mDb.put(bytes("bye"), bytes("moon")); 44 | 45 | byte[] val = mDb.get(bytes("hello")); 46 | assertNotNull(val); 47 | assertTrue(Arrays.equals(val, bytes("world"))); 48 | 49 | val = mDb.get(bytes("bye")); 50 | assertNotNull(val); 51 | assertTrue(Arrays.equals(val, bytes("moon"))); 52 | 53 | val = mDb.get(bytes("boo")); 54 | assertNull(val); 55 | 56 | final ByteBuffer bb = ByteBuffer.allocate(20); 57 | bb.put(bytes("boo hello world")); 58 | bb.flip(); 59 | 60 | // Search for "hello" 61 | bb.position(4).limit(bb.position() + 5); 62 | val = mDb.get(bb); 63 | assertNotNull(val); 64 | assertTrue(Arrays.equals(val, bytes("world"))); 65 | 66 | // Search for "boo" 67 | bb.position(0).limit(4); 68 | bb.flip(); 69 | val = mDb.get(bb); 70 | assertNull(val); 71 | } 72 | 73 | public void testBatchAndIterator() { 74 | final String[] keys = { "foo1", "foo2", "foo3", "foo4", "foo5" }; 75 | final String[] vals = { "bar1", "bar2", "bar3", "bar4", "bar5" }; 76 | 77 | final WriteBatch batch = new WriteBatch(); 78 | try { 79 | final ByteBuffer key = ByteBuffer.allocate(10); 80 | final ByteBuffer val = ByteBuffer.allocate(10); 81 | for (int i = 0; i < keys.length; i++) { 82 | key.clear(); 83 | key.put(bytes(keys[i])); 84 | key.flip(); 85 | 86 | val.clear(); 87 | val.put(bytes(vals[i])); 88 | val.flip(); 89 | 90 | batch.put(key, val); 91 | } 92 | 93 | mDb.write(batch); 94 | } finally { 95 | batch.close(); 96 | } 97 | 98 | final Iterator iter = mDb.iterator(); 99 | try { 100 | int i = 0; 101 | for (iter.seekToFirst(); iter.isValid(); iter.next()) { 102 | assertTrue(i < keys.length); 103 | 104 | final byte[] key = iter.getKey(); 105 | final byte[] val = iter.getValue(); 106 | 107 | assertTrue(Arrays.equals(bytes(keys[i]), key)); 108 | assertTrue(Arrays.equals(bytes(vals[i]), val)); 109 | 110 | i++; 111 | } 112 | assertTrue(i == keys.length); 113 | } finally { 114 | iter.close(); 115 | } 116 | } 117 | 118 | public void testSnapshots() { 119 | mDb.put(bytes("hello"), bytes("one")); 120 | mDb.put(bytes("bye"), bytes("one")); 121 | mDb.put(bytes("hi"), bytes("one")); 122 | 123 | final DB.Snapshot snapshot = mDb.getSnapshot(); 124 | final Iterator iter = mDb.iterator(snapshot); 125 | try { 126 | mDb.put(bytes("hello"), bytes("two")); 127 | mDb.delete(bytes("bye")); 128 | 129 | assertTrue(Arrays.equals(mDb.get(snapshot, bytes("hello")), bytes("one"))); 130 | assertTrue(Arrays.equals(mDb.get(snapshot, bytes("bye")), bytes("one"))); 131 | assertTrue(Arrays.equals(mDb.get(snapshot, bytes("hi")), bytes("one"))); 132 | 133 | int i = 0; 134 | for (iter.seekToFirst(); iter.isValid(); iter.next()) { 135 | i++; 136 | assertTrue(Arrays.equals(iter.getValue(), bytes("one"))); 137 | } 138 | assertEquals(3, i); 139 | } finally { 140 | iter.close(); 141 | snapshot.close(); 142 | } 143 | 144 | assertTrue(Arrays.equals(mDb.get(bytes("hello")), bytes("two"))); 145 | assertTrue(Arrays.equals(mDb.get(bytes("hi")), bytes("one"))); 146 | assertNull(mDb.get(bytes("bye"))); 147 | } 148 | 149 | public void testSeek() { 150 | mDb.put(bytes("01"), bytes("foo")); 151 | mDb.put(bytes("02"), bytes("foo")); 152 | mDb.put(bytes("11"), bytes("foo")); 153 | mDb.put(bytes("12"), bytes("foo")); 154 | mDb.put(bytes("13"), bytes("foo")); 155 | mDb.put(bytes("21"), bytes("foo")); 156 | 157 | Iterator iter = mDb.iterator(); 158 | try { 159 | iter.seek(bytes("1")); 160 | assertTrue(iter.isValid()); 161 | assertTrue(Arrays.equals(bytes("11"), iter.getKey())); 162 | 163 | iter.seek(bytes("2")); 164 | assertTrue(iter.isValid()); 165 | assertTrue(Arrays.equals(bytes("21"), iter.getKey())); 166 | 167 | iter.seek(bytes("3")); 168 | assertFalse(iter.isValid()); 169 | } finally { 170 | iter.close(); 171 | } 172 | 173 | iter = mDb.iterator(); 174 | try { 175 | // Iterate over all values starting with "1" 176 | int i = 0; 177 | final byte[] searchByte = new byte[] { '1' }; 178 | for (iter.seek(searchByte); iter.isValid(); iter.next()) { 179 | final byte[] key = iter.getKey(); 180 | if (key[0] != searchByte[0]) { 181 | break; 182 | } 183 | 184 | i++; 185 | } 186 | 187 | assertEquals(3, i); 188 | } finally { 189 | iter.close(); 190 | } 191 | } 192 | 193 | public void testWriteBatch() { 194 | final ByteBuffer managedBuf = ByteBuffer.allocate(10); 195 | final ByteBuffer directBuf = ByteBuffer.allocateDirect(10); 196 | 197 | final WriteBatch putBatch = new WriteBatch(); 198 | try { 199 | directBuf.clear(); 200 | directBuf.put(bytes("hello")); 201 | directBuf.flip(); 202 | managedBuf.clear(); 203 | managedBuf.put(bytes("world")); 204 | managedBuf.flip(); 205 | putBatch.put(directBuf, managedBuf); 206 | 207 | managedBuf.clear(); 208 | managedBuf.put(bytes("bye")); 209 | managedBuf.flip(); 210 | directBuf.clear(); 211 | directBuf.put(bytes("moon")); 212 | directBuf.flip(); 213 | putBatch.put(managedBuf, directBuf); 214 | 215 | mDb.write(putBatch); 216 | } finally { 217 | putBatch.close(); 218 | } 219 | 220 | final Iterator iter1 = mDb.iterator(); 221 | try { 222 | iter1.seekToFirst(); 223 | assertTrue(iter1.isValid()); 224 | assertTrue(Arrays.equals(bytes("bye"), iter1.getKey())); 225 | assertTrue(Arrays.equals(bytes("moon"), iter1.getValue())); 226 | iter1.next(); 227 | assertTrue(iter1.isValid()); 228 | assertTrue(Arrays.equals(bytes("hello"), iter1.getKey())); 229 | assertTrue(Arrays.equals(bytes("world"), iter1.getValue())); 230 | iter1.next(); 231 | assertFalse(iter1.isValid()); 232 | } finally { 233 | iter1.close(); 234 | } 235 | 236 | final WriteBatch deleteBatch = new WriteBatch(); 237 | try { 238 | managedBuf.clear(); 239 | managedBuf.put(bytes("hello")); 240 | managedBuf.flip(); 241 | deleteBatch.delete(managedBuf); 242 | 243 | directBuf.clear(); 244 | directBuf.put(bytes("bye")); 245 | directBuf.flip(); 246 | deleteBatch.delete(directBuf); 247 | 248 | mDb.write(deleteBatch); 249 | } finally { 250 | deleteBatch.close(); 251 | } 252 | 253 | final Iterator iter2 = mDb.iterator(); 254 | try { 255 | iter2.seekToFirst(); 256 | assertFalse(iter2.isValid()); 257 | } finally { 258 | iter2.close(); 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/Chunk.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | import java.io.File; 4 | import java.nio.ByteBuffer; 5 | 6 | public class Chunk extends NativeObject { 7 | 8 | public Chunk(DB db, int x, int z, int dim) { 9 | super(); 10 | mPtr = nativeOpen(db.getPtr(), x, z, dim); 11 | } 12 | 13 | public int getBlock(int x, int y, int z) { 14 | return nativeGetBlock(mPtr, x, y, z); 15 | } 16 | 17 | public void close() { 18 | closeNativeObject(mPtr); 19 | } 20 | 21 | @Override 22 | protected void closeNativeObject(long ptr) { 23 | nativeClose(ptr); 24 | } 25 | 26 | private static native long nativeOpen(long dbptr, int x, int z, int dim); 27 | 28 | private static native int nativeGetBlock(long ckptr, int x, int y, int z); 29 | 30 | private static native void nativeClose(long ckPtr); 31 | 32 | { 33 | System.loadLibrary("leveldbjni"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/DB.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | import java.io.File; 4 | import java.nio.ByteBuffer; 5 | 6 | public class DB extends NativeObject { 7 | public abstract static class Snapshot extends NativeObject { 8 | Snapshot(long ptr) { 9 | super(ptr); 10 | } 11 | } 12 | 13 | private final File mPath; 14 | private boolean mDestroyOnClose = false; 15 | 16 | public DB(File path) { 17 | super(); 18 | 19 | if (path == null) { 20 | throw new NullPointerException(); 21 | } 22 | mPath = path; 23 | } 24 | 25 | public void open() { 26 | mPtr = nativeOpen(mPath.getAbsolutePath()); 27 | } 28 | 29 | @Override 30 | protected void closeNativeObject(long ptr) { 31 | nativeClose(ptr); 32 | 33 | if (mDestroyOnClose) { 34 | destroy(mPath); 35 | } 36 | } 37 | 38 | public void put(byte[] key, byte[] value) { 39 | assertOpen("Database is closed"); 40 | if (key == null) { 41 | throw new NullPointerException("key"); 42 | } 43 | if (value == null) { 44 | throw new NullPointerException("value"); 45 | } 46 | 47 | nativePut(mPtr, key, value); 48 | } 49 | 50 | public byte[] get(byte[] key) { 51 | return get(null, key); 52 | } 53 | 54 | public byte[] get(Snapshot snapshot, byte[] key) { 55 | assertOpen("Database is closed"); 56 | if (key == null) { 57 | throw new NullPointerException(); 58 | } 59 | 60 | return nativeGet(mPtr, snapshot != null ? snapshot.getPtr() : 0, key); 61 | } 62 | 63 | public byte[] get(ByteBuffer key) { 64 | return get(null, key); 65 | } 66 | 67 | public byte[] get(Snapshot snapshot, ByteBuffer key) { 68 | assertOpen("Database is closed"); 69 | if (key == null) { 70 | throw new NullPointerException(); 71 | } 72 | 73 | return nativeGet(mPtr, snapshot != null ? snapshot.getPtr() : 0, key); 74 | } 75 | 76 | public void delete(byte[] key) { 77 | assertOpen("Database is closed"); 78 | if (key == null) { 79 | throw new NullPointerException(); 80 | } 81 | 82 | nativeDelete(mPtr, key); 83 | } 84 | 85 | public void write(WriteBatch batch) { 86 | assertOpen("Database is closed"); 87 | if (batch == null) { 88 | throw new NullPointerException(); 89 | } 90 | 91 | nativeWrite(mPtr, batch.getPtr()); 92 | } 93 | 94 | public Iterator iterator() { 95 | return iterator(null); 96 | } 97 | 98 | public Iterator iterator(final Snapshot snapshot) { 99 | assertOpen("Database is closed"); 100 | 101 | ref(); 102 | 103 | if (snapshot != null) { 104 | snapshot.ref(); 105 | } 106 | 107 | return new Iterator(nativeIterator(mPtr, snapshot != null ? snapshot.getPtr() : 0)) { 108 | @Override 109 | protected void closeNativeObject(long ptr) { 110 | super.closeNativeObject(ptr); 111 | if (snapshot != null) { 112 | snapshot.unref(); 113 | } 114 | 115 | DB.this.unref(); 116 | } 117 | }; 118 | } 119 | 120 | public Snapshot getSnapshot() { 121 | assertOpen("Database is closed"); 122 | ref(); 123 | return new Snapshot(nativeGetSnapshot(mPtr)) { 124 | protected void closeNativeObject(long ptr) { 125 | nativeReleaseSnapshot(DB.this.getPtr(), getPtr()); 126 | DB.this.unref(); 127 | } 128 | }; 129 | } 130 | 131 | public void destroy() { 132 | mDestroyOnClose = true; 133 | if (getPtr() == 0) { 134 | destroy(mPath); 135 | } 136 | } 137 | 138 | public static void destroy(File path) { 139 | nativeDestroy(path.getAbsolutePath()); 140 | } 141 | 142 | private static native long nativeOpen(String dbpath); 143 | 144 | private static native void nativeClose(long dbPtr); 145 | 146 | private static native void nativePut(long dbPtr, byte[] key, byte[] value); 147 | 148 | private static native byte[] nativeGet(long dbPtr, long snapshotPtr, byte[] key); 149 | 150 | private static native byte[] nativeGet(long dbPtr, long snapshotPtr, ByteBuffer key); 151 | 152 | private static native void nativeDelete(long dbPtr, byte[] key); 153 | 154 | private static native void nativeWrite(long dbPtr, long batchPtr); 155 | 156 | private static native void nativeDestroy(String dbpath); 157 | 158 | private static native long nativeIterator(long dbPtr, long snapshotPtr); 159 | 160 | private static native long nativeGetSnapshot(long dbPtr); 161 | 162 | private static native void nativeReleaseSnapshot(long dbPtr, long snapshotPtr); 163 | 164 | public static native String stringFromJNI(); 165 | 166 | { 167 | System.loadLibrary("leveldbjni"); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/DatabaseCorruptException.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | public class DatabaseCorruptException extends LevelDBException { 4 | private static final long serialVersionUID = -2110293580518875321L; 5 | 6 | public DatabaseCorruptException() { 7 | } 8 | 9 | public DatabaseCorruptException(String error) { 10 | super(error); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/Iterator.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | public class Iterator extends NativeObject { 4 | Iterator(long iterPtr) { 5 | super(iterPtr); 6 | } 7 | 8 | @Override 9 | protected void closeNativeObject(long ptr) { 10 | nativeDestroy(ptr); 11 | } 12 | 13 | public void seekToFirst() { 14 | assertOpen("Iterator is closed"); 15 | nativeSeekToFirst(mPtr); 16 | } 17 | 18 | public void seekToLast() { 19 | assertOpen("Iterator is closed"); 20 | nativeSeekToLast(mPtr); 21 | } 22 | 23 | public void seek(byte[] target) { 24 | assertOpen("Iterator is closed"); 25 | if (target == null) { 26 | throw new IllegalArgumentException(); 27 | } 28 | nativeSeek(mPtr, target); 29 | } 30 | 31 | public boolean isValid() { 32 | assertOpen("Iterator is closed"); 33 | return nativeValid(mPtr); 34 | } 35 | 36 | public void next() { 37 | assertOpen("Iterator is closed"); 38 | nativeNext(mPtr); 39 | } 40 | 41 | public void prev() { 42 | assertOpen("Iterator is closed"); 43 | nativePrev(mPtr); 44 | } 45 | 46 | public byte[] getKey() { 47 | assertOpen("Iterator is closed"); 48 | return nativeKey(mPtr); 49 | } 50 | 51 | public byte[] getValue() { 52 | assertOpen("Iterator is closed"); 53 | return nativeValue(mPtr); 54 | } 55 | 56 | private static native void nativeDestroy(long ptr); 57 | 58 | private static native void nativeSeekToFirst(long ptr); 59 | 60 | private static native void nativeSeekToLast(long ptr); 61 | 62 | private static native void nativeSeek(long ptr, byte[] key); 63 | 64 | private static native boolean nativeValid(long ptr); 65 | 66 | private static native void nativeNext(long ptr); 67 | 68 | private static native void nativePrev(long ptr); 69 | 70 | private static native byte[] nativeKey(long dbPtr); 71 | 72 | private static native byte[] nativeValue(long dbPtr); 73 | } 74 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/LevelDBException.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | public class LevelDBException extends RuntimeException { 4 | private static final long serialVersionUID = 2903013251786326801L; 5 | 6 | public LevelDBException() { 7 | } 8 | 9 | public LevelDBException(String error) { 10 | super(error); 11 | } 12 | 13 | public LevelDBException(String error, Throwable cause) { 14 | super(error, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/NativeObject.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | import java.io.Closeable; 4 | 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | 8 | abstract class NativeObject implements Closeable { 9 | private static final String TAG = NativeObject.class.getSimpleName(); 10 | protected long mPtr; 11 | private int mRefCount = 0; 12 | 13 | protected NativeObject() { 14 | // The Java wrapper counts as one reference, will 15 | // be released when closed 16 | ref(); 17 | } 18 | 19 | protected NativeObject(long ptr) { 20 | this(); 21 | 22 | if (ptr == 0) { 23 | throw new OutOfMemoryError("Failed to allocate native object"); 24 | } 25 | 26 | mPtr = ptr; 27 | } 28 | 29 | synchronized protected long getPtr() { 30 | return mPtr; 31 | } 32 | 33 | synchronized public boolean isClosed(){ 34 | return getPtr() == 0; 35 | } 36 | 37 | protected void assertOpen(String message) { 38 | if (getPtr() == 0) { 39 | throw new IllegalStateException(message); 40 | } 41 | } 42 | 43 | synchronized void ref() { 44 | mRefCount++; 45 | } 46 | 47 | synchronized void unref() { 48 | if (mRefCount <= 0) { 49 | throw new IllegalStateException("Reference count is already 0"); 50 | } 51 | 52 | mRefCount--; 53 | 54 | if (mRefCount == 0) { 55 | closeNativeObject(mPtr); 56 | mPtr = 0; 57 | } 58 | } 59 | 60 | protected abstract void closeNativeObject(long ptr); 61 | 62 | @Override 63 | public synchronized void close() { 64 | if (mPtr != 0) { 65 | unref(); 66 | } 67 | } 68 | 69 | @Override 70 | protected void finalize() throws Throwable { 71 | if (mPtr != 0) { 72 | Class clazz = getClass(); 73 | String name = clazz.getSimpleName(); 74 | while (TextUtils.isEmpty(name)) { 75 | clazz = clazz.getSuperclass(); 76 | name = clazz.getSimpleName(); 77 | } 78 | 79 | Log.w(TAG, "NativeObject " + name + " refcount: " + mRefCount 80 | + " id: " + System.identityHashCode(this) 81 | + " was finalized before native resource was closed, did you forget to call close()?"); 82 | } 83 | 84 | super.finalize(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | public class NotFoundException extends LevelDBException { 4 | private static final long serialVersionUID = 6207999645579440001L; 5 | 6 | public NotFoundException() { 7 | } 8 | 9 | public NotFoundException(String error) { 10 | super(error); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /library/src/main/java/com/litl/leveldb/WriteBatch.java: -------------------------------------------------------------------------------- 1 | package com.litl.leveldb; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class WriteBatch extends NativeObject { 6 | public WriteBatch() { 7 | super(nativeCreate()); 8 | } 9 | 10 | @Override 11 | protected void closeNativeObject(long ptr) { 12 | nativeDestroy(ptr); 13 | } 14 | 15 | public void delete(ByteBuffer key) { 16 | assertOpen("WriteBatch is closed"); 17 | if (key == null) { 18 | throw new NullPointerException("key"); 19 | } 20 | 21 | nativeDelete(mPtr, key); 22 | } 23 | 24 | public void put(ByteBuffer key, ByteBuffer value) { 25 | assertOpen("WriteBatch is closed"); 26 | if (key == null) { 27 | throw new NullPointerException("key"); 28 | } 29 | if (value == null) { 30 | throw new NullPointerException("value"); 31 | } 32 | 33 | nativePut(mPtr, key, value); 34 | } 35 | 36 | public void clear() { 37 | assertOpen("WriteBatch is closed"); 38 | nativeClear(mPtr); 39 | } 40 | 41 | private static native long nativeCreate(); 42 | 43 | private static native void nativeDestroy(long ptr); 44 | 45 | private static native void nativeDelete(long ptr, ByteBuffer key); 46 | 47 | private static native void nativePut(long ptr, ByteBuffer key, ByteBuffer val); 48 | 49 | private static native void nativeClear(long ptr); 50 | } 51 | -------------------------------------------------------------------------------- /library/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := leveldbjni 5 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/leveldb/include $(LOCAL_PATH)/leveldb/include/leveldb 6 | LOCAL_CPP_EXTENSION := .cc 7 | LOCAL_CFLAGS := -DLEVELDB_PLATFORM_ANDROID -std=gnu++11 -DDLLX= 8 | LOCAL_SRC_FILES := com_litl_leveldb_DB.cc com_litl_leveldb_Iterator.cc\ 9 | com_litl_leveldb_WriteBatch.cc leveldbjni.cc com_litl_leveldb_Chunk.cc\ 10 | subchunk.cc Chunk.cc blocknames.cc 11 | LOCAL_STATIC_LIBRARIES += leveldb 12 | LOCAL_LDLIBS += -llog -ldl -lz 13 | 14 | include $(BUILD_SHARED_LIBRARY) 15 | 16 | 17 | 18 | include $(CLEAR_VARS) 19 | LOCAL_MODULE := leveldb 20 | LOCAL_CFLAGS := -O3 -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX -DNDEBUG -std=gnu++11 -DDLLX= 21 | LOCAL_CPP_EXTENSION := .cc 22 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/leveldb $(LOCAL_PATH)/leveldb/include $(LOCAL_PATH)/leveldb/include/leveldb 23 | LOCAL_SRC_FILES := leveldb/db/builder.cc leveldb/db/c.cc leveldb/db/dbformat.cc leveldb/db/db_impl.cc leveldb/db/db_iter.cc leveldb/db/dumpfile.cc leveldb/db/filename.cc leveldb/db/log_reader.cc leveldb/db/log_writer.cc leveldb/db/memtable.cc leveldb/db/repair.cc leveldb/db/table_cache.cc leveldb/db/version_edit.cc leveldb/db/version_set.cc leveldb/db/write_batch.cc leveldb/db/zlib_compressor.cc leveldb/table/block_builder.cc leveldb/table/block.cc leveldb/table/filter_block.cc leveldb/table/format.cc leveldb/table/iterator.cc leveldb/table/merger.cc leveldb/table/table_builder.cc leveldb/table/table.cc leveldb/table/two_level_iterator.cc leveldb/util/arena.cc leveldb/util/bloom.cc leveldb/util/cache.cc leveldb/util/coding.cc leveldb/util/comparator.cc leveldb/util/crc32c.cc leveldb/util/env_boost.cc leveldb/util/env.cc leveldb/util/env_posix.cc leveldb/util/env_win.cc leveldb/util/filter_policy.cc leveldb/util/hash.cc leveldb/util/histogram.cc leveldb/util/logging.cc leveldb/util/options.cc leveldb/util/status.cc leveldb/util/win_logger.cc leveldb/port/port_posix.cc leveldb/port/port_posix_sse.cc 24 | 25 | include $(BUILD_STATIC_LIBRARY) -------------------------------------------------------------------------------- /library/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM=android-16 2 | APP_ABI := armeabi-v7a x86 x86_64 arm64-v8a 3 | APP_STL := c++_static 4 | -------------------------------------------------------------------------------- /library/src/main/jni/Chunk.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/23. 3 | // 4 | 5 | 6 | #include 7 | #include "Chunk.h" 8 | #include "mapkey.h" 9 | #include "qstr.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef LOG_CHUNK_LOADSAVE 15 | #define LOGE_LS(x, ...) LOGE(CAT("Chunk: ", x), ##__VA_ARGS__); 16 | #else 17 | #define LOGE_LS(x, ...) 18 | #endif 19 | 20 | #ifdef LOG_CHUNK_OPERATION 21 | #define LOGE_OP(x, ...) LOGE(CAT("Chunk: ", x), ##__VA_ARGS__); 22 | #else 23 | #define LOGE_OP(x, ...) 24 | #endif 25 | 26 | //Init constants. 27 | 28 | const int32_t Chunk::msk[] = {0b1, 0b11, 0b111, 0b1111, 0b11111, 0b111111, 0b1111111, 29 | 0b11111111, 30 | 0b111111111, 0b1111111111, 0b11111111111, 31 | 0b111111111111, 32 | 0b1111111111111, 0b11111111111111, 0b11111111111111}; 33 | 34 | const char Chunk::pattern_name[] = {0x0a, 0x00, 0x00, 0x08, 0x04, 0x00, 'n', 'a', 'm', 35 | 'e'}; 36 | 37 | const char Chunk::pattern_val[] = {0x02, 0x03, 0x00, 'v', 'a', 'l'}; 38 | 39 | Chunk::Chunk(leveldb::DB *db, mapkey_t key) 40 | : key(key) { 41 | std::string str; 42 | memset(subchunks, 0, sizeof(subchunks)); 43 | for (int i = 0; i < 16; i++) { 44 | loadSubChunk(db, i); 45 | } 46 | } 47 | 48 | void Chunk::loadSubChunk(leveldb::DB *db, unsigned char which) { 49 | LDBKEY_SUBCHUNK(this->key, which) 50 | leveldb::Slice slice(key, 0 == this->key.dimension ? 10 : 14); 51 | std::string val; 52 | leveldb::ReadOptions readOptions; 53 | readOptions.decompress_allocator = new leveldb::DecompressAllocator; 54 | bool hit = db->Get(readOptions, slice, &val).ok(); 55 | if (hit) {//Found, detect version. 56 | switch (val[0]) { 57 | case 0://Aligned subchunk. 58 | case 2://All 59 | case 3://The 60 | case 4://Same 61 | case 5://Really 62 | case 6://Wierd 63 | case 7://Ahhh 64 | break; 65 | case 8://Current paletted format 66 | subchunks[which] = new SubChunk(val, true); 67 | break; 68 | case 1: //Previous paletted version 69 | subchunks[which] = new SubChunk(val, false); 70 | break; 71 | default: 72 | //Unsupported format. 73 | break; 74 | } 75 | } 76 | } 77 | 78 | uint16_t Chunk::getBlock(unsigned char x, unsigned char y, unsigned char z) { 79 | unsigned char sub = y >> 4; 80 | if (subchunks[sub] == nullptr)return 0; 81 | return subchunks[sub]->getBlock(x, static_cast(y & 0xf), z); 82 | } 83 | 84 | Chunk::~Chunk() { 85 | for (int i = 0; i < 16; i++) { 86 | delete subchunks[i]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /library/src/main/jni/Chunk.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/23. 3 | // 4 | 5 | #ifndef CONVARTER_CHUNK_H 6 | #define CONVARTER_CHUNK_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "mapkey.h" 13 | #include "debug_conf.h" 14 | #include "qstr.h" 15 | 16 | class Chunk { 17 | private: 18 | 19 | //Constants. 20 | 21 | static const int32_t msk[]; 22 | 23 | static const char pattern_name[]; 24 | 25 | static const char pattern_val[]; 26 | 27 | //Member vars. 28 | 29 | mapkey_t key; 30 | 31 | SubChunk *subchunks[16]; 32 | 33 | void loadSubChunk(leveldb::DB *db, unsigned char which); 34 | 35 | public: 36 | 37 | Chunk(leveldb::DB *db, mapkey_t key); 38 | 39 | ~Chunk(); 40 | 41 | uint16_t getBlock(unsigned char x, unsigned char y, unsigned char z); 42 | }; 43 | 44 | #endif //CONVARTER_CHUNK_H 45 | -------------------------------------------------------------------------------- /library/src/main/jni/blocknames.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/30. 3 | // 4 | 5 | #include 6 | #include 7 | #include "blocknames.h" 8 | 9 | char BlockNames::names[256][32] = {"air", 10 | "stone", 11 | "grass", 12 | "dirt", 13 | "cobblestone", 14 | "planks", 15 | "sapling", 16 | "bedrock", 17 | "flowing_water", 18 | "water", 19 | "flowing_lava", 20 | "lava", 21 | "sand", 22 | "gravel", 23 | "gold_ore", 24 | "iron_ore", 25 | "coal_ore", 26 | "log", 27 | "leaves", 28 | "sponge", 29 | "glass", 30 | "lapis_ore", 31 | "lapis_block", 32 | "dispenser", 33 | "sandstone", 34 | "noteblock", 35 | "bed", 36 | "golden_rail", 37 | "detector_rail", 38 | "sticky_piston", 39 | "web", 40 | "tallgrass", 41 | "deadbush", 42 | "piston", 43 | "pistonarmcollision", 44 | "wool", 45 | "air", 46 | "yellow_flower", 47 | "red_flower", 48 | "brown_mushroom", 49 | "red_mushroom", 50 | "gold_block", 51 | "iron_block", 52 | "double_stone_slab", 53 | "stone_slab", 54 | "brick_block", 55 | "tnt", 56 | "bookshelf", 57 | "mossy_cobblestone", 58 | "obsidian", 59 | "torch", 60 | "fire", 61 | "mob_spawner", 62 | "oak_stairs", 63 | "chest", 64 | "redstone_wire", 65 | "diamond_ore", 66 | "diamond_block", 67 | "crafting_table", 68 | "wheat", 69 | "farmland", 70 | "furnace", 71 | "lit_furnace", 72 | "standing_sign", 73 | "wooden_door", 74 | "ladder", 75 | "rail", 76 | "stone_stairs", 77 | "wall_sign", 78 | "lever", 79 | "stone_pressure_plate", 80 | "iron_door", 81 | "oak_pressure_plate", 82 | "redstone_ore", 83 | "lit_redstone_ore", 84 | "unlit_redstone_torch", 85 | "lit_redstone_torch", 86 | "stone_button", 87 | "snow_layer", 88 | "ice", 89 | "snow", 90 | "cactus", 91 | "clay", 92 | "reeds", 93 | "jukebox", 94 | "fence", 95 | "pumpkin", 96 | "netherrack", 97 | "soul_sand", 98 | "glowstone", 99 | "portal", 100 | "lit_pumpkin", 101 | "cake", 102 | "unpowered_repeater", 103 | "powered_repeater", 104 | "invisiblebedrock", 105 | "oak_trapdoor", 106 | "monster_egg", 107 | "stonebrick", 108 | "brown_mushroom_block", 109 | "red_mushroom_block", 110 | "iron_bars", 111 | "glass_pane", 112 | "melon_block", 113 | "pumpkin_stem", 114 | "melon_stem", 115 | "vine", 116 | "fence_gate", 117 | "brick_stairs", 118 | "stone_brick_stairs", 119 | "mycelium", 120 | "waterlily", 121 | "nether_brick", 122 | "nether_brick_fence", 123 | "nether_brick_stairs", 124 | "nether_wart", 125 | "enchanting_table", 126 | "brewing_stand", 127 | "cauldron", 128 | "end_portal", 129 | "end_portal_frame", 130 | "end_stone", 131 | "dragon_egg", 132 | "redstone_lamp", 133 | "lit_redstone_lamp", 134 | "dropper", 135 | "activator_rail", 136 | "cocoa", 137 | "sandstone_stairs", 138 | "emerald_ore", 139 | "ender_chest", 140 | "tripwire_hook", 141 | "tripwire", 142 | "emerald_block", 143 | "spruce_stairs", 144 | "birch_stairs", 145 | "jungle_stairs", 146 | "command_block", 147 | "beacon", 148 | "cobblestone_wall", 149 | "flower_pot", 150 | "carrots", 151 | "potatoes", 152 | "oak_button", 153 | "skull", 154 | "anvil", 155 | "trapped_chest", 156 | "light_weighted_pressure_plate", 157 | "heavy_weighted_pressure_plate", 158 | "unpowered_comparator", 159 | "powered_comparator", 160 | "daylight_detector", 161 | "redstone_block", 162 | "quartz_ore", 163 | "hopper", 164 | "quartz_block", 165 | "quartz_stairs", 166 | "double_wooden_slab", 167 | "wooden_slab", 168 | "stained_hardened_clay", 169 | "stained_glass_pane", 170 | "leaves2", 171 | "log2", 172 | "acacia_stairs", 173 | "dark_oak_stairs", 174 | "slime", 175 | "air", 176 | "iron_trapdoor", 177 | "prismarine", 178 | "sealantern", 179 | "hay_block", 180 | "carpet", 181 | "terracotta", 182 | "coal_block", 183 | "packed_ice", 184 | "double_plant", 185 | "standing_banner", 186 | "wall_banner", 187 | "daylight_detector_inverted", 188 | "red_sandstone", 189 | "red_sandstone_stairs", 190 | "double_stone_slab2", 191 | "stone_slab2", 192 | "spruce_fence_gate", 193 | "birch_fence_gate", 194 | "jungle_fence_gate", 195 | "dark_oak_fence_gate", 196 | "acacia_fence_gate", 197 | "repeating_command_block", 198 | "chain_command_block", 199 | "air", 200 | "air", 201 | "air", 202 | "spruce_door", 203 | "birch_door", 204 | "jungle_door", 205 | "acacia_door", 206 | "dark_oak_door", 207 | "grass_path", 208 | "frame", 209 | "chorus_flower", 210 | "purpur_block", 211 | "purpur_stairs", 212 | "air", 213 | "air", 214 | "undyed_shulker_box", 215 | "end_bricks", 216 | "frosted_ice", 217 | "end_rod", 218 | "end_gateway", 219 | "air", 220 | "air", 221 | "air", 222 | "magma", 223 | "nether_wart_block", 224 | "red_nether_brick", 225 | "bone_block", 226 | "air", 227 | "shulker_box", 228 | "purple_glazed_terracotta", 229 | "white_glazed_terracotta", 230 | "orange_glazed_terracotta", 231 | "magenta_glazed_terracotta", 232 | "light_blue_glazed_terracotta", 233 | "yellow_glazed_terracotta", 234 | "lime_glazed_terracotta", 235 | "pink_glazed_terracotta", 236 | "gray_glazed_terracotta", 237 | "silver_glazed_terracotta", 238 | "cyan_glazed_terracotta", 239 | "air", 240 | "blue_glazed_terracotta", 241 | "brown_glazed_terracotta", 242 | "green_glazed_terracotta", 243 | "red_glazed_terracotta", 244 | "black_glazed_terracotta", 245 | "concrete", 246 | "concretepowder", 247 | "air", 248 | "air", 249 | "chorus_plant", 250 | "stained_glass", 251 | "air", 252 | "podzol", 253 | "beetroot", 254 | "stonecutter", 255 | "glowingobsidian", 256 | "netherreactor", 257 | "info_update", 258 | "info_update2", 259 | "movingblock", 260 | "observer", 261 | "structure_block", 262 | "air", 263 | "air", 264 | "reserved6"}; 265 | 266 | unsigned char BlockNames::resolve(qstr name) { 267 | if (name.str[9] == ':') { 268 | name.str += 10; 269 | name.length -= 10; 270 | } 271 | for (int i = 0; i < 256; i++) { 272 | char *nam = names[i]; 273 | if (memcmp(nam, name.str, name.length) != 0)continue; 274 | return static_cast(i); 275 | } 276 | return 0; 277 | } 278 | -------------------------------------------------------------------------------- /library/src/main/jni/blocknames.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/29. 3 | // 4 | 5 | #ifndef CONVARTER_BLOCKNAMES_H 6 | #define CONVARTER_BLOCKNAMES_H 7 | 8 | #include "qstr.h" 9 | 10 | class BlockNames { 11 | public: 12 | static char names[256][32]; 13 | 14 | static unsigned char resolve(qstr name); 15 | }; 16 | 17 | #endif //CONVARTER_BLOCKNAMES_H 18 | -------------------------------------------------------------------------------- /library/src/main/jni/com_litl_leveldb_Chunk.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "leveldbjni.h" 10 | #include "Chunk.h" 11 | 12 | static jlong nativeOpen(JNIEnv *env, jclass clazz, jlong dbptr, jint x, jint z, jint dim) { 13 | leveldb::DB *db = reinterpret_cast(dbptr); 14 | Chunk *chunk = new Chunk(db, LDBKEY_STRUCT(x, z, dim)); 15 | return reinterpret_cast(chunk); 16 | } 17 | 18 | static jint nativeGetBlock(JNIEnv *env, jclass clazz, jlong ckptr, jint x, jint y, jint z) { 19 | Chunk *chunk = reinterpret_cast(ckptr); 20 | return chunk->getBlock(static_cast(x), static_cast(y), 21 | static_cast(z)); 22 | } 23 | 24 | static void nativeClose(JNIEnv *env, jclass clazz, jlong ckptr) { 25 | Chunk *chunk = reinterpret_cast(ckptr); 26 | delete chunk; 27 | } 28 | 29 | static JNINativeMethod sMethods[] = 30 | { 31 | {"nativeOpen", "(JIII)J", (void *) nativeOpen}, 32 | {"nativeGetBlock", "(JIII)I", (void *) nativeGetBlock}, 33 | {"nativeClose", "(J)V", (void *) nativeClose} 34 | }; 35 | 36 | int 37 | register_com_litl_leveldb_Chunk(JNIEnv *env) { 38 | jclass clazz = env->FindClass("com/litl/leveldb/Chunk"); 39 | if (!clazz) { 40 | LOGE("Can't find class com.litl.leveldb.Chunk"); 41 | return 0; 42 | } 43 | 44 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 45 | } 46 | -------------------------------------------------------------------------------- /library/src/main/jni/com_litl_leveldb_DB.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "leveldbjni.h" 9 | 10 | #include "leveldb/db.h" 11 | #include "leveldb/write_batch.h" 12 | #include "leveldb/zlib_compressor.h" 13 | #include "leveldb/filter_policy.h" 14 | #include "leveldb/cache.h" 15 | #include "leveldb/options.h" 16 | #include "leveldb/decompress_allocator.h" 17 | #include "leveldb/env.h" 18 | 19 | static jmethodID gByteBuffer_isDirectMethodID; 20 | static jmethodID gByteBuffer_positionMethodID; 21 | static jmethodID gByteBuffer_limitMethodID; 22 | static jmethodID gByteBuffer_arrayMethodID; 23 | 24 | static leveldb::ZlibCompressor* zlibCompressorInstance; 25 | 26 | class NullLogger : public leveldb::Logger { 27 | public: 28 | void Logv(const char*, va_list) override { 29 | } 30 | }; 31 | 32 | static jlong 33 | nativeOpen(JNIEnv* env, 34 | jclass clazz, 35 | jstring dbpath) 36 | { 37 | static bool gInited; 38 | 39 | if (!gInited) { 40 | jclass byteBuffer_Clazz = env->FindClass("java/nio/ByteBuffer"); 41 | gByteBuffer_isDirectMethodID = env->GetMethodID(byteBuffer_Clazz, 42 | "isDirect", "()Z"); 43 | gByteBuffer_positionMethodID = env->GetMethodID(byteBuffer_Clazz, 44 | "position", "()I"); 45 | gByteBuffer_limitMethodID = env->GetMethodID(byteBuffer_Clazz, 46 | "limit", "()I"); 47 | gByteBuffer_arrayMethodID = env->GetMethodID(byteBuffer_Clazz, 48 | "array", "()[B"); 49 | gInited = true; 50 | } 51 | 52 | const char *path = env->GetStringUTFChars(dbpath, 0); 53 | LOGI("Opening database %s", path); 54 | 55 | leveldb::DB* db; 56 | leveldb::Options options; 57 | options.create_if_missing = true; 58 | options.paranoid_checks = true; 59 | if (zlibCompressorInstance == NULL) { 60 | zlibCompressorInstance = new leveldb::ZlibCompressor(); 61 | } 62 | 63 | //create a bloom filter to quickly tell if a key is in the database or not 64 | options.filter_policy = leveldb::NewBloomFilterPolicy(10); 65 | 66 | //create a 40 mb cache (we use this on ~1gb devices) 67 | options.block_cache = leveldb::NewLRUCache(40 * 1024 * 1024); 68 | 69 | //create a 4mb write buffer, to improve compression and touch the disk less 70 | options.write_buffer_size = 4 * 1024 * 1024; 71 | 72 | //disable internal logging. The default logger will still print out things to a file 73 | options.info_log = new NullLogger(); 74 | 75 | //use the new raw-zip compressor to write (and read) 76 | options.compressors[0] = new leveldb::ZlibCompressorRaw(-1); 77 | 78 | options.compressors[1] = zlibCompressorInstance; 79 | leveldb::Status status = leveldb::DB::Open(options, path, &db); 80 | env->ReleaseStringUTFChars(dbpath, path); 81 | 82 | if (!status.ok()) { 83 | throwException(env, status); 84 | } else { 85 | LOGI("Opened database"); 86 | } 87 | 88 | return reinterpret_cast(db); 89 | } 90 | 91 | static void 92 | nativeClose(JNIEnv* env, 93 | jclass clazz, 94 | jlong dbPtr) 95 | { 96 | leveldb::DB* db = reinterpret_cast(dbPtr); 97 | if (db) { 98 | delete db; 99 | } 100 | 101 | LOGI("Database closed"); 102 | } 103 | 104 | static jbyteArray 105 | nativeGet(JNIEnv * env, 106 | jclass clazz, 107 | jlong dbPtr, 108 | jlong snapshotPtr, 109 | jbyteArray keyObj) 110 | { 111 | leveldb::DB* db = reinterpret_cast(dbPtr); 112 | leveldb::ReadOptions options = leveldb::ReadOptions(); 113 | options.decompress_allocator = new leveldb::DecompressAllocator(); 114 | options.snapshot = reinterpret_cast(snapshotPtr); 115 | 116 | size_t keyLen = env->GetArrayLength(keyObj); 117 | jbyte *buffer = env->GetByteArrayElements(keyObj, NULL); 118 | jbyteArray result; 119 | 120 | leveldb::Slice key = leveldb::Slice((const char *)buffer, keyLen); 121 | leveldb::Iterator* iter = db->NewIterator(options); 122 | iter->Seek(key); 123 | if (iter->Valid() && key == iter->key()) { 124 | leveldb::Slice value = iter->value(); 125 | size_t len = value.size(); 126 | result = env->NewByteArray(len); 127 | env->SetByteArrayRegion(result, 0, len, (const jbyte *) value.data()); 128 | } else { 129 | result = NULL; 130 | } 131 | 132 | env->ReleaseByteArrayElements(keyObj, buffer, JNI_ABORT); 133 | delete iter; 134 | 135 | return result; 136 | } 137 | 138 | static jbyteArray 139 | nativeGetBB(JNIEnv * env, 140 | jclass clazz, 141 | jlong dbPtr, 142 | jlong snapshotPtr, 143 | jobject keyObj) 144 | { 145 | leveldb::DB* db = reinterpret_cast(dbPtr); 146 | leveldb::ReadOptions options = leveldb::ReadOptions(); 147 | options.snapshot = reinterpret_cast(snapshotPtr); 148 | 149 | jint keyPos = env->CallIntMethod(keyObj, gByteBuffer_positionMethodID); 150 | jint keyLimit = env->CallIntMethod(keyObj, gByteBuffer_limitMethodID); 151 | jboolean keyIsDirect = env->CallBooleanMethod(keyObj, gByteBuffer_isDirectMethodID); 152 | jbyteArray keyArray; 153 | void* key; 154 | if (keyIsDirect) { 155 | key = env->GetDirectBufferAddress(keyObj); 156 | keyArray = NULL; 157 | } else { 158 | keyArray = (jbyteArray) env->CallObjectMethod(keyObj, gByteBuffer_arrayMethodID); 159 | key = (void*) env->GetByteArrayElements(keyArray, NULL); 160 | } 161 | 162 | jbyteArray result; 163 | leveldb::Slice keySlice = leveldb::Slice((const char *) key + keyPos, keyLimit - keyPos); 164 | leveldb::Iterator* iter = db->NewIterator(options); 165 | iter->Seek(keySlice); 166 | if (iter->Valid() && keySlice == iter->key()) { 167 | leveldb::Slice value = iter->value(); 168 | size_t len = value.size(); 169 | result = env->NewByteArray(len); 170 | env->SetByteArrayRegion(result, 0, len, (const jbyte *) value.data()); 171 | } else { 172 | result = NULL; 173 | } 174 | 175 | if (keyArray) { 176 | env->ReleaseByteArrayElements(keyArray, (jbyte*) key, JNI_ABORT); 177 | } 178 | 179 | delete iter; 180 | 181 | return result; 182 | } 183 | 184 | static void 185 | nativePut(JNIEnv *env, 186 | jclass clazz, 187 | jlong dbPtr, 188 | jbyteArray keyObj, 189 | jbyteArray valObj) 190 | { 191 | leveldb::DB* db = reinterpret_cast(dbPtr); 192 | 193 | size_t keyLen = env->GetArrayLength(keyObj); 194 | jbyte *keyBuf = env->GetByteArrayElements(keyObj, NULL); 195 | 196 | size_t valLen = env->GetArrayLength(valObj); 197 | jbyte *valBuf = env->GetByteArrayElements(valObj, NULL); 198 | 199 | leveldb::Status status = db->Put(leveldb::WriteOptions(), 200 | leveldb::Slice((const char *) keyBuf, keyLen), 201 | leveldb::Slice((const char *) valBuf, valLen)); 202 | 203 | env->ReleaseByteArrayElements(keyObj, keyBuf, JNI_ABORT); 204 | env->ReleaseByteArrayElements(valObj, valBuf, JNI_ABORT); 205 | 206 | if (!status.ok()) { 207 | throwException(env, status); 208 | } 209 | } 210 | 211 | static void 212 | nativeDelete(JNIEnv *env, 213 | jclass clazz, 214 | jlong dbPtr, 215 | jbyteArray keyObj) 216 | { 217 | leveldb::DB* db = reinterpret_cast(dbPtr); 218 | 219 | size_t keyLen = env->GetArrayLength(keyObj); 220 | jbyte *buffer = env->GetByteArrayElements(keyObj, NULL); 221 | 222 | leveldb::Status status = db->Delete(leveldb::WriteOptions(), leveldb::Slice((const char *) buffer, keyLen)); 223 | env->ReleaseByteArrayElements(keyObj, buffer, JNI_ABORT); 224 | 225 | if (!status.ok()) { 226 | throwException(env, status); 227 | } 228 | } 229 | 230 | static void 231 | nativeWrite(JNIEnv *env, 232 | jclass clazz, 233 | jlong dbPtr, 234 | jlong batchPtr) 235 | { 236 | leveldb::DB* db = reinterpret_cast(dbPtr); 237 | 238 | leveldb::WriteBatch *batch = (leveldb::WriteBatch *) batchPtr; 239 | leveldb::Status status = db->Write(leveldb::WriteOptions(), batch); 240 | if (!status.ok()) { 241 | throwException(env, status); 242 | } 243 | } 244 | 245 | static jlong 246 | nativeIterator(JNIEnv* env, 247 | jclass clazz, 248 | jlong dbPtr, 249 | jlong snapshotPtr) 250 | { 251 | leveldb::DB* db = reinterpret_cast(dbPtr); 252 | leveldb::ReadOptions options = leveldb::ReadOptions(); 253 | options.snapshot = reinterpret_cast(snapshotPtr); 254 | 255 | leveldb::Iterator *iter = db->NewIterator(options); 256 | return reinterpret_cast(iter); 257 | } 258 | 259 | static jlong 260 | nativeGetSnapshot(JNIEnv *env, 261 | jclass clazz, 262 | jlong dbPtr) 263 | { 264 | leveldb::DB* db = reinterpret_cast(dbPtr); 265 | const leveldb::Snapshot* snapshot = db->GetSnapshot(); 266 | return reinterpret_cast(snapshot); 267 | } 268 | 269 | static void 270 | nativeReleaseSnapshot(JNIEnv *env, 271 | jclass clazz, 272 | jlong dbPtr, 273 | jlong snapshotPtr) 274 | { 275 | leveldb::DB* db = reinterpret_cast(dbPtr); 276 | const leveldb::Snapshot *snapshot = reinterpret_cast(snapshotPtr); 277 | db->ReleaseSnapshot(snapshot); 278 | } 279 | 280 | static void 281 | nativeDestroy(JNIEnv *env, 282 | jclass clazz, 283 | jstring dbpath) 284 | { 285 | const char* path = env->GetStringUTFChars(dbpath,0); 286 | leveldb::Options options; 287 | options.create_if_missing = true; 288 | leveldb::Status status = DestroyDB(path, options); 289 | if (!status.ok()) { 290 | throwException(env, status); 291 | } 292 | } 293 | 294 | static JNINativeMethod sMethods[] = 295 | { 296 | { "nativeOpen", "(Ljava/lang/String;)J", (void*) nativeOpen }, 297 | { "nativeClose", "(J)V", (void*) nativeClose }, 298 | { "nativeGet", "(JJ[B)[B", (void*) nativeGet }, 299 | { "nativeGet", "(JJLjava/nio/ByteBuffer;)[B", (void*) nativeGetBB }, 300 | { "nativePut", "(J[B[B)V", (void*) nativePut }, 301 | { "nativeDelete", "(J[B)V", (void*) nativeDelete }, 302 | { "nativeWrite", "(JJ)V", (void*) nativeWrite }, 303 | { "nativeIterator", "(JJ)J", (void*) nativeIterator }, 304 | { "nativeGetSnapshot", "(J)J", (void*) nativeGetSnapshot }, 305 | { "nativeReleaseSnapshot", "(JJ)V", (void*) nativeReleaseSnapshot }, 306 | { "nativeDestroy", "(Ljava/lang/String;)V", (void*) nativeDestroy } 307 | }; 308 | 309 | int 310 | register_com_litl_leveldb_DB(JNIEnv *env) { 311 | jclass clazz = env->FindClass("com/litl/leveldb/DB"); 312 | if (!clazz) { 313 | LOGE("Can't find class com.litl.leveldb.DB"); 314 | return 0; 315 | } 316 | 317 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 318 | } 319 | -------------------------------------------------------------------------------- /library/src/main/jni/com_litl_leveldb_Iterator.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "leveldbjni.h" 5 | 6 | #include "leveldb/iterator.h" 7 | 8 | static void 9 | nativeDestroy(JNIEnv* env, 10 | jclass clazz, 11 | jlong ptr) 12 | { 13 | leveldb::Iterator* iter = reinterpret_cast(ptr); 14 | 15 | delete iter; 16 | } 17 | 18 | static void 19 | nativeSeekToFirst(JNIEnv* env, 20 | jclass clazz, 21 | jlong iterPtr) 22 | { 23 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 24 | iter->SeekToFirst(); 25 | } 26 | 27 | static void 28 | nativeSeekToLast(JNIEnv* env, 29 | jclass clazz, 30 | jlong iterPtr) 31 | { 32 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 33 | iter->SeekToLast(); 34 | } 35 | 36 | static void 37 | nativeSeek(JNIEnv* env, 38 | jclass clazz, 39 | jlong iterPtr, 40 | jbyteArray keyObj) 41 | { 42 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 43 | 44 | size_t keyLen = env->GetArrayLength(keyObj); 45 | jbyte *buffer = env->GetByteArrayElements(keyObj, NULL); 46 | 47 | iter->Seek(leveldb::Slice((const char *)buffer, keyLen)); 48 | env->ReleaseByteArrayElements(keyObj, buffer, JNI_ABORT); 49 | } 50 | 51 | static jboolean 52 | nativeValid(JNIEnv* env, 53 | jclass clazz, 54 | jlong iterPtr) 55 | { 56 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 57 | return iter->Valid(); 58 | } 59 | 60 | static void 61 | nativeNext(JNIEnv* env, 62 | jclass clazz, 63 | jlong iterPtr) 64 | { 65 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 66 | iter->Next(); 67 | } 68 | 69 | static void 70 | nativePrev(JNIEnv* env, 71 | jclass clazz, 72 | jlong iterPtr) 73 | { 74 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 75 | iter->Prev(); 76 | } 77 | 78 | static jbyteArray 79 | nativeKey(JNIEnv* env, 80 | jclass clazz, 81 | jlong iterPtr) 82 | { 83 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 84 | leveldb::Slice key = iter->key(); 85 | 86 | size_t len = key.size(); 87 | jbyteArray result = env->NewByteArray(len); 88 | env->SetByteArrayRegion(result, 0, len, (const jbyte *) key.data()); 89 | return result; 90 | } 91 | 92 | static jbyteArray 93 | nativeValue(JNIEnv* env, 94 | jclass clazz, 95 | jlong iterPtr) 96 | { 97 | leveldb::Iterator* iter = reinterpret_cast(iterPtr); 98 | leveldb::Slice value = iter->value(); 99 | 100 | size_t len = value.size(); 101 | jbyteArray result = env->NewByteArray(len); 102 | env->SetByteArrayRegion(result, 0, len, (const jbyte *) value.data()); 103 | return result; 104 | } 105 | 106 | static JNINativeMethod sMethods[] = 107 | { 108 | { "nativeDestroy", "(J)V", (void*) nativeDestroy }, 109 | { "nativeSeekToFirst", "(J)V", (void*) nativeSeekToFirst }, 110 | { "nativeSeekToLast", "(J)V", (void*) nativeSeekToLast }, 111 | { "nativeSeek", "(J[B)V", (void*) nativeSeek }, 112 | { "nativeValid", "(J)Z", (void*) nativeValid }, 113 | { "nativeNext", "(J)V", (void*) nativeNext }, 114 | { "nativePrev", "(J)V", (void*) nativePrev }, 115 | { "nativeKey", "(J)[B", (void*) nativeKey }, 116 | { "nativeValue", "(J)[B", (void*) nativeValue } 117 | }; 118 | 119 | int 120 | register_com_litl_leveldb_Iterator(JNIEnv *env) { 121 | jclass clazz = env->FindClass("com/litl/leveldb/Iterator"); 122 | if (!clazz) { 123 | LOGE("Can't find class com.litl.leveldb.Iterator"); 124 | return 0; 125 | } 126 | 127 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 128 | } 129 | -------------------------------------------------------------------------------- /library/src/main/jni/com_litl_leveldb_WriteBatch.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "leveldbjni.h" 8 | 9 | #include "leveldb/write_batch.h" 10 | 11 | static jmethodID gByteBuffer_isDirectMethodID; 12 | static jmethodID gByteBuffer_positionMethodID; 13 | static jmethodID gByteBuffer_limitMethodID; 14 | static jmethodID gByteBuffer_arrayMethodID; 15 | 16 | static jlong 17 | nativeCreate(JNIEnv* env, 18 | jclass clazz) 19 | { 20 | static bool gInited; 21 | 22 | if (!gInited) { 23 | jclass byteBuffer_Clazz = env->FindClass("java/nio/ByteBuffer"); 24 | gByteBuffer_isDirectMethodID = env->GetMethodID(byteBuffer_Clazz, 25 | "isDirect", "()Z"); 26 | gByteBuffer_positionMethodID = env->GetMethodID(byteBuffer_Clazz, 27 | "position", "()I"); 28 | gByteBuffer_limitMethodID = env->GetMethodID(byteBuffer_Clazz, 29 | "limit", "()I"); 30 | gByteBuffer_arrayMethodID = env->GetMethodID(byteBuffer_Clazz, 31 | "array", "()[B"); 32 | gInited = true; 33 | } 34 | 35 | leveldb::WriteBatch* batch = new leveldb::WriteBatch(); 36 | return reinterpret_cast(batch); 37 | } 38 | 39 | static void 40 | nativeDestroy(JNIEnv* env, 41 | jclass clazz, 42 | jlong ptr) 43 | { 44 | leveldb::WriteBatch* batch = reinterpret_cast(ptr); 45 | 46 | delete batch; 47 | } 48 | 49 | static void 50 | nativeDelete(JNIEnv* env, 51 | jclass clazz, 52 | jlong ptr, 53 | jobject buffer) 54 | { 55 | leveldb::WriteBatch* batch = reinterpret_cast(ptr); 56 | 57 | jint pos = env->CallIntMethod(buffer, gByteBuffer_positionMethodID); 58 | jint limit = env->CallIntMethod(buffer, gByteBuffer_limitMethodID); 59 | jboolean isDirect = env->CallBooleanMethod(buffer, gByteBuffer_isDirectMethodID); 60 | if (isDirect) { 61 | const char *bytes = (const char *) env->GetDirectBufferAddress(buffer); 62 | batch->Delete(leveldb::Slice(bytes + pos, limit - pos)); 63 | } else { 64 | jbyteArray array = (jbyteArray) env->CallObjectMethod(buffer, gByteBuffer_arrayMethodID); 65 | jbyte *bytes = env->GetByteArrayElements(array, NULL); 66 | batch->Delete(leveldb::Slice((const char *) bytes + pos, limit - pos)); 67 | env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); 68 | } 69 | } 70 | 71 | static void 72 | nativePut(JNIEnv* env, 73 | jclass clazz, 74 | jlong ptr, 75 | jobject keyObj, 76 | jobject valObj) 77 | { 78 | leveldb::WriteBatch* batch = reinterpret_cast(ptr); 79 | 80 | jint keyPos = env->CallIntMethod(keyObj, gByteBuffer_positionMethodID); 81 | jint keyLimit = env->CallIntMethod(keyObj, gByteBuffer_limitMethodID); 82 | jboolean keyIsDirect = env->CallBooleanMethod(keyObj, gByteBuffer_isDirectMethodID); 83 | jbyteArray keyArray; 84 | void* key; 85 | if (keyIsDirect) { 86 | key = env->GetDirectBufferAddress(keyObj); 87 | keyArray = NULL; 88 | } else { 89 | keyArray = (jbyteArray) env->CallObjectMethod(keyObj, gByteBuffer_arrayMethodID); 90 | key = (void*) env->GetByteArrayElements(keyArray, NULL); 91 | } 92 | 93 | jint valPos = env->CallIntMethod(valObj, gByteBuffer_positionMethodID); 94 | jint valLimit = env->CallIntMethod(valObj, gByteBuffer_limitMethodID); 95 | jboolean valIsDirect = env->CallBooleanMethod(valObj, gByteBuffer_isDirectMethodID); 96 | jbyteArray valArray; 97 | void* val; 98 | if (valIsDirect) { 99 | val = env->GetDirectBufferAddress(valObj); 100 | valArray = NULL; 101 | } else { 102 | valArray = (jbyteArray) env->CallObjectMethod(valObj, gByteBuffer_arrayMethodID); 103 | val = (void*) env->GetByteArrayElements(valArray, NULL); 104 | } 105 | 106 | batch->Put(leveldb::Slice((const char *) key + keyPos, keyLimit - keyPos), 107 | leveldb::Slice((const char *) val + valPos, valLimit - valPos)); 108 | 109 | if (keyArray) { 110 | env->ReleaseByteArrayElements(keyArray, (jbyte*) key, JNI_ABORT); 111 | } 112 | if (valArray) { 113 | env->ReleaseByteArrayElements(valArray, (jbyte*) val, JNI_ABORT); 114 | } 115 | } 116 | 117 | static void 118 | nativeClear(JNIEnv* env, 119 | jclass clazz, 120 | jlong ptr) 121 | { 122 | leveldb::WriteBatch* batch = reinterpret_cast(ptr); 123 | batch->Clear(); 124 | } 125 | 126 | static JNINativeMethod sMethods[] = 127 | { 128 | { "nativeCreate", "()J", (void*) nativeCreate }, 129 | { "nativeDestroy", "(J)V", (void*) nativeDestroy }, 130 | { "nativeDelete", "(JLjava/nio/ByteBuffer;)V", (void*) nativeDelete }, 131 | { "nativePut", "(JLjava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V", (void*) nativePut }, 132 | { "nativeClear", "(J)V", (void*) nativeClear } 133 | }; 134 | 135 | int 136 | register_com_litl_leveldb_WriteBatch(JNIEnv *env) { 137 | jclass clazz = env->FindClass("com/litl/leveldb/WriteBatch"); 138 | if (!clazz) { 139 | LOGE("Can't find class com.litl.leveldb.WriteBatch"); 140 | return 0; 141 | } 142 | 143 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 144 | } 145 | -------------------------------------------------------------------------------- /library/src/main/jni/debug_conf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/4/12. 3 | // 4 | 5 | #ifndef CONVARTER_DEBUG_CONF_H 6 | #define CONVARTER_DEBUG_CONF_H 7 | 8 | //Chunk 9 | //#define LOG_CHUNK_OPERATION 10 | #define LOG_CHUNK_LOADSAVE 11 | 12 | //World 13 | //#define LOG_SAVDB_OPERATION 14 | #define LOG_SAVDB_LOADSAVE 15 | #define LOG_SAVDB_LRU 16 | 17 | #ifdef LOG_CHUNK_OPERATION 18 | #ifndef LOG_CHUNK_LOADSAVE 19 | #define LOG_CHUNK_LOADSAVE 20 | #endif 21 | #endif 22 | 23 | #ifdef LOG_SAVDB_OPERATION 24 | #ifndef LOG_SAVDB_LOADSAVE 25 | #define LOG_SAVDB_LOADSAVE 26 | #endif 27 | #ifndef LOG_SAVDB_LRU 28 | #define LOG_SAVDB_LRU 29 | #endif 30 | #endif 31 | 32 | #define CAT(x, y) x y 33 | 34 | #endif //CONVARTER_DEBUG_CONF_H 35 | -------------------------------------------------------------------------------- /library/src/main/jni/leveldbjni.cc: -------------------------------------------------------------------------------- 1 | #include "leveldbjni.h" 2 | 3 | extern int register_com_litl_leveldb_DB(JNIEnv *env); 4 | 5 | extern int register_com_litl_leveldb_WriteBatch(JNIEnv *env); 6 | 7 | extern int register_com_litl_leveldb_Iterator(JNIEnv *env); 8 | 9 | extern int register_com_litl_leveldb_Chunk(JNIEnv *env); 10 | 11 | jint 12 | throwException(JNIEnv *env, leveldb::Status status) { 13 | const char *exceptionClass; 14 | 15 | if (status.IsNotFound()) { 16 | exceptionClass = "com/litl/leveldb/NotFoundException"; 17 | } else if (status.IsCorruption()) { 18 | exceptionClass = "com/litl/leveldb/DatabaseCorruptException"; 19 | } else if (status.IsIOError()) { 20 | exceptionClass = "java/io/IOException"; 21 | } else { 22 | return 0; 23 | } 24 | 25 | jclass clazz = env->FindClass(exceptionClass); 26 | if (!clazz) { 27 | LOGE("Can't find exception class %s", exceptionClass); 28 | return -1; 29 | } 30 | 31 | return env->ThrowNew(clazz, status.ToString().c_str()); 32 | } 33 | 34 | jint JNI_OnLoad(JavaVM *vm, void *reserved) { 35 | JNIEnv *env; 36 | if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { 37 | return -1; 38 | } 39 | 40 | register_com_litl_leveldb_DB(env); 41 | register_com_litl_leveldb_WriteBatch(env); 42 | register_com_litl_leveldb_Iterator(env); 43 | register_com_litl_leveldb_Chunk(env); 44 | 45 | return JNI_VERSION_1_6; 46 | } 47 | -------------------------------------------------------------------------------- /library/src/main/jni/leveldbjni.h: -------------------------------------------------------------------------------- 1 | #ifndef LEVELDBJNI_H_ 2 | #define LEVELDBJNI_H_ 3 | 4 | #include 5 | #include 6 | #include "leveldb/status.h" 7 | 8 | # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 9 | #define LOG_TAG "LevelDB" 10 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 11 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 12 | 13 | jint throwException(JNIEnv* env, leveldb::Status status); 14 | 15 | #endif /* LEVELDBJNI_H_ */ 16 | -------------------------------------------------------------------------------- /library/src/main/jni/mapkey.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/27. 3 | // 4 | 5 | #ifndef CONVARTER_MAPKEY_H 6 | #define CONVARTER_MAPKEY_H 7 | 8 | #include 9 | 10 | typedef struct { 11 | int32_t x_div16; 12 | int32_t z_div16; 13 | int32_t dimension; 14 | } mapkey_t; 15 | 16 | #define LDBKEY_STRUCT(x, z, dim) mapkey_t{x >> 4, z >> 4, dim} 17 | 18 | #define LDBKEY_SUBCHUNK(k, ydiv) \ 19 | char key[14];\ 20 | if(true){\ 21 | char* ptr = key;\ 22 | *(int32_t*)ptr = k.x_div16;\ 23 | ptr+=4;\ 24 | *(int32_t*)ptr = k.z_div16;\ 25 | ptr += 4;\ 26 | if(k.dimension != 0){\ 27 | *(int32_t*)ptr = k.dimension;\ 28 | ptr += 4;\ 29 | }\ 30 | *ptr = 0x2f;\ 31 | ptr++;\ 32 | *ptr = ydiv;\ 33 | } 34 | 35 | #define LDBKEY_VERSION(k) \ 36 | char key_db[14];\ 37 | if(true){\ 38 | char* ptr = key_db;\ 39 | *(int32_t*)ptr = k.x_div16;\ 40 | ptr+=4;\ 41 | *(int32_t*)ptr = k.z_div16;\ 42 | ptr += 4;\ 43 | if(k.dimension != 0){\ 44 | *(int32_t*)ptr = k.dimension;\ 45 | ptr += 4;\ 46 | }\ 47 | *ptr = 0x76;\ 48 | ptr++;\ 49 | } 50 | 51 | #endif //CONVARTER_MAPKEY_H 52 | -------------------------------------------------------------------------------- /library/src/main/jni/qstr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/3/24. 3 | // 4 | 5 | #ifndef CONVARTER_QSTR_H 6 | #define CONVARTER_QSTR_H 7 | 8 | typedef struct { 9 | unsigned int length; 10 | char *str; 11 | } qstr; 12 | 13 | typedef struct { 14 | unsigned int length; 15 | const char *str; 16 | } qcstr; 17 | 18 | typedef struct { 19 | unsigned int length; 20 | unsigned char *str; 21 | } qustr; 22 | 23 | #endif //CONVARTER_QSTR_H 24 | -------------------------------------------------------------------------------- /library/src/main/jni/subchunk.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/12/26. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //////////////// 12 | // 13 | 14 | char SubChunk::pattern_name[] = {0x0a, 0x00, 0x00, 0x08, 0x04, 0x00, 'n', 'a', 'm', 15 | 'e'}; 16 | char SubChunk::pattern_val[] = {0x02, 0x03, 0x00, 'v', 'a', 'l'}; 17 | 18 | const int32_t SubChunk::msk[] = {0b1, 0b11, 0b111, 0b1111, 0b11111, 0b111111, 0b1111111, 19 | 0b11111111, 20 | 0b111111111, 0b1111111111, 0b11111111111, 21 | 0b111111111111, 22 | 0b1111111111111, 0b11111111111111, 0b11111111111111}; 23 | 24 | SubChunk::SubChunk(const std::string &buf, bool hasMultiStorage) { 25 | 26 | storages[0].storage = nullptr; 27 | storages[0].palette = nullptr; 28 | 29 | const char *cbuf = buf.c_str(); 30 | const char *ptr = cbuf + 1; 31 | if (hasMultiStorage) { 32 | ptr++; 33 | loadStorage(ptr, cbuf + buf.length(), 0); 34 | } else { 35 | loadStorage(ptr, cbuf + buf.length(), 0); 36 | } 37 | } 38 | 39 | SubChunk::~SubChunk() { 40 | delete[] storages[0].storage; 41 | delete[] storages[0].palette; 42 | } 43 | 44 | const char *SubChunk::loadStorage(const char *ptr, const char *max, int which) { 45 | 46 | #ifdef DEBUG_SUBCHUNK 47 | //lowest bit should be 0. 48 | if (((*ptr) & 1) != 0 || max - ptr < 2) { 49 | // 50 | } 51 | #endif 52 | 53 | //Length of each block, in bits. 54 | storages[which].blen = static_cast(*ptr >> 1); 55 | 56 | ptr++; 57 | 58 | //How many uint32s do it need to store all blocks? 59 | div_t res = div(4096, (32 / storages[which].blen)); 60 | size_t bufsize = static_cast(res.quot); 61 | if (res.rem != 0)bufsize++; 62 | 63 | #ifdef DEBUG_SUBCHUNK 64 | if (max - ptr < (bufsize << 2)) { 65 | // 66 | } 67 | #endif 68 | 69 | //Copy 'em up. bufsize is 4-bytes long and memcpy requires count of bytes so x4. 70 | storages[which].storage = new uint32_t[bufsize]; 71 | memcpy(storages[which].storage, ptr, bufsize << 2); 72 | 73 | //Move the pointer to the end of uint32s. 74 | ptr += bufsize << 2; 75 | 76 | //Here records how many types of blocks are in this subchunk. 77 | storages[which].types = *(uint16_t *) ptr; 78 | ptr += 4; 79 | 80 | storages[which].palette = new uint16_t[storages[which].types]; 81 | 82 | for (uint16_t i = 0; i < storages[which].types; i++) { 83 | #ifdef DEBUG_SUBCHUNK 84 | if (max - ptr < sizeof(pattern_name)) { 85 | // 86 | } 87 | if (memcmp(ptr, pattern_name, sizeof(pattern_name)) != 0) { 88 | //Something has gone wrong. 89 | } 90 | #endif 91 | ptr += sizeof(pattern_name); 92 | qstr name; 93 | #ifdef DEBUG_SUBCHUNK 94 | if (max - ptr < 2) { 95 | // 96 | } 97 | #endif 98 | name.length = *(uint16_t *) ptr; 99 | ptr += 2; 100 | #ifdef DEBUG_SUBCHUNK 101 | if (max - ptr < name.length) { 102 | // 103 | } 104 | #endif 105 | name.str = new char[name.length]; 106 | memcpy(name.str, ptr, name.length); 107 | ptr += name.length; 108 | #ifdef DEBUG_SUBCHUNK 109 | if (max - ptr < sizeof(pattern_val) + 3) { 110 | // 111 | } 112 | if (memcmp(ptr, pattern_val, sizeof(pattern_val)) != 0) { 113 | // 114 | } 115 | #endif 116 | ptr += sizeof(pattern_val); 117 | storages[which].palette[i] = BlockNames::resolve(name); 118 | delete[] name.str; 119 | storages[which].palette[i] <<= 8; 120 | storages[which].palette[i] |= *ptr; 121 | ptr += 3; 122 | } 123 | return ptr; 124 | } 125 | 126 | uint16_t 127 | SubChunk::getBlockCode(unsigned char x, unsigned char y, unsigned char z, uint8_t which) { 128 | 129 | //If there's only one storage than getBlockCode or other storages can just return 0. 130 | if (which == 0 && storages[which].storage == nullptr)return 0; 131 | 132 | BlockStorage &thiz = storages[which]; 133 | 134 | //Get the index among all blocks. 135 | int index = x; 136 | index <<= 4; 137 | index |= z; 138 | index <<= 4; 139 | index |= y; 140 | 141 | //How many blox can each stick hold. 142 | int capa = (32 / thiz.blen); 143 | 144 | //Stick that hold this block. 145 | uint32_t stick = *(thiz.storage + (index / capa)); 146 | 147 | //The bits for this block is index in palette. 148 | //No need care about endian but not very efficiency, i guess? 149 | uint32_t ind = (stick >> (index % capa * thiz.blen)) & msk[thiz.blen - 1]; 150 | 151 | //Return the record. 152 | return thiz.palette[ind]; 153 | } 154 | 155 | uint16_t SubChunk::getBlock(unsigned char x, unsigned char y, unsigned char z) { 156 | return getBlockCode(x, y, z, 0); 157 | } 158 | 159 | uint16_t SubChunk::getBlock3(unsigned char x, unsigned char y, unsigned char z, 160 | unsigned char layer) { 161 | if (layer != 0 && layer != 1)return 0; 162 | return getBlockCode(x, y, z, layer); 163 | } 164 | -------------------------------------------------------------------------------- /library/src/main/jni/subchunk.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by barco on 2018/12/24. 3 | // 4 | 5 | #ifndef CONVARTER_SUBCHUNK_H 6 | #define CONVARTER_SUBCHUNK_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define DEBUG_SUBCHUNK 13 | 14 | struct BlockStorage { 15 | uint8_t blen;//Length per block 16 | uint16_t types;//Types of blocks 17 | uint16_t *palette;//Palette 18 | uint32_t *storage;//Storage 19 | }; 20 | 21 | class SubChunk { 22 | private: 23 | 24 | static const int32_t msk[15]; 25 | 26 | static char pattern_name[10]; 27 | 28 | static char pattern_val[6]; 29 | 30 | BlockStorage storages[1]; 31 | 32 | const char *loadStorage(const char *ptr, const char *max, int which); 33 | 34 | uint16_t getBlockCode(unsigned char x, unsigned char y, unsigned char z, uint8_t which); 35 | 36 | public: 37 | SubChunk(const std::string &buf, bool hasMultiStorage); 38 | 39 | ~SubChunk(); 40 | 41 | uint16_t getBlock(unsigned char x, unsigned char y, unsigned char z); 42 | 43 | uint16_t 44 | getBlock3(unsigned char x, unsigned char y, unsigned char z, unsigned char layer); 45 | 46 | }; 47 | 48 | #endif //CONVARTER_SUBCHUNK_H 49 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':library' 2 | --------------------------------------------------------------------------------