Skip to content

Commit

Permalink
feat: add documentation and final touches
Browse files Browse the repository at this point in the history
  • Loading branch information
savv committed Dec 21, 2020
1 parent f09b512 commit 4a06c27
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 24 deletions.
42 changes: 37 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,49 @@ Superfast React Native bindings for LevelDB
## Installation

```sh
npm install react-native-leveldb
yarn add react-native-leveldb
```

## Usage

```js
import Leveldb from "react-native-leveldb";
```ts
import {LevelDB} from "react-native-leveldb";

// ...
export function leveldbExample(): boolean {
// Open a potentially new database.
const name = 'example.db';
const createIfMissing = true;
const errorIfExists = false;
const db = new LevelDB(name, createIfMissing, errorIfExists);

const result = await Leveldb.multiply(3, 7);
// Insert something into the database. Note that the key and the value can either be
// strings or ArrayBuffers. Strings are read & written in utf8.
db.put('key', 'value');

// You can also use ArrayBuffers as input, containing binary data.
const key = new Uint8Array([1, 2, 3]);
const value = new Uint32Array([654321]);
db.put(key.buffer, value.buffer);

// Get values as string or as an ArrayBuffer (useful for binary data).
const readStringValue = db.getStr('key');
const readBufferValue = new Uint32Array(db.getBuf(key.buffer));
console.log(readStringValue, readBufferValue); // should log: value [654321]

// Iterate over a range of values (here, from key "key" to the end.)
let iter = db.newIterator();
for (iter.seek('key'); iter.valid(); iter.next()) {
// There are also *Buf version to access iterators' keys & values.
console.log(`iterating: "${iter.keyStr()}" / "${iter.valueStr()}"`);
}

// You need to close iterators when you are done with them. Iterators will throw an error if used after this.
iter.close();

db.close(); // Same for databases.

return readStringValue == 'value' && readBufferValue.length == 1 && readBufferValue[0] == 654321;
}
```

## Contributing
Expand Down
37 changes: 27 additions & 10 deletions cpp/react-native-leveldb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ std::vector<std::unique_ptr<leveldb::DB>> dbs;
std::vector<std::unique_ptr<leveldb::Iterator>> iterators;

// Returns false if the passed value is not a string or an ArrayBuffer.
bool valueToSlice(jsi::Runtime& runtime, const jsi::Value& value, leveldb::Slice* slice) {
bool valueToString(jsi::Runtime& runtime, const jsi::Value& value, std::string* str) {
if (value.isString()) {
*slice = leveldb::Slice(value.asString(runtime).utf8(runtime));
*str = value.asString(runtime).utf8(runtime);
return true;
}

Expand All @@ -25,7 +25,7 @@ bool valueToSlice(jsi::Runtime& runtime, const jsi::Value& value, leveldb::Slice
return false;
}
auto buf = obj.getArrayBuffer(runtime);
*slice = leveldb::Slice((char*)buf.data(runtime), buf.size(runtime));
*str = std::string((char*)buf.data(runtime), buf.size(runtime));
return true;
}

Expand Down Expand Up @@ -115,9 +115,9 @@ void installLeveldb(jsi::Runtime& jsiRuntime, std::string documentDir) {
jsi::PropNameID::forAscii(jsiRuntime, "leveldbPut"),
3, // dbs index, key, value
[](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
leveldb::Slice key, value;
std::string key, value;
leveldb::DB* db = valueToDb(arguments[0]);
if (!db || !valueToSlice(runtime, arguments[1], &key) || !valueToSlice(runtime, arguments[2], &value)) {
if (!db || !valueToString(runtime, arguments[1], &key) || !valueToString(runtime, arguments[2], &value)) {
return jsi::Value(-1);
}

Expand Down Expand Up @@ -187,11 +187,11 @@ void installLeveldb(jsi::Runtime& jsiRuntime, std::string documentDir) {
return jsi::Value(-1);
}

leveldb::Slice slice;
if (!valueToSlice(runtime, arguments[1], &slice)) {
std::string target;
if (!valueToString(runtime, arguments[1], &target)) {
return jsi::Value(-1);
}
iterator->Seek(slice);
iterator->Seek(target);
return nullptr;
}
);
Expand Down Expand Up @@ -226,6 +226,21 @@ void installLeveldb(jsi::Runtime& jsiRuntime, std::string documentDir) {
);
jsiRuntime.global().setProperty(jsiRuntime, "leveldbIteratorNext", std::move(leveldbIteratorNext));

auto leveldbIteratorDelete = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forAscii(jsiRuntime, "leveldbIteratorDelete"),
1, // iterators index
[](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
leveldb::Iterator* iterator = valueToIterator(arguments[0]);
if (!iterator) {
return jsi::Value(-1);
}
iterators[(int)arguments[0].getNumber()].release();
return nullptr;
}
);
jsiRuntime.global().setProperty(jsiRuntime, "leveldbIteratorDelete", std::move(leveldbIteratorDelete));

auto leveldbIteratorKeyStr = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forAscii(jsiRuntime, "leveldbIteratorKeyStr"),
Expand Down Expand Up @@ -259,8 +274,9 @@ void installLeveldb(jsi::Runtime& jsiRuntime, std::string documentDir) {
jsi::PropNameID::forAscii(jsiRuntime, "leveldbGetStr"),
2, // dbs index, key
[](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
leveldb::Slice key;
leveldb::DB* db = valueToDb(arguments[0]);
std::string key;
valueToString(runtime, arguments[1], &key);

std::string value;
auto status = db->Get(leveldb::ReadOptions(), key, &value);
Expand Down Expand Up @@ -317,8 +333,9 @@ void installLeveldb(jsi::Runtime& jsiRuntime, std::string documentDir) {
jsi::PropNameID::forAscii(jsiRuntime, "leveldbGetBuf"),
2, // dbs index, key
[](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
leveldb::Slice key;
leveldb::DB* db = valueToDb(arguments[0]);
std::string key;
valueToString(runtime, arguments[1], &key);

std::string value;
auto status = db->Get(leveldb::ReadOptions(), key, &value);
Expand Down
15 changes: 12 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import {benchmarkAsyncStorage, benchmarkLeveldb, BenchmarkResults, BenchmarkResultsView} from "./benchmark";
import {StyleSheet, View, Text} from 'react-native';
import {
benchmarkAsyncStorage,
benchmarkLeveldb,
BenchmarkResults,
BenchmarkResultsView
} from "./benchmark";
import {leveldbExample} from "./example";

interface BenchmarkState {
leveldb?: BenchmarkResults;
leveldbExample?: boolean;
asyncStorage?: BenchmarkResults;
}

Expand All @@ -13,7 +20,8 @@ export default class App extends React.Component<{}, BenchmarkState> {
componentDidMount() {
try {
this.setState({
leveldb: benchmarkLeveldb()
leveldb: benchmarkLeveldb(),
leveldbExample: leveldbExample(),
});

benchmarkAsyncStorage().then(res => this.setState({asyncStorage: res}));
Expand All @@ -25,6 +33,7 @@ export default class App extends React.Component<{}, BenchmarkState> {
render() {
return (
<View style={styles.container}>
<Text>Example validity: {this.state.leveldbExample == undefined ? '' : this.state.leveldbExample ? 'passed' : 'failed'}</Text>
{this.state.leveldb && <BenchmarkResultsView title="LevelDB" {...this.state.leveldb} />}
{this.state.asyncStorage && <BenchmarkResultsView title="AsyncStorage" {...this.state.asyncStorage} />}
</View>
Expand Down
37 changes: 37 additions & 0 deletions example/src/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {LevelDB} from "react-native-leveldb";

export function leveldbExample(): boolean {
// Open a potentially new database.
const name = 'example.db';
const createIfMissing = true;
const errorIfExists = false;
const db = new LevelDB(name, createIfMissing, errorIfExists);

// Insert something into the database. Note that the key and the value can either be
// strings or ArrayBuffers. Strings are read & written in utf8.
db.put('key', 'value');

// You can also use ArrayBuffers as input, containing binary data.
const key = new Uint8Array([1, 2, 3]);
const value = new Uint32Array([654321]);
db.put(key.buffer, value.buffer);

// Get values as string or as an ArrayBuffer (useful for binary data).
const readStringValue = db.getStr('key');
const readBufferValue = new Uint32Array(db.getBuf(key.buffer));
console.log(readStringValue, readBufferValue); // should log: value [654321]

// Iterate over a range of values (here, from key "key" to the end.)
let iter = db.newIterator();
for (iter.seek('key'); iter.valid(); iter.next()) {
// There are also *Buf version to access iterators' keys & values.
console.log(`iterating: "${iter.keyStr()}" / "${iter.valueStr()}"`);
}

// You need to close iterators when you are done with them. Iterators will throw an error if used after this.
iter.close();

db.close(); // Same for databases.

return readStringValue == 'value' && readBufferValue.length == 1 && readBufferValue[0] == 654321;
}
20 changes: 14 additions & 6 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ export class LevelDBIterator {
}
}

close() {
if (isBadResult(g.leveldbIteratorDelete(this.ref))) {
throw new Error('LevelDBIterator error');
}

this.ref = -1;
}

keyStr(): string {
const res = g.leveldbIteratorKeyStr(this.ref);
if (isBadResult(res)) {
Expand Down Expand Up @@ -147,18 +155,18 @@ export class LevelDB {
}
}

getStr(k: ArrayBuffer | string, v: ArrayBuffer | string) {
const res = g.leveldbGetStr(this.ref, k, v);
getStr(k: ArrayBuffer | string) {
const res = g.leveldbGetStr(this.ref, k);
if (isBadResult(res)) {
throw new Error('LevelDB: unable to get()');
throw new Error(`LevelDB: unable to getStr(): ${res}`);
}
return res;
}

getBuf(k: ArrayBuffer | string, v: ArrayBuffer | string) {
const res = g.leveldbGetBuf(this.ref, k, v);
getBuf(k: ArrayBuffer | string) {
const res = g.leveldbGetBuf(this.ref, k);
if (isBadResult(res)) {
throw new Error('LevelDB: unable to get()');
throw new Error(`LevelDB: unable to getBuf(): ${res}`);
}
return res;
}
Expand Down

0 comments on commit 4a06c27

Please sign in to comment.