Implement delete, select functions

This commit is contained in:
2023-06-07 09:10:18 +09:00
parent 019e8831a4
commit 4e11bf7d47
6 changed files with 219 additions and 85 deletions

View File

@@ -13,6 +13,7 @@
348168332A2EC3AC00A50BD3 /* libkiss-db.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 348168302A2EC26F00A50BD3 /* libkiss-db.a */; };
348168412A2EE0C600A50BD3 /* KissMemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168402A2EE0C600A50BD3 /* KissMemeTests.swift */; };
348168422A2EE0C600A50BD3 /* KissMeme.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 349942482A217D85009457A4 /* KissMeme.framework */; };
3481684B2A2F961500A50BD3 /* CString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3481684A2A2F961500A50BD3 /* CString.swift */; };
3499424D2A217D85009457A4 /* KissMeme.docc in Sources */ = {isa = PBXBuildFile; fileRef = 3499424C2A217D85009457A4 /* KissMeme.docc */; };
349942592A217D85009457A4 /* KissMeme.h in Headers */ = {isa = PBXBuildFile; fileRef = 3499424B2A217D85009457A4 /* KissMeme.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
@@ -41,6 +42,7 @@
348168312A2EC2A200A50BD3 /* kiss-db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "kiss-db.h"; path = "kiss-db/kiss-db/kiss-db.h"; sourceTree = SOURCE_ROOT; };
3481683E2A2EE0C600A50BD3 /* KissMemeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KissMemeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
348168402A2EE0C600A50BD3 /* KissMemeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissMemeTests.swift; sourceTree = "<group>"; };
3481684A2A2F961500A50BD3 /* CString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CString.swift; sourceTree = "<group>"; };
349942482A217D85009457A4 /* KissMeme.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KissMeme.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3499424B2A217D85009457A4 /* KissMeme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KissMeme.h; sourceTree = "<group>"; };
3499424C2A217D85009457A4 /* KissMeme.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = KissMeme.docc; sourceTree = "<group>"; };
@@ -110,6 +112,7 @@
3499424C2A217D85009457A4 /* KissMeme.docc */,
348168252A2E478F00A50BD3 /* KissMeme.swift */,
348168272A2E4B7700A50BD3 /* KissDB.swift */,
3481684A2A2F961500A50BD3 /* CString.swift */,
);
path = KissMeme;
sourceTree = "<group>";
@@ -258,6 +261,7 @@
files = (
3499424D2A217D85009457A4 /* KissMeme.docc in Sources */,
348168282A2E4B7700A50BD3 /* KissDB.swift in Sources */,
3481684B2A2F961500A50BD3 /* CString.swift in Sources */,
348168262A2E478F00A50BD3 /* KissMeme.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

33
KissMeme/CString.swift Normal file
View File

@@ -0,0 +1,33 @@
//
// CString.swift
// KissMeme
//
// Created by ened-book-m1 on 2023/06/07.
//
import Foundation
public class CString {
public let mutableString: UnsafeMutablePointer<CChar>
public var cstring: UnsafePointer<CChar> {
UnsafePointer(mutableString)
}
public let size: Int
public init(_ string: String) {
let count = string.utf8.count + 1
mutableString = UnsafeMutablePointer<CChar>.allocate(capacity: count)
size = count
string.withCString { (baseAddress) in
mutableString.initialize(from: baseAddress, count: count)
}
}
deinit {
mutableString.deallocate()
}
}

View File

@@ -15,8 +15,18 @@ public enum KissDBError: Error {
public class KissDB: NSObject {
let cPtr: OpaquePointer
let cError: UnsafeMutablePointer<kiss_db_error_t>!
public struct Item {
public let key: String
public let value: String
public init(key: String, value: String) {
self.key = key
self.value = value
}
}
private let cPtr: OpaquePointer
private let cError: UnsafeMutablePointer<kiss_db_error_t>!
public init(directory: URL) throws {
let cConfig = UnsafeMutablePointer<kiss_db_config_t>.allocate(capacity: 1)
@@ -35,7 +45,7 @@ public class KissDB: NSObject {
}
public var count: Int64 {
Int64(kiss_db_count(cPtr, cError))
Int64(kiss_db_count(cPtr))
}
deinit {
@@ -61,38 +71,112 @@ public class KissDB: NSObject {
}
}
public func add(key: String, value: String) throws -> Bool {
let item = UnsafeMutablePointer<kiss_db_item_t>.allocate(capacity: 1)
public func insert(item: Item) throws {
let cItem = UnsafeMutablePointer<kiss_db_item_t>.allocate(capacity: 1)
defer {
item.deallocate()
cItem.deallocate()
}
let key = CString(key)
let value = CString(value)
let key = CString(item.key)
let value = CString(item.value)
item.pointee.key = UnsafeMutableRawPointer(key.mutableString)
item.pointee.key_size = UInt32(key.size)
item.pointee.data = UnsafeMutableRawPointer(key.mutableString)
item.pointee.data_size = UInt64(value.size)
cItem.pointee.key = UnsafeMutableRawPointer(key.mutableString)
cItem.pointee.key_size = UInt32(key.size)
cItem.pointee.data = UnsafeMutableRawPointer(key.mutableString)
cItem.pointee.data_size = UInt64(value.size)
guard kiss_db_insert(cPtr, item, true, cError) else {
guard kiss_db_insert(cPtr, cItem, true, cError) else {
throw KissDBError.general(cError.code, cError.message)
}
}
public func insertBulk(from: ((Item)->Bool)?) throws {
let obj = CallbackObject(itemCallback: from)
let userPtr = Unmanaged.passUnretained(obj).toOpaque()
guard kiss_db_insert_bulk(cPtr, true, CallbackObject.insertCallback, userPtr, cError) else {
throw KissDBError.general(cError.code, cError.message)
}
}
public func delete(key: String, output: ((Item)->Bool)?) throws {
let key = CString(key)
let obj = CallbackObject(itemCallback: output)
let userPtr = Unmanaged.passUnretained(obj).toOpaque()
guard kiss_db_delete(cPtr, key.mutableString, UInt32(key.size), CallbackObject.deleteCallback, userPtr, cError) else {
throw KissDBError.general(cError.code, cError.message)
}
}
public func select(key: String, output: ((Item)->Bool)?) throws {
let key = CString(key)
let obj = CallbackObject(itemCallback: output)
let userPtr = Unmanaged.passUnretained(obj).toOpaque()
guard kiss_db_select(cPtr, key.mutableString, UInt32(key.size), CallbackObject.selectCallback, userPtr, cError) else {
throw KissDBError.general(cError.code, cError.message)
}
}
public func selectAll(output: ((Item)->Bool)?) throws {
let obj = CallbackObject(itemCallback: output)
let userPtr = Unmanaged.passUnretained(obj).toOpaque()
guard kiss_db_select_bulk(cPtr, CallbackObject.selectCallback, userPtr, cError) else {
throw KissDBError.general(cError.code, cError.message)
}
}
}
class CallbackObject: NSObject {
let itemCallback: ((KissDB.Item)->Bool)?
init(itemCallback: ((KissDB.Item)->Bool)?) {
self.itemCallback = itemCallback
}
static let insertCallback: kiss_insert_callback = { (item: UnsafeMutablePointer<kiss_db_item_t>?, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in
let obj = Unmanaged<CallbackObject>.fromOpaque(userPtr!).takeUnretainedValue()
if let itemCallback = obj.itemCallback, let item = item {
let cKey = item.pointee.key.bindMemory(to: CChar.self, capacity: Int(item.pointee.key_size))
let cValue = item.pointee.data.bindMemory(to: CChar.self, capacity: Int(item.pointee.data_size))
let key = String(cString: cKey)
let value = String(cString: cValue)
let item = KissDB.Item(key: key, value: value)
return itemCallback(item)
}
// Continue to insert remained items
return true
}
public func remove(key: String) throws -> Bool {
//
// let key = CString(key)
//
// guard kiss_db_delete(cPtr, key.mutableString, UInt32(key.size), KissDB.deleteCallback, cError) else {
// throw KissDBError.general(cError.code, cError.message)
// }
static let deleteCallback: kiss_delete_callback = { (item: UnsafeMutablePointer<kiss_db_item_t>!, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in
let obj = Unmanaged<CallbackObject>.fromOpaque(userPtr!).takeUnretainedValue()
if let itemCallback = obj.itemCallback, let item = item {
let cKey = item.pointee.key.bindMemory(to: CChar.self, capacity: Int(item.pointee.key_size))
let cValue = item.pointee.data.bindMemory(to: CChar.self, capacity: Int(item.pointee.data_size))
let key = String(cString: cKey)
let value = String(cString: cValue)
let item = KissDB.Item(key: key, value: value)
return itemCallback(item)
}
// Continue to delete remained items
return true
}
// static func deleteCallback(UnsafeMutablePointer<kiss_db_item_t>?) -> kiss_bool = { item in
// return true
// }
static let selectCallback: kiss_select_callback = { (item: UnsafeMutablePointer<kiss_db_item_t>!, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in
let obj = Unmanaged<CallbackObject>.fromOpaque(userPtr!).takeUnretainedValue()
if let itemCallback = obj.itemCallback, let item = item {
let cKey = item.pointee.key.bindMemory(to: CChar.self, capacity: Int(item.pointee.key_size))
let cValue = item.pointee.data.bindMemory(to: CChar.self, capacity: Int(item.pointee.data_size))
let key = String(cString: cKey)
let value = String(cString: cValue)
let item = KissDB.Item(key: key, value: value)
return itemCallback(item)
}
// Continue to select remained items
return true
}
}
@@ -107,26 +191,8 @@ extension UnsafeMutablePointer where Pointee == kiss_db_error_t {
}
public class CString {
public let mutableString: UnsafeMutablePointer<CChar>
public var cstring: UnsafePointer<CChar> {
UnsafePointer(mutableString)
}
public let size: Int
public init(_ string: String) {
let count = string.utf8.count
mutableString = UnsafeMutablePointer<CChar>.allocate(capacity: count)
size = count
string.withCString { (baseAddress) in
mutableString.initialize(from: baseAddress, count: count)
}
}
deinit {
mutableString.deallocate()
extension KissDB {
public static var appTime: TimeInterval {
ProcessInfo.processInfo.systemUptime
}
}

View File

@@ -26,22 +26,35 @@ final class KissMemeTests: XCTestCase {
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
let directory = URL.currentDirectory().appending(path: "testdb")
try? FileManager.default.removeItem(at: directory)
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
print(directory.path)
do {
let db = try KissDB(directory: directory)
print("DB count: \(db.count)")
try db.begin()
_ = try db.add(key: "hello", value: "world")
try db.commit()
print("DB count: \(db.count)")
} catch {
print(error)
var startTime: TimeInterval, endTime: TimeInterval
let db = try KissDB(directory: directory)
startTime = KissDB.appTime
try db.begin()
print("DB count: \(db.count)")
for i in 0 ..< 1_000_000 {
try db.insert(item: KissDB.Item(key: "hello\(i)", value: "world\(i)"))
}
try db.commit()
endTime = KissDB.appTime
print("DB count: \(db.count) insert elapsed: \(endTime - startTime)")
startTime = KissDB.appTime
try db.begin()
for i in 0 ..< 1_000_000 {
try db.delete(key: "hello\(i)", output: { item in
print("\(item.value)")
return true
})
}
try db.commit()
endTime = KissDB.appTime
print("DB count: \(db.count) delete elapsed: \(endTime - startTime)")
}
func testPerformanceExample() throws {

View File

@@ -23,15 +23,23 @@
return val; \
}
#define CHECK_RET(expr, val) \
if ((rc = (expr)) != MDB_SUCCESS) { \
return val; \
}
#define GIGA_BYTE_SIZE (1024UL * 1024 * 1024)
#define TERA_BYTE_SIZE (1024UL * GIGA_BYTE_SIZE)
static kiss_uint64 _kiss_db_update_count(kiss_db_ptr ptr);
typedef struct kiss_db {
MDB_env* env;
MDB_dbi dbi;
MDB_txn* txn;
MDB_cursor* cursor;
kiss_uint64 count;
} kiss_db_t;
@@ -58,7 +66,6 @@ void kiss_db_destroy(kiss_db_ptr ptr)
return;
}
mdb_cursor_close(ptr->cursor);
mdb_txn_abort(ptr->txn);
mdb_dbi_close(ptr->env, ptr->dbi);
mdb_env_close(ptr->env);
free(ptr);
@@ -72,22 +79,24 @@ kiss_bool kiss_db_begin(kiss_db_ptr ptr, kiss_db_error_t* error)
CHECK_RET_ERROR(mdb_txn_begin(ptr->env, NULL, 0, &ptr->txn), false);
CHECK_RET_ERROR(mdb_dbi_open(ptr->txn, NULL, MDB_CREATE|MDB_DUPSORT, &ptr->dbi), false);
_kiss_db_update_count(ptr);
return true;
}
kiss_bool kiss_db_commit(kiss_db_ptr ptr, kiss_db_error_t* error)
{
_kiss_db_update_count(ptr);
mdb_txn_commit(ptr->txn);
return true;
}
kiss_bool kiss_db_rollback(kiss_db_ptr ptr, kiss_db_error_t* error)
{
_kiss_db_update_count(ptr);
mdb_txn_abort(ptr->txn);
return true;
}
@@ -99,31 +108,36 @@ kiss_bool kiss_db_insert(kiss_db_ptr ptr, kiss_db_item_t* item, kiss_bool update
MDB_val key, data;
if (update) {
if (update == false) {
flags |= MDB_NOOVERWRITE;
}
key.mv_data = item->key;
key.mv_size = item->key_size;
data.mv_data = item->data;
data.mv_size = item->data_size;
CHECK_RET_ERROR(mdb_put(ptr->txn, ptr->dbi, &key, &data, flags), false);
return true;
}
kiss_bool kiss_db_insert_bulk(kiss_db_ptr ptr, kiss_bool update, kiss_insert_callback callback, kiss_db_error_t* error)
kiss_bool kiss_db_insert_bulk(kiss_db_ptr ptr, kiss_bool update, kiss_insert_callback callback, void* callback_user_ptr, kiss_db_error_t* error)
{
int rc = 0;
unsigned int flags = 0;
MDB_val key, data;
if (update) {
if (update == false) {
flags |= MDB_NOOVERWRITE;
}
kiss_db_item_t item;
while (true) {
if (false == callback(&item)) {
if (false == callback(&item, callback_user_ptr)) {
break;
}
key.mv_data = item.key;
@@ -138,7 +152,7 @@ kiss_bool kiss_db_insert_bulk(kiss_db_ptr ptr, kiss_bool update, kiss_insert_cal
}
kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_delete_callback callback, kiss_db_error_t* error)
kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_delete_callback callback, void* callback_user_ptr, kiss_db_error_t* error)
{
int rc = 0;
MDB_val key, data;
@@ -148,9 +162,6 @@ kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s
kiss_db_item_t item;
item.key = key.mv_data;
item.key_size = key.mv_size;
while (true) {
rc = mdb_del(ptr->txn, ptr->dbi, &key, &data);
if (rc == MDB_NOTFOUND) {
@@ -161,7 +172,7 @@ kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s
item.data = data.mv_data;
item.data_size = data.mv_size;
if (false == callback(&item)) {
if (false == callback(&item, callback_user_ptr)) {
break;
}
}
@@ -169,7 +180,7 @@ kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s
}
kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_select_callback callback, kiss_db_error_t* error)
kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_select_callback callback, void* callback_user_ptr, kiss_db_error_t* error)
{
int rc = 0;
MDB_val key, data;
@@ -183,11 +194,11 @@ kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s
while ((rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_NEXT_DUP)) == 0) {
item.key = key.mv_data;
item.key_size = key.mv_size;
item.key_size = (kiss_uint)key.mv_size;
item.data = data.mv_data;
item.data_size = data.mv_size;
callback(&item);
callback(&item, callback_user_ptr);
}
mdb_cursor_close(ptr->cursor);
@@ -195,7 +206,7 @@ kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s
}
kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, kiss_db_error_t* error)
kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, void* callback_user_ptr, kiss_db_error_t* error)
{
int rc = 0;
MDB_val key, data;
@@ -206,11 +217,11 @@ kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, ki
while ((rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_NEXT)) == 0) {
item.key = key.mv_data;
item.key_size = key.mv_size;
item.key_size = (kiss_uint)key.mv_size;
item.data = data.mv_data;
item.data_size = data.mv_size;
callback(&item);
callback(&item, callback_user_ptr);
}
mdb_cursor_close(ptr->cursor);
@@ -218,12 +229,19 @@ kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, ki
}
kiss_int64 kiss_db_count(kiss_db_ptr ptr, kiss_db_error_t* error)
kiss_int64 kiss_db_count(kiss_db_ptr ptr)
{
return ptr->count;
}
static kiss_uint64 _kiss_db_update_count(kiss_db_ptr ptr)
{
int rc = 0;
MDB_stat stat;
CHECK_RET_ERROR(mdb_stat(ptr->txn, ptr->dbi, &stat), 0);
CHECK_RET(mdb_stat(ptr->txn, ptr->dbi, &stat), 0);
return stat.ms_entries;
ptr->count = stat.ms_entries;
return ptr->count;
}

View File

@@ -52,9 +52,9 @@ extern "C"
#endif
typedef kiss_bool (*kiss_insert_callback)(kiss_db_item_t* item);
typedef kiss_bool (*kiss_delete_callback)(kiss_db_item_t* item);
typedef kiss_bool (*kiss_select_callback)(kiss_db_item_t* item);
typedef kiss_bool (*kiss_insert_callback)(kiss_db_item_t* item, void* user_ptr);
typedef kiss_bool (*kiss_delete_callback)(kiss_db_item_t* item, void* user_ptr);
typedef kiss_bool (*kiss_select_callback)(kiss_db_item_t* item, void* user_ptr);
kiss_db_ptr kiss_db_create(const kiss_db_config_t* config, kiss_db_error_t* error);
@@ -65,14 +65,14 @@ kiss_bool kiss_db_commit(kiss_db_ptr ptr, kiss_db_error_t* error);
kiss_bool kiss_db_rollback(kiss_db_ptr ptr, kiss_db_error_t* error);
kiss_bool kiss_db_insert(kiss_db_ptr ptr, kiss_db_item_t* item, kiss_bool update, kiss_db_error_t* error);
kiss_bool kiss_db_insert_bulk(kiss_db_ptr ptr, kiss_bool update, kiss_insert_callback callback, kiss_db_error_t* error);
kiss_bool kiss_db_insert_bulk(kiss_db_ptr ptr, kiss_bool update, kiss_insert_callback callback, void* callback_user_ptr, kiss_db_error_t* error);
kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_delete_callback callback, kiss_db_error_t* error);
kiss_bool kiss_db_delete(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_delete_callback callback, void* callback_user_ptr, kiss_db_error_t* error);
kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_select_callback callback, kiss_db_error_t* error);
kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, kiss_db_error_t* error);
kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_size, kiss_select_callback callback, void* callback_user_ptr, kiss_db_error_t* error);
kiss_bool kiss_db_select_bulk(kiss_db_ptr ptr, kiss_select_callback callback, void* callback_user_ptr, kiss_db_error_t* error);
kiss_int64 kiss_db_count(kiss_db_ptr ptr, kiss_db_error_t* error);
kiss_int64 kiss_db_count(kiss_db_ptr ptr);
#ifdef __cplusplus