Skip to content

Commit

Permalink
Merge pull request #1 from badsyntax/feature/android
Browse files Browse the repository at this point in the history
Add initial support for android
  • Loading branch information
badsyntax authored Nov 24, 2023
2 parents 94806c7 + c97e4cb commit 55fd9d1
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "lmdb"]
path = lmdb
url = https://git.openldap.org/openldap/openldap.git
url = https://github.com/LMDB/lmdb.git
branch = mdb.master
48 changes: 28 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@

React Native bindings for LMDB (proof of concept).

This package is in early development and not ready for consumption. Currently it only works on iOS.
> Lightning Memory-Mapped Database
LMDB is an embedded transactional database in the form of a key-value store.

https://www.symas.com/lmdb

This package is in early development and not ready for consumption.

## Goals of this Project

- Simple API
- Performance over features

## Benchmarks

Initial benchmarks show good performance:

iOS Simulator:

| Action | Time |
| ---------------- | -------- |
| open/create db | 1.62ms |
Expand All @@ -15,18 +30,16 @@ Initial benchmarks show good performance:
| get 1000 items | 4.42ms |
| get 10_000 items | 44.25ms |

## LMDB

> Lightning Memory-Mapped Database
LMDB is an embedded transactional database in the form of a key-value store. It's fast, small, and can handle large amounts of data.
Android Emulator:

https://www.symas.com/lmdb

## Goals of this Project

- Simple API
- Performance over features
| Action | Time |
| ---------------- | --------- |
| open/create db | 0.67ms |
| put 2 items | 26.76ms |
| put 10_000 items | 4343.95ms |
| get 2 items | 0.24ms |
| get 1000 items | 4.42ms |
| get 10_000 items | 28.03ms |

## Installation

Expand All @@ -39,16 +52,11 @@ npm install react-native-lmdb
```js
import { open } from 'react-native-lmdb';

const { put, putBatch, get, del } = open('mydb.mdb');
const { put, get, del } = open('mydb.mdb');

put('key1', 'value1');
put('key2', 'value2');

putBatch({
key3: 'value3',
key4: 'value4',
});

console.log(get('key1'));
console.log(get('key2'));

Expand All @@ -61,12 +69,12 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the

## Credits

- Thanks to [sysmas](https://www.symas.com/) for open sourcing lmdb.
- Thanks to [lmdb++](https://github.com/drycpp/lmdbxx) for the useful c++ wrapper.
- Thanks to [SwiftLMDB](https://github.com/agisboye/SwiftLMDB) for a good overview of how to use LMDB.

## License

MIT
MIT Richard Willis

---

Expand Down
14 changes: 10 additions & 4 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_CXX_STANDARD 11)

add_library(cpp
SHARED
../cpp/react-native-lmdb.cpp
cpp-adapter.cpp
SHARED
../cpp/react-native-lmdb.cpp
../lmdb/libraries/liblmdb/mdb.c
../lmdb/libraries/liblmdb/midl.c
cpp-adapter.cpp
)

# Specifies a path to native header files.
include_directories(
../cpp
../cpp
)

include_directories(
../lmdb/libraries/liblmdb
)
37 changes: 34 additions & 3 deletions android/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
#include <jni.h>
#include "react-native-lmdb.h"

std::string getString(JNIEnv *env, jstring jStr) {
const char *cstr = env->GetStringUTFChars(jStr, NULL);
std::string str = std::string(cstr);
env->ReleaseStringUTFChars(jStr, cstr);
return str;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_lmdb_LmdbModule_nativeOpen(JNIEnv *env, jclass type, jstring dbPath, jdouble mapSize) {
std::string dbPathStr = getString(env, dbPath);
rnlmdb::open(dbPathStr, mapSize);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_lmdb_LmdbModule_nativePut(JNIEnv *env, jclass type, jstring key, jstring value) {
std::string keyStr = getString(env, key);
std::string valueStr = getString(env, value);
rnlmdb::put(keyStr, valueStr);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_lmdb_LmdbModule_nativeGet(JNIEnv *env, jclass type, jstring key) {
std::string keyStr = getString(env, key);
std::string result = rnlmdb::get(keyStr);
return (env)->NewStringUTF(result.c_str());
}

extern "C"
JNIEXPORT jdouble JNICALL
Java_com_lmdb_LmdbModule_nativeMultiply(JNIEnv *env, jclass type, jdouble a, jdouble b) {
return lmdb::multiply(a, b);
JNIEXPORT void JNICALL
Java_com_lmdb_LmdbModule_nativeDel(JNIEnv *env, jclass type, jstring key) {
std::string keyStr = getString(env, key);
rnlmdb::del(keyStr);
}
37 changes: 31 additions & 6 deletions android/src/main/java/com/lmdb/LmdbModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;

import java.io.File;
import java.nio.file.Paths;

@ReactModule(name = LmdbModule.NAME)
public class LmdbModule extends ReactContextBaseJavaModule {
public static final String NAME = "Lmdb";
private final String documentsDirectoryPath;

public LmdbModule(ReactApplicationContext reactContext) {
super(reactContext);
documentsDirectoryPath = reactContext.getFilesDir().getAbsolutePath();
}

@Override
Expand All @@ -26,12 +31,32 @@ public String getName() {
System.loadLibrary("cpp");
}

private static native double nativeMultiply(double a, double b);
private static native void nativeOpen(String dbPath, double mapSize);
private static native void nativePut(String key, String value);
private static native String nativeGet(String key);
private static native void nativeDel(String key);

@ReactMethod(isBlockingSynchronousMethod = true)
public void open(String dbName, Double mapSize) {
File dbPath = new File(documentsDirectoryPath, dbName);
if (!dbPath.exists()) {
dbPath.mkdirs(); // with intermediate directories
}
nativeOpen(dbPath.getPath(), mapSize);
}

@ReactMethod(isBlockingSynchronousMethod = true)
public void put(String key, String value) {
nativePut(key, value);
}

@ReactMethod(isBlockingSynchronousMethod = true)
public String get(String key) {
return nativeGet(key);
}

// Example method
// See https://reactnative.dev/docs/native-modules-android
@ReactMethod
public void multiply(double a, double b, Promise promise) {
promise.resolve(nativeMultiply(a, b));
@ReactMethod(isBlockingSynchronousMethod = true)
public void del(String key) {
nativeDel(key);
}
}
28 changes: 10 additions & 18 deletions cpp/react-native-lmdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
#include "lmdb++.h"

namespace rnlmdb {
lmdb::env env = nullptr;

lmdb::env env = lmdb::env::create();

void open(std::string dbName, long mapsize) {
env.set_mapsize(mapsize);
env.open(dbName.c_str(), MDB_CREATE, 0664);
void open(std::string dbPath, double mapSize) {
if (env != nullptr) {
throw std::runtime_error("environment already open");
}
env = lmdb::env::create();
env.set_mapsize((long)mapSize);
env.open(dbPath.c_str(), MDB_CREATE, 0664);
}

std::optional<std::string> get(std::string key)
std::string get(std::string key)
{
auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY);
auto dbi = lmdb::dbi::open(rtxn, nullptr);
Expand All @@ -19,7 +22,7 @@ namespace rnlmdb {
auto rc = ::mdb_get(rtxn, dbi, keyValue, dataValue);
rtxn.abort();
if (rc != 0) {
return std::nullopt;
return nullptr;
} else {
return dataValue.data();
}
Expand All @@ -33,17 +36,6 @@ namespace rnlmdb {
wtxn.commit();
}

void putBatch(std::unordered_map<std::string, std::string> batch)
{
auto wtxn = lmdb::txn::begin(env);
auto dbi = lmdb::dbi::open(wtxn, nullptr);
for (auto const& [key, value] : batch)
{
dbi.put(wtxn, key.c_str(), value.c_str());
}
wtxn.commit();
}

void del(std::string key)
{
auto wtxn = lmdb::txn::begin(env);
Expand Down
7 changes: 4 additions & 3 deletions cpp/react-native-lmdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
#define LMDB_H

#include <string>
#include <unordered_map>
#include <optional>

namespace rnlmdb {

void open(std::string dbName, long mapSize);
void open(std::string dbPath, double mapSize);
void put(std::string key, std::string value);
void putBatch(std::unordered_map<std::string, std::string> batch);
std::optional<std::string> get(std::string key);
std::string get(std::string key);
void del(std::string key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: c49502e5d02112247ee4526bc3ccfc891ae3eb9b
React-jsinspector: 8baadae51f01d867c3921213a25ab78ab4fbcd91
React-logger: 8edc785c47c8686c7962199a307015e2ce9a0e4f
react-native-lmdb: 5e74e4001f03b3526f2e2a2b356cc7bf95962a48
react-native-lmdb: 03ad4877d714b27660e6e49230448925fc5425cb
React-NativeModulesApple: b6868ee904013a7923128892ee4a032498a1024a
React-perflogger: 31ea61077185eb1428baf60c0db6e2886f141a5a
React-RCTActionSheet: 392090a3abc8992eb269ef0eaa561750588fc39d
Expand Down
17 changes: 2 additions & 15 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { StyleSheet, View, Text } from 'react-native';
import { open } from 'react-native-lmdb';

const nowOpen = performance.now();
const { put, get, putBatch, del } = open('mydb.mdb');
const { put, get } = open('mydb.mdb');
const endOpen = performance.now() - nowOpen;

const nowPut = performance.now();
Expand All @@ -24,25 +24,13 @@ for (let i = 0; i < 10_000; i++) {
}
const endBatchPut = performance.now() - nowBatchPut;

putBatch({
key1: 'value1',
key2: 'value2',
});

const nowBatchGet = performance.now();
for (let i = 0; i < 10_000; i++) {
get(`key${i}`);
}
const endBatchGet = performance.now() - nowBatchGet;

const nowBatchGet1 = performance.now();
for (let i = 0; i < 1_000; i++) {
get(`key${i}`);
}
const endBatchGet1 = performance.now() - nowBatchGet1;

del('key1');
console.log('key1?', get('key1'));
// del('key1');

export default function App() {
return (
Expand All @@ -55,7 +43,6 @@ export default function App() {
<Text>get (2): {endGet}ms</Text>
<Text>put (10_000): {endBatchPut}ms</Text>
<Text>get (10_000): {endBatchGet}ms</Text>
<Text>get (1000): {endBatchGet1}ms</Text>
</View>
);
}
Expand Down
Loading

0 comments on commit 55fd9d1

Please sign in to comment.