diff --git a/KissMeme/KissDB.swift b/KissMeme/KissDB.swift index e142020..951468b 100644 --- a/KissMeme/KissDB.swift +++ b/KissMeme/KissDB.swift @@ -81,7 +81,7 @@ public class KissDB: NSObject { cItem.pointee.key = UnsafeMutableRawPointer(key.mutableString) cItem.pointee.key_size = UInt64(key.size) - cItem.pointee.data = UnsafeMutableRawPointer(key.mutableString) + cItem.pointee.data = UnsafeMutableRawPointer(value.mutableString) cItem.pointee.data_size = UInt64(value.size) guard kiss_db_insert(cPtr, cItem, true, cError) else { @@ -89,11 +89,11 @@ public class KissDB: NSObject { } } - public func insert(from: ((Item)->Bool)?) throws { - let obj = CallbackObject(itemCallback: from) + public func insert(from: @escaping ((Int)->Item?), count: Int) throws { + let obj = InsertCallbackObject(insertCallback: from, maxCount: count) let userPtr = Unmanaged.passUnretained(obj).toOpaque() - guard kiss_db_insert_from(cPtr, true, CallbackObject.insertCallback, userPtr, cError) else { + guard kiss_db_insert_from(cPtr, false, InsertCallbackObject.insertCallback, userPtr, cError) else { throw KissDBError.general(cError.code, cError.message) } } @@ -109,9 +109,9 @@ public class KissDB: NSObject { public func delete(key: String, into: ((Item)->Bool)?) throws { let key = CString(key) - let obj = CallbackObject(itemCallback: into) + let obj = ItemCallbackObject(itemCallback: into) let userPtr = Unmanaged.passUnretained(obj).toOpaque() - guard kiss_db_delete_into(cPtr, key.mutableString, UInt32(key.size), CallbackObject.deleteCallback, userPtr, cError) else { + guard kiss_db_delete_into(cPtr, key.mutableString, UInt32(key.size), ItemCallbackObject.deleteCallback, userPtr, cError) else { throw KissDBError.general(cError.code, cError.message) } } @@ -119,24 +119,62 @@ public class KissDB: NSObject { public func select(key: String, into: ((Item)->Bool)?) throws { let key = CString(key) - let obj = CallbackObject(itemCallback: into) + let obj = ItemCallbackObject(itemCallback: into) let userPtr = Unmanaged.passUnretained(obj).toOpaque() - guard kiss_db_select(cPtr, key.mutableString, UInt32(key.size), CallbackObject.selectCallback, userPtr, cError) else { + guard kiss_db_select(cPtr, key.mutableString, UInt32(key.size), ItemCallbackObject.selectCallback, userPtr, cError) else { throw KissDBError.general(cError.code, cError.message) } } public func select(into: ((Item)->Bool)?) throws { - let obj = CallbackObject(itemCallback: into) + let obj = ItemCallbackObject(itemCallback: into) let userPtr = Unmanaged.passUnretained(obj).toOpaque() - guard kiss_db_select_all(cPtr, CallbackObject.selectCallback, userPtr, cError) else { + guard kiss_db_select_all(cPtr, ItemCallbackObject.selectCallback, userPtr, cError) else { throw KissDBError.general(cError.code, cError.message) } } } -class CallbackObject: NSObject { +class InsertCallbackObject: NSObject { + + let insertCallback: ((Int)->KissDB.Item?) + let maxCount: Int + + private var index: Int = 0 + private var cKey: CString? + private var cValue: CString? + + init(insertCallback: @escaping ((Int)->KissDB.Item?), maxCount: Int) { + self.insertCallback = insertCallback + self.maxCount = maxCount + } + + static let insertCallback: kiss_insert_callback = { (item: UnsafeMutablePointer?, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in + let obj = Unmanaged.fromOpaque(userPtr!).takeUnretainedValue() + guard let item = item else { + return false + } + + guard obj.index < obj.maxCount, let newItem = obj.insertCallback(obj.index) else { + return false + } + obj.index += 1 + obj.cKey = CString(newItem.key) + obj.cValue = CString(newItem.value) + + item.pointee.key = UnsafeMutableRawPointer(obj.cKey!.mutableString) + item.pointee.key_size = UInt64(obj.cKey!.size) + item.pointee.data = UnsafeMutableRawPointer(obj.cValue!.mutableString) + item.pointee.data_size = UInt64(obj.cValue!.size) + + // Continue to insert remained items + return true + } +} + + +class ItemCallbackObject: NSObject { let itemCallback: ((KissDB.Item)->Bool)? @@ -144,28 +182,8 @@ class CallbackObject: NSObject { self.itemCallback = itemCallback } - static let insertCallback: kiss_insert_callback = { (item: UnsafeMutablePointer?, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in - let obj = Unmanaged.fromOpaque(userPtr!).takeUnretainedValue() - if let itemCallback = obj.itemCallback, let item = item { - KissDB.Item(key: , value: <#T##String#>) - - if itemCallback(item) { - item.pointee.key = item.pointee.key.bindMemory(to: CChar.self, capacity: Int(item.pointee.key_size)) - item.pointee.key_size = item.po - item.pointee.data = item.pointee.data.bindMemory(to: CChar.self, capacity: Int(item.pointee.data_size)) - } - 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) - } - // Continue to insert remained items - return true - } - static let deleteCallback: kiss_delete_callback = { (item: UnsafeMutablePointer!, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in - let obj = Unmanaged.fromOpaque(userPtr!).takeUnretainedValue() + let obj = Unmanaged.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)) @@ -179,7 +197,7 @@ class CallbackObject: NSObject { } static let selectCallback: kiss_select_callback = { (item: UnsafeMutablePointer!, userPtr: UnsafeMutableRawPointer?) -> kiss_bool in - let obj = Unmanaged.fromOpaque(userPtr!).takeUnretainedValue() + let obj = Unmanaged.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)) diff --git a/KissMemeTests/KissMemeTests.swift b/KissMemeTests/KissMemeTests.swift index 5cbf704..7fd1f88 100644 --- a/KissMemeTests/KissMemeTests.swift +++ b/KissMemeTests/KissMemeTests.swift @@ -27,7 +27,7 @@ final class KissMemeTests: XCTestCase { } - private func testInsert(db: KissDB, maxItems: Int) throws { + private func testInsertEach(db: KissDB, maxItems: Int) throws { let startTime = KissDB.appTime try db.begin() print("DB count: \(db.count)") @@ -47,17 +47,13 @@ final class KissMemeTests: XCTestCase { try db.begin() print("DB count: \(db.count)") - for i in 0 ..< maxItems { - try db.insert(from: { item in - item.key = "hello\(i)" - item.value = "world\(i)" - return true - }) - } + try db.insert(from: { (index) in + KissDB.Item(key: "hello\(index)", value: "world\(index)") + }, count: maxItems) try db.commit() let endTime = KissDB.appTime - print("DB count: \(db.count) insert elapsed: \(endTime - startTime)") + print("DB count: \(db.count) insertAll elapsed: \(endTime - startTime)") } @@ -66,8 +62,11 @@ final class KissMemeTests: XCTestCase { try db.begin() print("DB count: \(db.count)") + var index = 0 try db.select(into: { item in - print("\(item.value)") + XCTAssertTrue(item.key == "hello\(index)") + XCTAssertTrue(item.value == "world\(index)") + index += 1 return true }) @@ -84,7 +83,9 @@ final class KissMemeTests: XCTestCase { for i in 0 ..< maxItems { try db.select(key: "hello\(i)", into: { item in - print("\(item.value)") + //print("\(item.key) \(item.value)", "hello\(i) world\(i)") + XCTAssertTrue(item.key == "hello\(i)") + XCTAssertTrue(item.value == "world\(i)") return true }) } @@ -102,7 +103,9 @@ final class KissMemeTests: XCTestCase { for i in 0 ..< maxItems { try db.delete(key: "hello\(i)", into: { item in - print("\(item.value)") + //print("\(item.key) \(item.value)", "hello\(i) world\(i)") + XCTAssertTrue(item.key == "hello\(i)") + XCTAssertTrue(item.value == "world\(i)") return true }) } @@ -138,19 +141,27 @@ final class KissMemeTests: XCTestCase { let maxItems = 10 let db = try KissDB(directory: testDBDirectory) - try testInsert(db: db, maxItems: maxItems) - try testSelectAll(db: db, maxItems: maxItems) + try testInsertEach(db: db, maxItems: maxItems) try testSelectEach(db: db, maxItems: maxItems) + try testDeleteEach(db: db, maxItems: maxItems) try testInsertAll(db: db, maxItems: maxItems) - try testDeleteEach(db: db, maxItems: maxItems) + try testSelectAll(db: db, maxItems: maxItems) + try testDeleteAll(db: db, maxItems: maxItems) } func testPerformanceExample() throws { + let maxItems = 1_000_000 + let db = try KissDB(directory: testDBDirectory) + // This is an example of a performance test case. measure { // Put the code you want to measure the time of here. + do { + try testInsertEach(db: db, maxItems: maxItems) + } catch { + print(error) + } } } - } diff --git a/kiss-db/kiss-db/kiss-db.c b/kiss-db/kiss-db/kiss-db.c index 717be0c..324b32d 100644 --- a/kiss-db/kiss-db/kiss-db.c +++ b/kiss-db/kiss-db/kiss-db.c @@ -196,7 +196,8 @@ kiss_bool kiss_db_delete_into(kiss_db_ptr ptr, const void* in_key, kiss_uint in_ break; } CHECK_RET_ERROR(mdb_cursor_del(ptr->cursor, 0), false); - } while ((rc = mdb_cursor_get(ptr->cursor, &key, NULL, MDB_NEXT_DUP)) == 0); + + } while ((rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_NEXT_DUP)) == 0); } mdb_cursor_close(ptr->cursor); @@ -213,19 +214,34 @@ kiss_bool kiss_db_select(kiss_db_ptr ptr, const void* in_key, kiss_uint in_key_s key.mv_data = (void*)in_key; key.mv_size = in_key_size; - + data.mv_data = NULL; + data.mv_size = 0; + kiss_db_item_t item; - while ((rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_NEXT_DUP)) == 0) { + rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_SET); + if (rc == MDB_NOTFOUND) { + return true; + } + CHECK_RET_ERROR(rc, false); + + do { item.key = key.mv_data; item.key_size = key.mv_size; item.data = data.mv_data; item.data_size = data.mv_size; - + if (false == callback(&item, callback_user_ptr)) { break; } - } + + rc = mdb_cursor_get(ptr->cursor, &key, &data, MDB_NEXT_DUP); + if (rc == MDB_NOTFOUND) { + return true; + } + CHECK_RET_ERROR(rc, false); + + } while (true); mdb_cursor_close(ptr->cursor); return true; @@ -247,7 +263,9 @@ kiss_bool kiss_db_select_all(kiss_db_ptr ptr, kiss_select_callback callback, voi item.data = data.mv_data; item.data_size = data.mv_size; - callback(&item, callback_user_ptr); + if (false == callback(&item, callback_user_ptr)) { + break; + } } mdb_cursor_close(ptr->cursor);