diff --git a/ObjectiveRocks.xcodeproj/project.pbxproj b/ObjectiveRocks.xcodeproj/project.pbxproj index 1eb5aa8..ca2e3ca 100644 --- a/ObjectiveRocks.xcodeproj/project.pbxproj +++ b/ObjectiveRocks.xcodeproj/project.pbxproj @@ -8,25 +8,39 @@ /* Begin PBXBuildFile section */ 620494871A3284A800A0E950 /* RocksDBSnapshot.mm in Sources */ = {isa = PBXBuildFile; fileRef = 620494861A3284A800A0E950 /* RocksDBSnapshot.mm */; }; + 620629601A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6206295F1A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.mm */; }; + 620629631A50ECCA001DEDC4 /* RocksDBEncodingOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 620629621A50ECCA001DEDC4 /* RocksDBEncodingOptions.mm */; }; + 620629661A510056001DEDC4 /* RocksDBDatabaseOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 620629651A510056001DEDC4 /* RocksDBDatabaseOptions.mm */; }; + 6206296A1A510D36001DEDC4 /* RocksDBColumnFamily.mm in Sources */ = {isa = PBXBuildFile; fileRef = 620629691A510D36001DEDC4 /* RocksDBColumnFamily.mm */; }; + 62074F2D1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62074F2C1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.mm */; }; 620A2CAE1A3654D5007224A4 /* RocksDBMergeOperatorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 620A2CAD1A3654D5007224A4 /* RocksDBMergeOperatorTests.mm */; }; + 6214FD091A3F698300B92E5C /* RocksDBCallbackMergeOperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6214FD071A3F698300B92E5C /* RocksDBCallbackMergeOperator.cpp */; }; 62269EDC1A1FF27A005A58D3 /* checkpoint.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62269EDB1A1FF27A005A58D3 /* checkpoint.cc */; }; 62269EE01A1FF2DC005A58D3 /* leveldb_options.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62269EDF1A1FF2DC005A58D3 /* leveldb_options.cc */; }; 623224AC1A2116A400FB6625 /* RocksDBComparator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 623224AB1A2116A400FB6625 /* RocksDBComparator.mm */; }; 6232B7351A1E80F900B14535 /* RocksDBWriteOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6232B7341A1E80F900B14535 /* RocksDBWriteOptions.mm */; }; 6232B7381A1E860700B14535 /* RocksDBReadOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6232B7371A1E860700B14535 /* RocksDBReadOptions.mm */; }; 623533A01A47722900D5CD14 /* memtable_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6235339E1A47722900D5CD14 /* memtable_allocator.cc */; }; + 6236E2571A4DD25000A81ED6 /* RocksDBPrefixExtractor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6236E2561A4DD25000A81ED6 /* RocksDBPrefixExtractor.mm */; }; + 6236E25A1A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6236E2581A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.cpp */; }; + 6236E25C1A4E19A300A81ED6 /* RocksDBPrefixExtractorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6236E25B1A4E19A300A81ED6 /* RocksDBPrefixExtractorTests.mm */; }; 62376BBF1A20EDF000C85DFB /* RocksDBCallbackComparator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62376BBD1A20EDF000C85DFB /* RocksDBCallbackComparator.cpp */; }; 62385EB21A2FC87500493F18 /* RocksDBIterator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62385EB11A2FC87500493F18 /* RocksDBIterator.mm */; }; 62385EB41A2FCFB100493F18 /* RocksDBIteratorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62385EB31A2FCFB100493F18 /* RocksDBIteratorTests.mm */; }; 62385EB61A2FD05500493F18 /* RocksDBComparatorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62385EB51A2FD05500493F18 /* RocksDBComparatorTests.mm */; }; 623DC2561A335BAF00B61B18 /* RocksDBSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 623DC2551A335BAF00B61B18 /* RocksDBSnapshotTests.mm */; }; 623FE7501A2D2ED500E68421 /* RocksDBWriteBatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 623FE74F1A2D2ED500E68421 /* RocksDBWriteBatch.mm */; }; + 62451ACA1A4BAEC000AF11C8 /* RocksDBTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 62451AC91A4BAEC000AF11C8 /* RocksDBTypes.m */; }; + 62451AD21A4BC85400AF11C8 /* thread_status_updater.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62451ACD1A4BC85400AF11C8 /* thread_status_updater.cc */; }; + 62451AD31A4BC85400AF11C8 /* thread_status_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62451ACF1A4BC85400AF11C8 /* thread_status_util.cc */; }; + 624566C81A51DA79009AC3FB /* RocksDBColumnFamilyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 624566C71A51DA79009AC3FB /* RocksDBColumnFamilyTests.mm */; }; + 6249A3CC1A4A43CA00949B07 /* RocksDBTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6249A3CB1A4A43CA00949B07 /* RocksDBTests.mm */; }; 62685CB91A2E600A009401B1 /* RocksDBWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62685CB81A2E600A009401B1 /* RocksDBWriteBatchTests.mm */; }; - 628B0CEA1A1C104D0099C39B /* ObjectiveRocksError.mm in Sources */ = {isa = PBXBuildFile; fileRef = 628B0CE91A1C104D0099C39B /* ObjectiveRocksError.mm */; }; + 628B0CEA1A1C104D0099C39B /* RocksDBError.mm in Sources */ = {isa = PBXBuildFile; fileRef = 628B0CE91A1C104D0099C39B /* RocksDBError.mm */; }; 6299F80D1A17B28200123F56 /* RocksDB.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6299F80C1A17B28200123F56 /* RocksDB.h */; }; 6299F80F1A17B28200123F56 /* RocksDB.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6299F80E1A17B28200123F56 /* RocksDB.mm */; }; 6299F8151A17B28200123F56 /* libObjectiveRocks.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6299F8091A17B28200123F56 /* libObjectiveRocks.a */; }; - 6299F8561A17B7AD00123F56 /* RocksDBTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6299F8551A17B7AD00123F56 /* RocksDBTests.mm */; }; + 6299F8561A17B7AD00123F56 /* RocksDBBasicTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6299F8551A17B7AD00123F56 /* RocksDBBasicTests.mm */; }; 62AEF9BB1A1D5DA900E63E89 /* RocksDBOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62AEF9BA1A1D5DA900E63E89 /* RocksDBOptions.mm */; }; 62EE2BD81A1ABA2000486C31 /* builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62EE29141A1ABA1F00486C31 /* builder.cc */; }; 62EE2BDA1A1ABA2000486C31 /* c.cc in Sources */ = {isa = PBXBuildFile; fileRef = 62EE29171A1ABA1F00486C31 /* c.cc */; }; @@ -162,7 +176,7 @@ 62EEC4B01A34B93B00624DA2 /* RocksDBWriteOptions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6232B7331A1E80F900B14535 /* RocksDBWriteOptions.h */; }; 62EEC4B11A34B93B00624DA2 /* RocksDBReadOptions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6232B7361A1E860700B14535 /* RocksDBReadOptions.h */; }; 62EEC4B21A34B93B00624DA2 /* RocksDBSnapshot.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 620494851A3284A800A0E950 /* RocksDBSnapshot.h */; }; - 62EEC4B31A34B93B00624DA2 /* ObjectiveRocksError.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 628B0CE81A1C104D0099C39B /* ObjectiveRocksError.h */; }; + 62EEC4B31A34B93B00624DA2 /* RocksDBError.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 628B0CE81A1C104D0099C39B /* RocksDBError.h */; }; 62EEC4B71A34FEE100624DA2 /* RocksDBMergeOperator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62EEC4B61A34FEE100624DA2 /* RocksDBMergeOperator.mm */; }; 62EEC4BA1A34FF0600624DA2 /* RocksDBCallbackAssociativeMergeOperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62EEC4B81A34FF0600624DA2 /* RocksDBCallbackAssociativeMergeOperator.cpp */; }; /* End PBXBuildFile section */ @@ -192,7 +206,7 @@ 62EEC4B01A34B93B00624DA2 /* RocksDBWriteOptions.h in CopyFiles */, 62EEC4B11A34B93B00624DA2 /* RocksDBReadOptions.h in CopyFiles */, 62EEC4B21A34B93B00624DA2 /* RocksDBSnapshot.h in CopyFiles */, - 62EEC4B31A34B93B00624DA2 /* ObjectiveRocksError.h in CopyFiles */, + 62EEC4B31A34B93B00624DA2 /* RocksDBError.h in CopyFiles */, 6299F80D1A17B28200123F56 /* RocksDB.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -202,7 +216,19 @@ /* Begin PBXFileReference section */ 620494851A3284A800A0E950 /* RocksDBSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBSnapshot.h; sourceTree = ""; }; 620494861A3284A800A0E950 /* RocksDBSnapshot.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBSnapshot.mm; sourceTree = ""; }; + 6206295E1A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBColumnFamilyOptions.h; sourceTree = ""; }; + 6206295F1A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBColumnFamilyOptions.mm; sourceTree = ""; }; + 620629611A50ECCA001DEDC4 /* RocksDBEncodingOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBEncodingOptions.h; sourceTree = ""; }; + 620629621A50ECCA001DEDC4 /* RocksDBEncodingOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBEncodingOptions.mm; sourceTree = ""; }; + 620629641A510056001DEDC4 /* RocksDBDatabaseOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBDatabaseOptions.h; sourceTree = ""; }; + 620629651A510056001DEDC4 /* RocksDBDatabaseOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBDatabaseOptions.mm; sourceTree = ""; }; + 620629681A510D36001DEDC4 /* RocksDBColumnFamily.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBColumnFamily.h; sourceTree = ""; }; + 620629691A510D36001DEDC4 /* RocksDBColumnFamily.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBColumnFamily.mm; sourceTree = ""; }; + 62074F2B1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBColumnFamilyDescriptor.h; sourceTree = ""; }; + 62074F2C1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBColumnFamilyDescriptor.mm; sourceTree = ""; }; 620A2CAD1A3654D5007224A4 /* RocksDBMergeOperatorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBMergeOperatorTests.mm; sourceTree = ""; }; + 6214FD071A3F698300B92E5C /* RocksDBCallbackMergeOperator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RocksDBCallbackMergeOperator.cpp; sourceTree = ""; }; + 6214FD081A3F698300B92E5C /* RocksDBCallbackMergeOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBCallbackMergeOperator.h; sourceTree = ""; }; 62269ED91A1FF266005A58D3 /* checkpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = checkpoint.h; path = utilities/checkpoint.h; sourceTree = ""; }; 62269EDB1A1FF27A005A58D3 /* checkpoint.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = checkpoint.cc; sourceTree = ""; }; 62269EDD1A1FF2B2005A58D3 /* leveldb_options.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = leveldb_options.h; sourceTree = ""; }; @@ -217,6 +243,11 @@ 6235339F1A47722900D5CD14 /* memtable_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memtable_allocator.h; sourceTree = ""; }; 623533A11A47724D00D5CD14 /* writebuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = writebuffer.h; sourceTree = ""; }; 623533A21A4772AC00D5CD14 /* allocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = allocator.h; sourceTree = ""; }; + 6236E2551A4DD25000A81ED6 /* RocksDBPrefixExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBPrefixExtractor.h; sourceTree = ""; }; + 6236E2561A4DD25000A81ED6 /* RocksDBPrefixExtractor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBPrefixExtractor.mm; sourceTree = ""; }; + 6236E2581A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RocksDBCallbackSliceTransform.cpp; sourceTree = ""; }; + 6236E2591A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBCallbackSliceTransform.h; sourceTree = ""; }; + 6236E25B1A4E19A300A81ED6 /* RocksDBPrefixExtractorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBPrefixExtractorTests.mm; sourceTree = ""; }; 62376BBD1A20EDF000C85DFB /* RocksDBCallbackComparator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RocksDBCallbackComparator.cpp; sourceTree = ""; }; 62376BBE1A20EDF000C85DFB /* RocksDBCallbackComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBCallbackComparator.h; sourceTree = ""; }; 62385EB01A2FC87500493F18 /* RocksDBIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBIterator.h; sourceTree = ""; }; @@ -227,6 +258,19 @@ 623DC2551A335BAF00B61B18 /* RocksDBSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBSnapshotTests.mm; sourceTree = ""; }; 623FE74E1A2D2ED500E68421 /* RocksDBWriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBWriteBatch.h; sourceTree = ""; }; 623FE74F1A2D2ED500E68421 /* RocksDBWriteBatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBWriteBatch.mm; sourceTree = ""; }; + 62451AC81A4BAEC000AF11C8 /* RocksDBTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBTypes.h; sourceTree = ""; }; + 62451AC91A4BAEC000AF11C8 /* RocksDBTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RocksDBTypes.m; sourceTree = ""; }; + 62451ACB1A4BC7A600AF11C8 /* sst_dump_tool_imp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sst_dump_tool_imp.h; sourceTree = ""; }; + 62451ACC1A4BC85400AF11C8 /* thread_status_updater_debug.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_status_updater_debug.cc; sourceTree = ""; }; + 62451ACD1A4BC85400AF11C8 /* thread_status_updater.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_status_updater.cc; sourceTree = ""; }; + 62451ACE1A4BC85400AF11C8 /* thread_status_updater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_status_updater.h; sourceTree = ""; }; + 62451ACF1A4BC85400AF11C8 /* thread_status_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_status_util.cc; sourceTree = ""; }; + 62451AD01A4BC85400AF11C8 /* thread_status_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_status_util.h; sourceTree = ""; }; + 62451AD41A4BD61700AF11C8 /* sst_dump_tool.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sst_dump_tool.cc; sourceTree = ""; }; + 62451AD51A4BD63D00AF11C8 /* sst_dump_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sst_dump_test.cc; sourceTree = ""; }; + 624566C71A51DA79009AC3FB /* RocksDBColumnFamilyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBColumnFamilyTests.mm; sourceTree = ""; }; + 6249A3CB1A4A43CA00949B07 /* RocksDBTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBTests.mm; sourceTree = ""; }; + 6249A3CD1A4A6E4000949B07 /* RocksDBTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RocksDBTests.h; sourceTree = ""; }; 62685CB81A2E600A009401B1 /* RocksDBWriteBatchTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBWriteBatchTests.mm; sourceTree = ""; }; 626A7D4C1A27EC570098A979 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 626A7D4D1A27EC570098A979 /* API.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = API.md; sourceTree = ""; }; @@ -237,15 +281,15 @@ 626A7D521A27EC570098A979 /* rdb.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rdb.cc; sourceTree = ""; }; 626A7D531A27EC570098A979 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 626A7D541A27EC570098A979 /* unit_test.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = unit_test.js; sourceTree = ""; }; - 628B0CE81A1C104D0099C39B /* ObjectiveRocksError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectiveRocksError.h; sourceTree = ""; }; - 628B0CE91A1C104D0099C39B /* ObjectiveRocksError.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ObjectiveRocksError.mm; sourceTree = ""; }; + 628B0CE81A1C104D0099C39B /* RocksDBError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBError.h; sourceTree = ""; }; + 628B0CE91A1C104D0099C39B /* RocksDBError.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBError.mm; sourceTree = ""; }; 628C51C71A3BD08500B96F8F /* RocksDBSnapshotUnavailable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RocksDBSnapshotUnavailable.h; sourceTree = ""; }; 6299F8091A17B28200123F56 /* libObjectiveRocks.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libObjectiveRocks.a; sourceTree = BUILT_PRODUCTS_DIR; }; 6299F80C1A17B28200123F56 /* RocksDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RocksDB.h; sourceTree = ""; }; 6299F80E1A17B28200123F56 /* RocksDB.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDB.mm; sourceTree = ""; }; 6299F8141A17B28200123F56 /* ObjectiveRocksTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ObjectiveRocksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6299F81A1A17B28200123F56 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 6299F8551A17B7AD00123F56 /* RocksDBTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBTests.mm; sourceTree = ""; }; + 6299F8551A17B7AD00123F56 /* RocksDBBasicTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBBasicTests.mm; sourceTree = ""; }; 62AEF9B81A1D5CF000E63E89 /* ObjectiveRocks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjectiveRocks.h; sourceTree = ""; }; 62AEF9B91A1D5DA900E63E89 /* RocksDBOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RocksDBOptions.h; sourceTree = ""; }; 62AEF9BA1A1D5DA900E63E89 /* RocksDBOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RocksDBOptions.mm; sourceTree = ""; }; @@ -258,7 +302,6 @@ 62EE29051A1ABA1F00486C31 /* build_detect_version */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = build_detect_version; sourceTree = ""; }; 62EE29061A1ABA1F00486C31 /* fbcode_config.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = fbcode_config.sh; sourceTree = ""; }; 62EE29071A1ABA1F00486C31 /* format-diff.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "format-diff.sh"; sourceTree = ""; }; - 62EE29081A1ABA1F00486C31 /* mac-install-gflags.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "mac-install-gflags.sh"; sourceTree = ""; }; 62EE29091A1ABA1F00486C31 /* make_new_version.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = make_new_version.sh; sourceTree = ""; }; 62EE290A1A1ABA1F00486C31 /* make_package.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = make_package.sh; sourceTree = ""; }; 62EE290B1A1ABA1F00486C31 /* regression_build_test.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = regression_build_test.sh; sourceTree = ""; }; @@ -368,17 +411,6 @@ 62EE29971A1ABA1F00486C31 /* write_controller_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write_controller_test.cc; sourceTree = ""; }; 62EE29981A1ABA1F00486C31 /* write_thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write_thread.cc; sourceTree = ""; }; 62EE29991A1ABA1F00486C31 /* write_thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write_thread.h; sourceTree = ""; }; - 62EE299C1A1ABA1F00486C31 /* doc.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = doc.css; sourceTree = ""; }; - 62EE299D1A1ABA1F00486C31 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; }; - 62EE299E1A1ABA1F00486C31 /* log_format.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = log_format.txt; sourceTree = ""; }; - 62EE299F1A1ABA1F00486C31 /* rockslogo.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = rockslogo.jpg; sourceTree = ""; }; - 62EE29A01A1ABA1F00486C31 /* rockslogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rockslogo.png; sourceTree = ""; }; - 62EE29A21A1ABA1F00486C31 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; - 62EE29A31A1ABA1F00486C31 /* column_families_example.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = column_families_example.cc; sourceTree = ""; }; - 62EE29A41A1ABA1F00486C31 /* compact_files_example.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_files_example.cc; sourceTree = ""; }; - 62EE29A51A1ABA1F00486C31 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; - 62EE29A61A1ABA1F00486C31 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 62EE29A71A1ABA1F00486C31 /* simple_example.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_example.cc; sourceTree = ""; }; 62EE29A91A1ABA1F00486C31 /* env_hdfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = env_hdfs.h; sourceTree = ""; }; 62EE29AA1A1ABA1F00486C31 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; 62EE29AB1A1ABA1F00486C31 /* setup.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = setup.sh; sourceTree = ""; }; @@ -719,6 +751,8 @@ children = ( 62385EB01A2FC87500493F18 /* RocksDBIterator.h */, 62385EB11A2FC87500493F18 /* RocksDBIterator.mm */, + 6236E2551A4DD25000A81ED6 /* RocksDBPrefixExtractor.h */, + 6236E2561A4DD25000A81ED6 /* RocksDBPrefixExtractor.mm */, ); name = Iterator; sourceTree = ""; @@ -732,6 +766,17 @@ name = Comparator; sourceTree = ""; }; + 620629671A510D24001DEDC4 /* Column Family */ = { + isa = PBXGroup; + children = ( + 620629681A510D36001DEDC4 /* RocksDBColumnFamily.h */, + 620629691A510D36001DEDC4 /* RocksDBColumnFamily.mm */, + 62074F2B1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.h */, + 62074F2C1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.mm */, + ); + name = "Column Family"; + sourceTree = ""; + }; 62269EDA1A1FF27A005A58D3 /* checkpoint */ = { isa = PBXGroup; children = ( @@ -754,6 +799,12 @@ children = ( 62AEF9B91A1D5DA900E63E89 /* RocksDBOptions.h */, 62AEF9BA1A1D5DA900E63E89 /* RocksDBOptions.mm */, + 620629611A50ECCA001DEDC4 /* RocksDBEncodingOptions.h */, + 620629621A50ECCA001DEDC4 /* RocksDBEncodingOptions.mm */, + 620629641A510056001DEDC4 /* RocksDBDatabaseOptions.h */, + 620629651A510056001DEDC4 /* RocksDBDatabaseOptions.mm */, + 6206295E1A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.h */, + 6206295F1A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.mm */, 6232B7331A1E80F900B14535 /* RocksDBWriteOptions.h */, 6232B7341A1E80F900B14535 /* RocksDBWriteOptions.mm */, 6232B7361A1E860700B14535 /* RocksDBReadOptions.h */, @@ -769,6 +820,10 @@ 62376BBD1A20EDF000C85DFB /* RocksDBCallbackComparator.cpp */, 62EEC4B91A34FF0600624DA2 /* RocksDBCallbackAssociativeMergeOperator.h */, 62EEC4B81A34FF0600624DA2 /* RocksDBCallbackAssociativeMergeOperator.cpp */, + 6214FD081A3F698300B92E5C /* RocksDBCallbackMergeOperator.h */, + 6214FD071A3F698300B92E5C /* RocksDBCallbackMergeOperator.cpp */, + 6236E2591A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.h */, + 6236E2581A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.cpp */, 623D3C201A37C4FF00389207 /* RocksDBSlice.h */, ); name = Internal; @@ -833,14 +888,17 @@ 62AEF9B81A1D5CF000E63E89 /* ObjectiveRocks.h */, 6299F80C1A17B28200123F56 /* RocksDB.h */, 6299F80E1A17B28200123F56 /* RocksDB.mm */, + 62451AC81A4BAEC000AF11C8 /* RocksDBTypes.h */, + 62451AC91A4BAEC000AF11C8 /* RocksDBTypes.m */, + 628B0CE81A1C104D0099C39B /* RocksDBError.h */, + 628B0CE91A1C104D0099C39B /* RocksDBError.mm */, + 620629671A510D24001DEDC4 /* Column Family */, 620494831A32808800A0E950 /* Iterator */, 62685CBA1A2E8257009401B1 /* Write Batch */, 620494841A32809400A0E950 /* Comparator */, 62269EE21A200CD9005A58D3 /* Options */, 623DC2541A335B9300B61B18 /* Snapshot */, 62EEC4B41A34FEC500624DA2 /* Merge Operator */, - 628B0CE81A1C104D0099C39B /* ObjectiveRocksError.h */, - 628B0CE91A1C104D0099C39B /* ObjectiveRocksError.mm */, 62376BBC1A20EA4B00C85DFB /* Internal */, 62EE28FD1A1ABA1F00486C31 /* rocksdb */, ); @@ -850,12 +908,16 @@ 6299F8181A17B28200123F56 /* ObjectiveRocksTests */ = { isa = PBXGroup; children = ( - 6299F8551A17B7AD00123F56 /* RocksDBTests.mm */, + 6249A3CD1A4A6E4000949B07 /* RocksDBTests.h */, + 6249A3CB1A4A43CA00949B07 /* RocksDBTests.mm */, + 6299F8551A17B7AD00123F56 /* RocksDBBasicTests.mm */, 62685CB81A2E600A009401B1 /* RocksDBWriteBatchTests.mm */, 62385EB31A2FCFB100493F18 /* RocksDBIteratorTests.mm */, 62385EB51A2FD05500493F18 /* RocksDBComparatorTests.mm */, 623DC2551A335BAF00B61B18 /* RocksDBSnapshotTests.mm */, 620A2CAD1A3654D5007224A4 /* RocksDBMergeOperatorTests.mm */, + 6236E25B1A4E19A300A81ED6 /* RocksDBPrefixExtractorTests.mm */, + 624566C71A51DA79009AC3FB /* RocksDBColumnFamilyTests.mm */, 6299F8191A17B28200123F56 /* Supporting Files */, ); path = ObjectiveRocksTests; @@ -881,8 +943,6 @@ 62EE290F1A1ABA1F00486C31 /* CONTRIBUTING.md */, 62EE29101A1ABA1F00486C31 /* coverage */, 62EE29131A1ABA1F00486C31 /* db */, - 62EE299B1A1ABA1F00486C31 /* doc */, - 62EE29A11A1ABA1F00486C31 /* examples */, 62EE29A81A1ABA1F00486C31 /* hdfs */, 62EE29AC1A1ABA1F00486C31 /* helpers */, 62EE29B01A1ABA1F00486C31 /* HISTORY.md */, @@ -913,7 +973,6 @@ 62EE29051A1ABA1F00486C31 /* build_detect_version */, 62EE29061A1ABA1F00486C31 /* fbcode_config.sh */, 62EE29071A1ABA1F00486C31 /* format-diff.sh */, - 62EE29081A1ABA1F00486C31 /* mac-install-gflags.sh */, 62EE29091A1ABA1F00486C31 /* make_new_version.sh */, 62EE290A1A1ABA1F00486C31 /* make_package.sh */, 62EE290B1A1ABA1F00486C31 /* regression_build_test.sh */, @@ -1043,31 +1102,6 @@ path = db; sourceTree = ""; }; - 62EE299B1A1ABA1F00486C31 /* doc */ = { - isa = PBXGroup; - children = ( - 62EE299C1A1ABA1F00486C31 /* doc.css */, - 62EE299D1A1ABA1F00486C31 /* index.html */, - 62EE299E1A1ABA1F00486C31 /* log_format.txt */, - 62EE299F1A1ABA1F00486C31 /* rockslogo.jpg */, - 62EE29A01A1ABA1F00486C31 /* rockslogo.png */, - ); - path = doc; - sourceTree = ""; - }; - 62EE29A11A1ABA1F00486C31 /* examples */ = { - isa = PBXGroup; - children = ( - 62EE29A21A1ABA1F00486C31 /* .gitignore */, - 62EE29A31A1ABA1F00486C31 /* column_families_example.cc */, - 62EE29A41A1ABA1F00486C31 /* compact_files_example.cc */, - 62EE29A51A1ABA1F00486C31 /* Makefile */, - 62EE29A61A1ABA1F00486C31 /* README.md */, - 62EE29A71A1ABA1F00486C31 /* simple_example.cc */, - ); - path = examples; - sourceTree = ""; - }; 62EE29A81A1ABA1F00486C31 /* hdfs */ = { isa = PBXGroup; children = ( @@ -1460,6 +1494,9 @@ 62EE2B891A1ABA2000486C31 /* stop_watch.h */, 62EE2B8A1A1ABA2000486C31 /* string_util.cc */, 62EE2B8B1A1ABA2000486C31 /* string_util.h */, + 62451AD51A4BD63D00AF11C8 /* sst_dump_test.cc */, + 62451AD41A4BD61700AF11C8 /* sst_dump_tool.cc */, + 62451ACB1A4BC7A600AF11C8 /* sst_dump_tool_imp.h */, 62EE2B8D1A1ABA2000486C31 /* sync_point.cc */, 62EE2B8E1A1ABA2000486C31 /* sync_point.h */, 62EE2B901A1ABA2000486C31 /* testharness.cc */, @@ -1469,6 +1506,11 @@ 62EE2B941A1ABA2000486C31 /* thread_local.cc */, 62EE2B951A1ABA2000486C31 /* thread_local.h */, 62EE2B971A1ABA2000486C31 /* thread_local_test.cc */, + 62451ACC1A4BC85400AF11C8 /* thread_status_updater_debug.cc */, + 62451ACD1A4BC85400AF11C8 /* thread_status_updater.cc */, + 62451ACE1A4BC85400AF11C8 /* thread_status_updater.h */, + 62451ACF1A4BC85400AF11C8 /* thread_status_util.cc */, + 62451AD01A4BC85400AF11C8 /* thread_status_util.h */, 62EE2B981A1ABA2000486C31 /* vectorrep.cc */, 62EE2B9A1A1ABA2000486C31 /* xxhash.cc */, 62EE2B9B1A1ABA2000486C31 /* xxhash.h */, @@ -1697,6 +1739,7 @@ files = ( 62EE2C121A1ABA2000486C31 /* memtable.cc in Sources */, 62EE2D381A1ABA2100486C31 /* hash.cc in Sources */, + 6214FD091A3F698300B92E5C /* RocksDBCallbackMergeOperator.cpp in Sources */, 62EE2D3E1A1ABA2100486C31 /* hash_skiplist_rep.cc in Sources */, 62EE2D4C1A1ABA2100486C31 /* logging.cc in Sources */, 62EE2D7C1A1ABA2100486C31 /* compacted_db_impl.cc in Sources */, @@ -1715,6 +1758,7 @@ 62AEF9BB1A1D5DA900E63E89 /* RocksDBOptions.mm in Sources */, 62EE2CBD1A1ABA2000486C31 /* port_posix.cc in Sources */, 62EE2D221A1ABA2100486C31 /* comparator.cc in Sources */, + 6206296A1A510D36001DEDC4 /* RocksDBColumnFamily.mm in Sources */, 62EE2D621A1ABA2100486C31 /* skiplistrep.cc in Sources */, 62EE2D3C1A1ABA2100486C31 /* hash_linklist_rep.cc in Sources */, 623533A01A47722900D5CD14 /* memtable_allocator.cc in Sources */, @@ -1725,6 +1769,7 @@ 62EE2BD81A1ABA2000486C31 /* builder.cc in Sources */, 6299F80F1A17B28200123F56 /* RocksDB.mm in Sources */, 62EE2BEC1A1ABA2000486C31 /* db_filesnapshot.cc in Sources */, + 62451ACA1A4BAEC000AF11C8 /* RocksDBTypes.m in Sources */, 62EE2CE21A1ABA2000486C31 /* flush_block_policy.cc in Sources */, 62EE2CCA1A1ABA2000486C31 /* block_based_table_builder.cc in Sources */, 6232B7381A1E860700B14535 /* RocksDBReadOptions.mm in Sources */, @@ -1758,8 +1803,9 @@ 62EE2D291A1ABA2100486C31 /* dynamic_bloom.cc in Sources */, 62EE2D811A1ABA2100486C31 /* json_document.cc in Sources */, 62EE2C411A1ABA2000486C31 /* memenv.cc in Sources */, - 628B0CEA1A1C104D0099C39B /* ObjectiveRocksError.mm in Sources */, + 628B0CEA1A1C104D0099C39B /* RocksDBError.mm in Sources */, 62EE2D711A1ABA2100486C31 /* testutil.cc in Sources */, + 6236E2571A4DD25000A81ED6 /* RocksDBPrefixExtractor.mm in Sources */, 62269EE01A1FF2DC005A58D3 /* leveldb_options.cc in Sources */, 62EE2D8E1A1ABA2100486C31 /* uint64add.cc in Sources */, 62EE2D771A1ABA2100486C31 /* xxhash.cc in Sources */, @@ -1768,6 +1814,7 @@ 62EEC4BA1A34FF0600624DA2 /* RocksDBCallbackAssociativeMergeOperator.cpp in Sources */, 62EE2C261A1ABA2000486C31 /* transaction_log_impl.cc in Sources */, 62EE2D0A1A1ABA2000486C31 /* arena.cc in Sources */, + 620629661A510056001DEDC4 /* RocksDBDatabaseOptions.mm in Sources */, 62EE2C341A1ABA2000486C31 /* write_batch.cc in Sources */, 62EE2BF01A1ABA2000486C31 /* db_impl_debug.cc in Sources */, 62376BBF1A20EDF000C85DFB /* RocksDBCallbackComparator.cpp in Sources */, @@ -1785,8 +1832,11 @@ 62EE2D401A1ABA2100486C31 /* histogram.cc in Sources */, 62385EB21A2FC87500493F18 /* RocksDBIterator.mm in Sources */, 62EE2D791A1ABA2100486C31 /* backupable_db.cc in Sources */, + 620629601A509DF1001DEDC4 /* RocksDBColumnFamilyOptions.mm in Sources */, 62EE2D991A1ABA2100486C31 /* write_batch_with_index.cc in Sources */, + 62074F2D1A520EC2002B1885 /* RocksDBColumnFamilyDescriptor.mm in Sources */, 62EE2D041A1ABA2000486C31 /* db_repl_stress.cc in Sources */, + 62451AD21A4BC85400AF11C8 /* thread_status_updater.cc in Sources */, 62EE2BFF1A1ABA2000486C31 /* filename.cc in Sources */, 62EE2D641A1ABA2100486C31 /* slice.cc in Sources */, 62EE2D511A1ABA2100486C31 /* murmurhash.cc in Sources */, @@ -1812,9 +1862,11 @@ 62EE2D2E1A1ABA2100486C31 /* env_hdfs.cc in Sources */, 62EE2D131A1ABA2100486C31 /* blob_store.cc in Sources */, 62EE2BF41A1ABA2000486C31 /* db_iter.cc in Sources */, + 62451AD31A4BC85400AF11C8 /* thread_status_util.cc in Sources */, 62EE2D531A1ABA2100486C31 /* mutable_cf_options.cc in Sources */, 62EEC4B71A34FEE100624DA2 /* RocksDBMergeOperator.mm in Sources */, 62EE2BDD1A1ABA2000486C31 /* column_family.cc in Sources */, + 620629631A50ECCA001DEDC4 /* RocksDBEncodingOptions.mm in Sources */, 62EE2CC71A1ABA2000486C31 /* block_based_filter_block.cc in Sources */, 62EE2BF81A1ABA2000486C31 /* dbformat.cc in Sources */, 62EE2C2B1A1ABA2000486C31 /* version_edit.cc in Sources */, @@ -1829,6 +1881,7 @@ 62EE2D571A1ABA2100486C31 /* options_builder.cc in Sources */, 62EE2C211A1ABA2000486C31 /* table_cache.cc in Sources */, 62EE2D431A1ABA2100486C31 /* iostats_context.cc in Sources */, + 6236E25A1A4DD71600A81ED6 /* RocksDBCallbackSliceTransform.cpp in Sources */, 62EE2C021A1ABA2000486C31 /* flush_job.cc in Sources */, 62EE2CE91A1ABA2000486C31 /* get_context.cc in Sources */, 62EE2CCE1A1ABA2000486C31 /* block_based_table_reader.cc in Sources */, @@ -1843,10 +1896,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6299F8561A17B7AD00123F56 /* RocksDBTests.mm in Sources */, + 6299F8561A17B7AD00123F56 /* RocksDBBasicTests.mm in Sources */, + 6249A3CC1A4A43CA00949B07 /* RocksDBTests.mm in Sources */, + 624566C81A51DA79009AC3FB /* RocksDBColumnFamilyTests.mm in Sources */, 62385EB41A2FCFB100493F18 /* RocksDBIteratorTests.mm in Sources */, 62385EB61A2FD05500493F18 /* RocksDBComparatorTests.mm in Sources */, 623DC2561A335BAF00B61B18 /* RocksDBSnapshotTests.mm in Sources */, + 6236E25C1A4E19A300A81ED6 /* RocksDBPrefixExtractorTests.mm in Sources */, 62685CB91A2E600A009401B1 /* RocksDBWriteBatchTests.mm in Sources */, 620A2CAE1A3654D5007224A4 /* RocksDBMergeOperatorTests.mm in Sources */, ); diff --git a/ObjectiveRocks/ObjectiveRocks.h b/ObjectiveRocks/ObjectiveRocks.h index 9a66392..99b3750 100644 --- a/ObjectiveRocks/ObjectiveRocks.h +++ b/ObjectiveRocks/ObjectiveRocks.h @@ -7,6 +7,7 @@ // #import "RocksDB.h" +#import "RocksDBColumnFamily.h" #import "RocksDBOptions.h" #import "RocksDBReadOptions.h" #import "RocksDBWriteOptions.h" @@ -15,3 +16,6 @@ #import "RocksDBSnapshot.h" #import "RocksDBComparator.h" #import "RocksDBMergeOperator.h" + +#import "RocksDBTypes.h" +#import "RocksDBError.h" diff --git a/ObjectiveRocks/RocksDB.h b/ObjectiveRocks/RocksDB.h index 0658b52..7203f20 100644 --- a/ObjectiveRocks/RocksDB.h +++ b/ObjectiveRocks/RocksDB.h @@ -7,23 +7,35 @@ // #import +#import "RocksDBColumnFamilyDescriptor.h" #import "RocksDBOptions.h" #import "RocksDBReadOptions.h" #import "RocksDBWriteOptions.h" #import "RocksDBWriteBatch.h" #import "RocksDBIterator.h" +@class RocksDBColumnFamily; @class RocksDBSnapshot; @interface RocksDB : NSObject ++ (NSArray *)listColumnFamiliesInDatabaseAtPath:(NSString *)path; + - (instancetype)initWithPath:(NSString *)path; -- (instancetype)initWithPath:(NSString *)path andDBOptions:(void (^)(RocksDBOptions *options))options; +- (instancetype)initWithPath:(NSString *)path + andDBOptions:(void (^)(RocksDBOptions *options))options; +- (instancetype)initWithPath:(NSString *)path + columnFamilies:(RocksDBColumnFamilyDescriptor *)descriptor + andDatabaseOptions:(void (^)(RocksDBDatabaseOptions *options))options; + +- (void)close; - (void)setDefaultReadOptions:(void (^)(RocksDBReadOptions *readOptions))readOptions andWriteOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions; -- (void)close; +- (RocksDBColumnFamily *)createColumnFamilyWithName:(NSString *)name + andOptions:(void (^)(RocksDBColumnFamilyOptions *options))optionsBlock; +- (NSArray *)columnFamilies; @end @@ -31,7 +43,7 @@ - (BOOL)setObject:(id)anObject forKey:(id)aKey; - (BOOL)setObject:(id)anObject forKey:(id)aKey error:(NSError **)error; -- (BOOL)setObject:(id)anObject forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock; +- (BOOL)setObject:(id)anObject forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock; - (BOOL)setObject:(id)anObject forKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions; - (BOOL)setData:(NSData *)data forKey:(NSData *)aKey; @@ -43,6 +55,11 @@ @interface RocksDB (MergeOps) +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey; +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error; +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions; +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions; + - (BOOL)mergeObject:(id)anObject forKey:(id)aKey; - (BOOL)mergeObject:(id)anObject forKey:(id)aKey error:(NSError **)error; - (BOOL)mergeObject:(id)anObject forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions; diff --git a/ObjectiveRocks/RocksDB.mm b/ObjectiveRocks/RocksDB.mm index 3791dde..451126b 100644 --- a/ObjectiveRocks/RocksDB.mm +++ b/ObjectiveRocks/RocksDB.mm @@ -7,11 +7,14 @@ // #import "RocksDB.h" -#import "ObjectiveRocksError.h" + +#import "RocksDBColumnFamily.h" #import "RocksDBOptions.h" #import "RocksDBReadOptions.h" #import "RocksDBWriteOptions.h" #import "RocksDBSnapshot.h" + +#import "RocksDBError.h" #import "RocksDBSlice.h" #include @@ -20,8 +23,22 @@ #pragma mark - +@interface RocksDBColumnFamilyDescriptor (Private) +@property (nonatomic, assign) std::vector *columnFamilies; +@end + @interface RocksDBOptions (Private) @property (nonatomic, assign) rocksdb::Options options; +@property (nonatomic, strong) RocksDBDatabaseOptions *databaseOptions; +@property (nonatomic, strong) RocksDBColumnFamilyOptions *columnFamilyOption; +@end + +@interface RocksDBDatabaseOptions (Private) +@property (nonatomic, assign) rocksdb::DBOptions options; +@end + +@interface RocksDBColumnFamilyOptions (Private) +@property (nonatomic, assign) rocksdb::ColumnFamilyOptions options; @end @interface RocksDBReadOptions (Private) @@ -38,19 +55,29 @@ @interface RocksDBWriteBatch (Private) @interface RocksDB () { + NSString *_path; rocksdb::DB *_db; + rocksdb::ColumnFamilyHandle *_columnFamily; + std::vector *_columnFamilyHandles; + + NSMutableArray *_columnFamilies; + RocksDBOptions *_options; RocksDBReadOptions *_readOptions; RocksDBWriteOptions *_writeOptions; } +@property (nonatomic, strong) NSString *path; @property (nonatomic, assign) rocksdb::DB *db; -@property (nonatomic, retain) RocksDBOptions *options; -@property (nonatomic, retain) RocksDBReadOptions *readOptions; -@property (nonatomic, retain) RocksDBWriteOptions *writeOptions; +@property (nonatomic, assign) rocksdb::ColumnFamilyHandle *columnFamily; +@property (nonatomic, strong) RocksDBOptions *options; +@property (nonatomic, strong) RocksDBReadOptions *readOptions; +@property (nonatomic, strong) RocksDBWriteOptions *writeOptions; @end @implementation RocksDB +@synthesize path = _path; @synthesize db = _db; +@synthesize columnFamily = _columnFamily; @synthesize options = _options; @synthesize readOptions = _readOptions; @synthesize writeOptions = _writeOptions; @@ -66,15 +93,37 @@ - (instancetype)initWithPath:(NSString *)path andDBOptions:(void (^)(RocksDBOpti { self = [super init]; if (self) { + _path = [path copy]; _options = [RocksDBOptions new]; if (optionsBlock) { optionsBlock(_options); } - rocksdb::Status status = rocksdb::DB::Open(_options.options, path.UTF8String, &_db); - if (!status.ok()) { - NSLog(@"Error creating database: %@", [ObjectiveRocksError errorWithRocksStatus:status]); - [self close]; + if ([self open] == NO) { + return nil; + } + [self setDefaultReadOptions:nil andWriteOptions:nil]; + } + return self; +} + +- (instancetype)initWithPath:(NSString *)path + columnFamilies:(RocksDBColumnFamilyDescriptor *)descriptor + andDatabaseOptions:(void (^)(RocksDBDatabaseOptions *options))optionsBlock +{ + self = [super init]; + if (self) { + _path = [path copy]; + + RocksDBDatabaseOptions *dbOptions = [RocksDBDatabaseOptions new]; + if (optionsBlock) { + optionsBlock(dbOptions); + } + + _options = [RocksDBOptions new]; + _options.databaseOptions = dbOptions; + + if ([self openColumnFamilies:descriptor] == NO) { return nil; } [self setDefaultReadOptions:nil andWriteOptions:nil]; @@ -90,13 +139,110 @@ - (void)dealloc - (void)close { @synchronized(self) { - if (_db != NULL) { + [_columnFamilies makeObjectsPerformSelector:@selector(close)]; + + if (_columnFamilyHandles != nullptr) { + delete _columnFamilyHandles; + _columnFamilyHandles = nullptr; + } + + if (_db != nullptr) { delete _db; - _db = NULL; + _db = nullptr; } } } +#pragma mark - Open + +- (BOOL)open +{ + rocksdb::Status status = rocksdb::DB::Open(_options.options, _path.UTF8String, &_db); + if (!status.ok()) { + NSLog(@"Error opening database: %@", [RocksDBError errorWithRocksStatus:status]); + [self close]; + return NO; + } + _columnFamily = _db->DefaultColumnFamily(); + + return YES; +} + +- (BOOL)openColumnFamilies:(RocksDBColumnFamilyDescriptor *)descriptor +{ + std::vector *columnFamilies = descriptor.columnFamilies; + _columnFamilyHandles = new std::vector; + + rocksdb::Status status = rocksdb::DB::Open(_options.options, + _path.UTF8String, + *columnFamilies, + _columnFamilyHandles, + &_db); + + if (!status.ok()) { + NSLog(@"Error opening database: %@", [RocksDBError errorWithRocksStatus:status]); + [self close]; + return NO; + } + _columnFamily = _db->DefaultColumnFamily(); + + _columnFamilies = [NSMutableArray new]; + for(auto it = std::begin(*_columnFamilyHandles); it != std::end(*_columnFamilyHandles); ++it) { + RocksDBColumnFamily *columnFamily = [[RocksDBColumnFamily alloc] initWithDBInstance:_db + columnFamily:*it + andOptions:_options]; + [_columnFamilies addObject:columnFamily]; + } + + return YES; +} + +#pragma mark - Column Families + ++ (NSArray *)listColumnFamiliesInDatabaseAtPath:(NSString *)path +{ + std::vector names; + + rocksdb::Status status = rocksdb::DB::ListColumnFamilies(rocksdb::Options(), path.UTF8String, &names); + if (!status.ok()) { + NSLog(@"Error listing column families in database at %@: %@", path, [RocksDBError errorWithRocksStatus:status]); + } + + NSMutableArray *columnFamilies = [NSMutableArray array]; + for(auto it = std::begin(names); it != std::end(names); ++it) { + [columnFamilies addObject:[[NSString alloc] initWithCString:it->c_str() encoding:NSUTF8StringEncoding]]; + } + return columnFamilies; +} + +- (RocksDBColumnFamily *)createColumnFamilyWithName:(NSString *)name andOptions:(void (^)(RocksDBColumnFamilyOptions *options))optionsBlock +{ + RocksDBColumnFamilyOptions *columnFamilyOptions = [RocksDBColumnFamilyOptions new]; + if (optionsBlock) { + optionsBlock(columnFamilyOptions); + } + + rocksdb::ColumnFamilyHandle *handle; + rocksdb::Status status = _db->CreateColumnFamily(columnFamilyOptions.options, name.UTF8String, &handle); + if (!status.ok()) { + NSLog(@"Error creating column family: %@", [RocksDBError errorWithRocksStatus:status]); + return nil; + } + + RocksDBOptions *options = [[RocksDBOptions alloc] initWithDatabaseOptions:_options.databaseOptions + andColumnFamilyOptions:columnFamilyOptions]; + + RocksDBColumnFamily *columnFamily = [[RocksDBColumnFamily alloc] initWithDBInstance:_db + columnFamily:handle + andOptions:options]; + return columnFamily; +} + +- (NSArray *)columnFamilies +{ + return _columnFamilies; +} + #pragma mark - Read/Write Options - (void)setDefaultReadOptions:(void (^)(RocksDBReadOptions *))readOptionsBlock andWriteOptions:(void (^)(RocksDBWriteOptions *))writeOptionsBlock @@ -135,16 +281,18 @@ - (BOOL)setObject:(id)anObject error:(NSError * __autoreleasing *)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock { - if (_options.keyEncoder == nil || _options.valueEncoder == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *locError = nil; + NSData *keyData = EncodeKey(aKey, (RocksDBEncodingOptions *)_options, &locError); + NSData *valueData = EncodeValue(aKey, anObject, (RocksDBEncodingOptions *)_options, &locError); + if (locError) { if (error && *error == nil) { - *error = temp; + *error = locError; } return NO; } - return [self setData:_options.valueEncoder(aKey, anObject) - forKey:_options.keyEncoder(aKey) + return [self setData:valueData + forKey:keyData error:error writeOptions:writeOptionsBlock]; } @@ -174,11 +322,12 @@ - (BOOL)setData:(NSData *)data forKey:(NSData *)aKey } rocksdb::Status status = _db->Put(writeOptions.options, + _columnFamily, SliceFromData(aKey), SliceFromData(data)); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -190,6 +339,37 @@ - (BOOL)setData:(NSData *)data forKey:(NSData *)aKey #pragma mark - Merge Operations +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey +{ + return [self mergeOperation:aMerge forKey:aKey error:nil]; +} + +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error +{ + return [self mergeOperation:aMerge forKey:aKey error:error writeOptions:nil]; +} + +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock +{ + return [self mergeOperation:aMerge forKey:aKey error:nil writeOptions:writeOptionsBlock]; +} + +- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock +{ + NSError *locError = nil; + NSData *keyData = EncodeKey(aKey, (RocksDBEncodingOptions *)_options, &locError); + if (locError) { + if (error && *error == nil) { + *error = locError; + } + return NO; + } + + return [self mergeData:[aMerge dataUsingEncoding:NSUTF8StringEncoding] + forKey:keyData + writeOptions:writeOptionsBlock]; +} + - (BOOL)mergeObject:(id)anObject forKey:(id)aKey { return [self mergeObject:anObject forKey:aKey error:nil]; @@ -210,16 +390,18 @@ - (BOOL)mergeObject:(id)anObject error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock { - if (_options.keyEncoder == nil || _options.valueEncoder == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *locError = nil; + NSData *keyData = EncodeKey(aKey, (RocksDBEncodingOptions *)_options, &locError); + NSData *valueData = EncodeValue(aKey, anObject, (RocksDBEncodingOptions *)_options, &locError); + if (locError) { if (error && *error == nil) { - *error = temp; + *error = locError; } return NO; } - return [self mergeData:_options.valueEncoder(aKey, anObject) - forKey:_options.keyEncoder(aKey) + return [self mergeData:valueData + forKey:keyData error:error writeOptions:writeOptionsBlock]; } @@ -250,11 +432,12 @@ - (BOOL)mergeData:(NSData *)data } rocksdb::Status status = _db->Merge(_writeOptions.options, + _columnFamily, SliceFromData(aKey), SliceFromData(data)); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -283,19 +466,20 @@ - (id)objectForKey:(id)aKey readOptions:(void (^)(RocksDBReadOptions *readOption - (id)objectForKey:(id)aKey error:(NSError **)error readOptions:(void (^)(RocksDBReadOptions *readOptions))readOptionsBlock { - if (_options.keyEncoder == nil || _options.valueDecoder == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *locError = nil; + NSData *keyData = EncodeKey(aKey, (RocksDBEncodingOptions *)_options, &locError); + if (locError) { if (error && *error == nil) { - *error = temp; + *error = locError; } return nil; } - NSData *data = [self dataForKey:_options.keyEncoder(aKey) + NSData *data = [self dataForKey:keyData error:error readOptions:readOptionsBlock]; - return _options.valueDecoder(aKey, data); + return DecodeValueData(aKey, data, (RocksDBEncodingOptions *)_options, error); } - (NSData *)dataForKey:(NSData *)aKey @@ -324,10 +508,11 @@ - (NSData *)dataForKey:(NSData *)aKey std::string value; rocksdb::Status status = _db->Get(readOptions.options, + _columnFamily, SliceFromData(aKey), &value); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -358,15 +543,16 @@ - (BOOL)deleteObjectForKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptionsBlock { - if (_options.keyEncoder == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *locError = nil; + NSData *keyData = EncodeKey(aKey, (RocksDBEncodingOptions *)_options, &locError); + if (locError) { if (error && *error == nil) { - *error = temp; + *error = locError; } return NO; } - return [self deleteDataForKey:_options.keyEncoder(aKey) + return [self deleteDataForKey:keyData error:error writeOptions:writeOptionsBlock]; } @@ -396,10 +582,11 @@ - (BOOL)deleteDataForKey:(NSData *)aKey } rocksdb::Status status = _db->Delete(writeOptions.options, + _columnFamily, SliceFromData(aKey)); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -413,7 +600,7 @@ - (BOOL)deleteDataForKey:(NSData *)aKey - (RocksDBWriteBatch *)writeBatch { - return [[RocksDBWriteBatch alloc] initWithOptions:_options]; + return [[RocksDBWriteBatch alloc] initWithColumnFamily:_columnFamily andEncodingOptions:(RocksDBEncodingOptions *)_options]; } - (BOOL)performWriteBatch:(void (^)(RocksDBWriteBatch *batch, RocksDBWriteOptions *options))batchBlock @@ -433,7 +620,7 @@ - (BOOL)performWriteBatch:(void (^)(RocksDBWriteBatch *batch, RocksDBWriteOption rocksdb::Status status = _db->Write(writeOptions.options, &batch); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -458,7 +645,7 @@ - (BOOL)applyWriteBatch:(RocksDBWriteBatch *)writeBatch error:(NSError **)error rocksdb::Status status = _db->Write(writeOptions.options, &batch); if (!status.ok()) { - NSError *temp = [ObjectiveRocksError errorWithRocksStatus:status]; + NSError *temp = [RocksDBError errorWithRocksStatus:status]; if (error && *error == nil) { *error = temp; } @@ -480,9 +667,10 @@ - (RocksDBIterator *)iteratorWithReadOptions:(void (^)(RocksDBReadOptions *readO if (readOptionsBlock) { readOptionsBlock(readOptions); } - rocksdb::Iterator *iterator = _db->NewIterator(readOptions.options); + rocksdb::Iterator *iterator = _db->NewIterator(readOptions.options, + _columnFamily); - return [[RocksDBIterator alloc] initWithDBIterator:iterator andOptions:_options]; + return [[RocksDBIterator alloc] initWithDBIterator:iterator andEncodingOptions:(RocksDBEncodingOptions *)_options]; } #pragma mark - Snapshot @@ -503,7 +691,7 @@ - (RocksDBSnapshot *)snapshotWithReadOptions:(void (^)(RocksDBReadOptions *readO options.snapshot = _db->GetSnapshot(); readOptions.options = options; - RocksDBSnapshot *snapshot = [[RocksDBSnapshot alloc] initWithDBInstance:_db andReadOptions:readOptions]; + RocksDBSnapshot *snapshot = [[RocksDBSnapshot alloc] initWithDBInstance:_db columnFamily:_columnFamily andReadOptions:readOptions]; return snapshot; } diff --git a/ObjectiveRocks/RocksDBCallbackMergeOperator.cpp b/ObjectiveRocks/RocksDBCallbackMergeOperator.cpp new file mode 100644 index 0000000..539d8b9 --- /dev/null +++ b/ObjectiveRocks/RocksDBCallbackMergeOperator.cpp @@ -0,0 +1,56 @@ +// +// RocksDBCallbackMergeOperator.cpp +// ObjectiveRocks +// +// Created by Iska on 15/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#include "RocksDBCallbackMergeOperator.h" + +class RocksDBCallbackMergeOperatorImpl : public rocksdb::MergeOperator +{ +private: + void* instance; + const char* name; + PartialMergeCallback partialMergeCallback; + FullMergeCallback fullMergeCallback; +public: + RocksDBCallbackMergeOperatorImpl(void* instance, + const char* name, + PartialMergeCallback partialMergeCallback, + FullMergeCallback fullMergeCallback): + instance(instance), name(name), partialMergeCallback(partialMergeCallback), fullMergeCallback(fullMergeCallback) {} + + const char* Name() const + { + return name; + } + + virtual bool FullMerge(const rocksdb::Slice& key, + const rocksdb::Slice* existing_value, + const std::deque& operand_list, + std::string* new_value, + rocksdb::Logger* logger) const + { + return fullMergeCallback(instance, key, existing_value, operand_list, new_value, logger); + } + + virtual bool PartialMerge(const rocksdb::Slice& key, + const rocksdb::Slice& left_operand, + const rocksdb::Slice& right_operand, + std::string* new_value, + rocksdb::Logger* logger) const + { + return partialMergeCallback(instance, key, left_operand, right_operand, new_value, logger); + } +}; + +rocksdb::MergeOperator* RocksDBCallbackMergeOperator(void* instance, + const char* name, + PartialMergeCallback partialMergeCallback, + FullMergeCallback fullMergeCallback) +{ + return new RocksDBCallbackMergeOperatorImpl(instance, name, partialMergeCallback, fullMergeCallback); +} + diff --git a/ObjectiveRocks/RocksDBCallbackMergeOperator.h b/ObjectiveRocks/RocksDBCallbackMergeOperator.h new file mode 100644 index 0000000..b4cbde3 --- /dev/null +++ b/ObjectiveRocks/RocksDBCallbackMergeOperator.h @@ -0,0 +1,36 @@ +// +// RocksDBCallbackMergeOperator.h +// ObjectiveRocks +// +// Created by Iska on 15/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#ifndef __ObjectiveRocks__RocksDBCallbackMergeOperator__ +#define __ObjectiveRocks__RocksDBCallbackMergeOperator__ + +#import +#import +#import +#import + +typedef bool (* PartialMergeCallback)(void* instance, + const rocksdb::Slice& key, + const rocksdb::Slice& left_operand, + const rocksdb::Slice& right_operand, + std::string* new_value, + rocksdb::Logger* logger); + +typedef bool (* FullMergeCallback)(void* instance, + const rocksdb::Slice& key, + const rocksdb::Slice* existing_value, + const std::deque& operand_list, + std::string* new_value, + rocksdb::Logger* logger); + +extern rocksdb::MergeOperator* RocksDBCallbackMergeOperator(void* instance, + const char* name, + PartialMergeCallback partialMergeCallback, + FullMergeCallback fullMergeCallback); + +#endif /* defined(__ObjectiveRocks__RocksDBCallbackMergeOperator__) */ diff --git a/ObjectiveRocks/RocksDBCallbackSliceTransform.cpp b/ObjectiveRocks/RocksDBCallbackSliceTransform.cpp new file mode 100644 index 0000000..6fc4abf --- /dev/null +++ b/ObjectiveRocks/RocksDBCallbackSliceTransform.cpp @@ -0,0 +1,56 @@ +// +// RocksDBCallbackSliceTransform.cpp +// ObjectiveRocks +// +// Created by Iska on 26/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#include "RocksDBCallbackSliceTransform.h" + +class RocksDBCallbackSliceTransformImpl : public rocksdb::SliceTransform +{ +private: + void* instance; + const char* name; + TransformCallback transformCallback; + InDomainCallback inDomainCallback; + InRangeCallback inRangeCallback; + +public: + RocksDBCallbackSliceTransformImpl(void* instance, const char* name, + TransformCallback transform, + InDomainCallback domain, + InRangeCallback range): + instance(instance), name(name), transformCallback(transform), inDomainCallback(domain), inRangeCallback(range) {} + + virtual const char* Name() const + { + return name; + } + + virtual rocksdb::Slice Transform(const rocksdb::Slice& src) const + { + return transformCallback(instance, src); + } + + virtual bool InDomain(const rocksdb::Slice& src) const + { + return inDomainCallback(instance, src); + } + + virtual bool InRange(const rocksdb::Slice& dst) const + { + return inRangeCallback(instance, dst); + } + +}; + +rocksdb::SliceTransform* RocksDBCallbackSliceTransform(void* instance, + const char* name, + TransformCallback transform, + InDomainCallback domain, + InRangeCallback range) +{ + return new RocksDBCallbackSliceTransformImpl(instance, name, transform, domain, range); +} diff --git a/ObjectiveRocks/RocksDBCallbackSliceTransform.h b/ObjectiveRocks/RocksDBCallbackSliceTransform.h new file mode 100644 index 0000000..8389f26 --- /dev/null +++ b/ObjectiveRocks/RocksDBCallbackSliceTransform.h @@ -0,0 +1,25 @@ +// +// RocksDBCallbackSliceTransform.h +// ObjectiveRocks +// +// Created by Iska on 26/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#ifndef __ObjectiveRocks__RocksDBCallbackSliceTransform__ +#define __ObjectiveRocks__RocksDBCallbackSliceTransform__ + +#include +#import + +typedef rocksdb::Slice (* TransformCallback)(void* instance, const rocksdb::Slice& src); +typedef bool (* InDomainCallback)(void* instance, const rocksdb::Slice& src); +typedef bool (* InRangeCallback)(void* instance, const rocksdb::Slice& dst); + +extern rocksdb::SliceTransform* RocksDBCallbackSliceTransform(void* instance, + const char* name, + TransformCallback transform, + InDomainCallback domain, + InRangeCallback range); + +#endif /* defined(__ObjectiveRocks__RocksDBCallbackSliceTransform__) */ diff --git a/ObjectiveRocks/RocksDBColumnFamily.h b/ObjectiveRocks/RocksDBColumnFamily.h new file mode 100644 index 0000000..f3e19b8 --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamily.h @@ -0,0 +1,36 @@ +// +// RocksDBColumnFamily.h +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDB.h" +#import "RocksDBOptions.h" + +namespace rocksdb { + class DB; + class ColumnFamilyHandle; +} + +@interface RocksDBColumnFamily : RocksDB + +- (instancetype)initWithPath:(NSString *)path __attribute__((unavailable("Create column family via a RocksDB instance"))); +- (instancetype)initWithPath:(NSString *)path + andDBOptions:(void (^)(RocksDBOptions *options))options __attribute__((unavailable("Create column family via a RocksDB instance"))); +- (instancetype)initWithPath:(NSString *)path + columnFamilies:(RocksDBColumnFamilyDescriptor *)descriptor + andDatabaseOptions:(void (^)(RocksDBDatabaseOptions *options))options __attribute__((unavailable("Create column family via a RocksDB instance"))); + ++ (NSArray *)listColumnFamiliesInDatabaseAtPath:(NSString *)path __attribute__((unavailable("Use the superclass RocksDB instead"))); +- (RocksDBColumnFamily *)createColumnFamilyWithName:(NSString *)name + andOptions:(void (^)(RocksDBColumnFamilyOptions *options))optionsBlock __attribute__((unavailable("Use the superclass RocksDB instead"))); + +- (instancetype)initWithDBInstance:(rocksdb::DB *)db + columnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andOptions:(RocksDBOptions *)options; + +- (void)drop; + +@end diff --git a/ObjectiveRocks/RocksDBColumnFamily.mm b/ObjectiveRocks/RocksDBColumnFamily.mm new file mode 100644 index 0000000..dd48ada --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamily.mm @@ -0,0 +1,52 @@ +// +// RocksDBColumnFamily.m +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBColumnFamily.h" + +#import + +@interface RocksDB (Private) +@property (nonatomic, assign) rocksdb::DB *db; +@property (nonatomic, assign) rocksdb::ColumnFamilyHandle *columnFamily; +@property (nonatomic, retain) RocksDBOptions *options; +@property (nonatomic, retain) RocksDBReadOptions *readOptions; +@property (nonatomic, retain) RocksDBWriteOptions *writeOptions; +@end + +@implementation RocksDBColumnFamily + +- (instancetype)initWithDBInstance:(rocksdb::DB *)db + columnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andOptions:(RocksDBOptions *)options +{ + self = [super init]; + if (self) { + self.db = db; + self.columnFamily = columnFamily; + self.options = options; + [self setDefaultReadOptions:nil andWriteOptions:nil]; + } + return self; +} + +- (void)close +{ + @synchronized(self) { + if (self.columnFamily != nullptr) { + delete self.columnFamily; + self.columnFamily = nullptr; + } + } +} + +- (void)drop +{ + self.db->DropColumnFamily(self.columnFamily); +} + +@end diff --git a/ObjectiveRocks/RocksDBColumnFamilyDescriptor.h b/ObjectiveRocks/RocksDBColumnFamilyDescriptor.h new file mode 100644 index 0000000..2641d22 --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamilyDescriptor.h @@ -0,0 +1,16 @@ +// +// RocksDBColumnFamilyDescriptor.h +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "RocksDBColumnFamilyOptions.h" + +@interface RocksDBColumnFamilyDescriptor : NSObject + +- (void)addColumnFamilyWithName:(NSString *)name andOptions:(void (^)(RocksDBColumnFamilyOptions *options))options; + +@end diff --git a/ObjectiveRocks/RocksDBColumnFamilyDescriptor.mm b/ObjectiveRocks/RocksDBColumnFamilyDescriptor.mm new file mode 100644 index 0000000..6334263 --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamilyDescriptor.mm @@ -0,0 +1,58 @@ +// +// RocksDBColumnFamilyDescriptor.m +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBColumnFamilyDescriptor.h" + +#import + +@interface RocksDBColumnFamilyOptions () +@property (nonatomic, assign) rocksdb::ColumnFamilyOptions options; +@end + +@interface RocksDBColumnFamilyDescriptor () +{ + std::vector *_columnFamilies; +} +@property (nonatomic, assign) std::vector *columnFamilies; +@end + +@implementation RocksDBColumnFamilyDescriptor +@synthesize columnFamilies = _columnFamilies; + +- (instancetype)init +{ + self = [super init]; + if (self) { + _columnFamilies = new std::vector; + } + return self; +} + +- (void)dealloc +{ + @synchronized(self) { + if (_columnFamilies != NULL) { + _columnFamilies->clear(); + delete _columnFamilies; + _columnFamilies = NULL; + } + } +} + +- (void)addColumnFamilyWithName:(NSString *)name andOptions:(void (^)(RocksDBColumnFamilyOptions *options))optionsBlock +{ + RocksDBColumnFamilyOptions *options = [RocksDBColumnFamilyOptions new]; + if (optionsBlock) { + optionsBlock(options); + } + + rocksdb::ColumnFamilyDescriptor descriptor = rocksdb::ColumnFamilyDescriptor(name.UTF8String, options.options); + _columnFamilies->push_back(descriptor); +} + +@end diff --git a/ObjectiveRocks/RocksDBColumnFamilyOptions.h b/ObjectiveRocks/RocksDBColumnFamilyOptions.h new file mode 100644 index 0000000..4c46dff --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamilyOptions.h @@ -0,0 +1,46 @@ +// +// RocksDBColumnFamilyOptions.h +// ObjectiveRocks +// +// Created by Iska on 28/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "RocksDBComparator.h" +#import "RocksDBMergeOperator.h" +#import "RocksDBPrefixExtractor.h" +#import "RocksDBTypes.h" + +typedef NS_ENUM(char, RocksDBCompressionType) +{ + RocksDBCompressionNone = 0x0, + RocksDBCompressionSnappy = 0x1, + RocksDBCompressionZlib = 0x2, + RocksDBCompressionBZip2 = 0x3, + RocksDBCompressionLZ4 = 0x4, + RocksDBCompressionLZ4HC = 0x5 +}; + +@interface RocksDBColumnFamilyOptions : NSObject + +@property (nonatomic, strong) RocksDBComparator *comparator; +@property (nonatomic, strong) RocksDBMergeOperator *mergeOperator; +@property (nonatomic, strong) RocksDBPrefixExtractor *prefixExtractor; +@property (nonatomic, assign) size_t writeBufferSize; +@property (nonatomic, assign) int maxWriteBufferNumber; +@property (nonatomic, assign) RocksDBCompressionType compressionType; + +@end + +@interface RocksDBColumnFamilyOptions (Encoding) + +@property (nonatomic, copy) NSData * (^ keyEncoder)(id key); +@property (nonatomic, copy) id (^ keyDecoder)(NSData *data); +@property (nonatomic, copy) NSData * (^ valueEncoder)(id key, id value); +@property (nonatomic, copy) id (^ valueDecoder)(id key, NSData *data); + +@property (nonatomic, assign) RocksDBType keyType; +@property (nonatomic, assign) RocksDBType valueType; + +@end diff --git a/ObjectiveRocks/RocksDBColumnFamilyOptions.mm b/ObjectiveRocks/RocksDBColumnFamilyOptions.mm new file mode 100644 index 0000000..8107523 --- /dev/null +++ b/ObjectiveRocks/RocksDBColumnFamilyOptions.mm @@ -0,0 +1,140 @@ +// +// RocksDBColumnFamilyOptions.m +// ObjectiveRocks +// +// Created by Iska on 28/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBColumnFamilyOptions.h" +#import "RocksDBEncodingOptions.h" + +#import +#import +#import +#import + +@class RocksDBOptions; + +@interface RocksDBComparator () +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, assign) const rocksdb::Comparator *comparator; +@end + +@interface RocksDBMergeOperator () +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, assign) rocksdb::MergeOperator *mergeOperator; +@end + +@interface RocksDBPrefixExtractor () +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, assign) const rocksdb::SliceTransform *sliceTransform; +@end + +@interface RocksDBColumnFamilyOptions () +{ + rocksdb::ColumnFamilyOptions _options; + RocksDBEncodingOptions *_encodingOptions; + + RocksDBComparator *_comparatorWrapper; + RocksDBMergeOperator *_mergeOperatorWrapper; + RocksDBPrefixExtractor *_prefixExtractorWrapper; +} +@property (nonatomic, assign) rocksdb::ColumnFamilyOptions options; +@end + +@implementation RocksDBColumnFamilyOptions +@synthesize options = _options; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + self = [super init]; + if (self) { + _encodingOptions = [RocksDBEncodingOptions new]; + _options = rocksdb::ColumnFamilyOptions(); + } + return self; +} + +#pragma mark - Encoding Options + +-(id) forwardingTargetForSelector:(SEL)aSelector +{ + if ([_encodingOptions respondsToSelector:aSelector]) { + return _encodingOptions; + } + + return nil; +} + +#pragma mark - Column Family Options + +- (void)setComparator:(RocksDBComparator *)comparator +{ + _comparatorWrapper = comparator; + _comparatorWrapper.encodingOptions = _encodingOptions; + _options.comparator = _comparatorWrapper.comparator; +} + +- (RocksDBComparator *)comparator +{ + return _comparatorWrapper; +} + +- (void)setMergeOperator:(RocksDBMergeOperator *)mergeOperator +{ + _mergeOperatorWrapper = mergeOperator; + _mergeOperatorWrapper.encodingOptions = _encodingOptions; + _options.merge_operator.reset(_mergeOperatorWrapper.mergeOperator); +} + +- (RocksDBMergeOperator *)mergeOperator +{ + return _mergeOperatorWrapper; +} + +- (void)setPrefixExtractor:(RocksDBPrefixExtractor *)prefixExtractor +{ + _prefixExtractorWrapper = prefixExtractor; + _prefixExtractorWrapper.encodingOptions = _encodingOptions; + _options.prefix_extractor.reset(_prefixExtractorWrapper.sliceTransform); +} + +- (RocksDBPrefixExtractor *)prefixExtractor +{ + return _prefixExtractorWrapper; +} + +- (size_t)writeBufferSize +{ + return _options.write_buffer_size; +} + +- (void)setWriteBufferSize:(size_t)writeBufferSize +{ + _options.write_buffer_size = writeBufferSize; +} + +- (int)maxWriteBufferNumber +{ + return _options.max_write_buffer_number; +} + +- (void)setMaxWriteBufferNumber:(int)maxWriteBufferNumber +{ + _options.max_write_buffer_number = maxWriteBufferNumber; +} + +- (RocksDBCompressionType)compressionType +{ + return (RocksDBCompressionType)_options.compression; +} + +- (void)setCompressionType:(RocksDBCompressionType)compressionType +{ + _options.compression = (rocksdb::CompressionType)compressionType; +} + +@end diff --git a/ObjectiveRocks/RocksDBComparator.h b/ObjectiveRocks/RocksDBComparator.h index dd3f471..a8599a7 100644 --- a/ObjectiveRocks/RocksDBComparator.h +++ b/ObjectiveRocks/RocksDBComparator.h @@ -8,9 +8,20 @@ #import +typedef NS_ENUM(NSUInteger, RocksDBComparatorType) +{ + RocksDBComparatorBytewiseAscending, + RocksDBComparatorBytewiseDescending, + RocksDBComparatorStringCompareAscending, + RocksDBComparatorStringCompareDescending, + RocksDBComparatorNumberAscending, + RocksDBComparatorNumberDescending +}; + @interface RocksDBComparator : NSObject -+ (instancetype)comaparatorWithName:(NSString *)name andBlock:(int (^)(id key1, id key2))block; ++ (instancetype)comaparatorWithType:(RocksDBComparatorType)type; + - (instancetype)initWithName:(NSString *)name andBlock:(int (^)(id key1, id key2))block; @end diff --git a/ObjectiveRocks/RocksDBComparator.mm b/ObjectiveRocks/RocksDBComparator.mm index e5563c9..1515cbd 100644 --- a/ObjectiveRocks/RocksDBComparator.mm +++ b/ObjectiveRocks/RocksDBComparator.mm @@ -7,7 +7,7 @@ // #import "RocksDBComparator.h" -#import "RocksDBOptions.h" +#import "RocksDBEncodingOptions.h" #import "RocksDBSlice.h" #import "RocksDBCallbackComparator.h" @@ -16,24 +16,56 @@ @interface RocksDBComparator () { - RocksDBOptions *_options; + RocksDBEncodingOptions *_encodingOptions; NSString *_name; int (^_comparatorBlock)(id data1, id data2); const rocksdb::Comparator *_comparator; } -@property (nonatomic, strong) RocksDBOptions *options; +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, strong) NSString *name; @property (nonatomic, assign) const rocksdb::Comparator *comparator; @end @implementation RocksDBComparator -@synthesize options = _options; +@synthesize encodingOptions = _encodingOptions; +@synthesize name = _name; @synthesize comparator = _comparator; -+ (instancetype)comaparatorWithName:(NSString *)name andBlock:(int (^)(id key1, id key2))block +#pragma mark - Comparator Factory + ++ (instancetype)comaparatorWithType:(RocksDBComparatorType)type { - return [[self alloc] initWithName:name andBlock:block]; + switch (type) { + case RocksDBComparatorBytewiseAscending: + return [[self alloc] initWithNativeComparator:rocksdb::BytewiseComparator()]; + + case RocksDBComparatorBytewiseDescending: + return [[self alloc] initWithNativeComparator:rocksdb::ReverseBytewiseComparator()]; + + case RocksDBComparatorStringCompareAscending: + return [[self alloc] initWithName:@"objectiverocks.string.compare.asc" andBlock:^int(id key1, id key2) { + return [key1 compare:key2]; + }]; + + case RocksDBComparatorStringCompareDescending: + return [[self alloc] initWithName:@"objectiverocks.string.compare.desc" andBlock:^int(id key1, id key2) { + return -1 * [key1 compare:key2]; + }]; + + case RocksDBComparatorNumberAscending: + return [[self alloc] initWithName:@"objectiverocks.key.length.asc" andBlock:^int(id key1, id key2) { + return [key1 compare:key2]; + }]; + + case RocksDBComparatorNumberDescending: + return [[self alloc] initWithName:@"objectiverocks.key.length.desc" andBlock:^int(id key1, id key2) { + return [key1 compare:key2] * -1; + }]; + } } +#pragma mark - Lifecycle + - (instancetype)initWithName:(NSString *)name andBlock:(int (^)(id key1, id key2))block { self = [super init]; @@ -45,10 +77,22 @@ - (instancetype)initWithName:(NSString *)name andBlock:(int (^)(id key1, id key2 return self; } +- (instancetype)initWithNativeComparator:(const rocksdb::Comparator *)comparator +{ + self = [super init]; + if (self) { + _name = [NSString stringWithCString:comparator->Name() encoding:NSUTF8StringEncoding]; + _comparator = comparator; + } + return self; +} + +#pragma mark - Callback + - (int)compare:(const rocksdb::Slice &)slice1 with:(const rocksdb::Slice &)slice2 { - id key1 = DecodeKeySlice(slice1, _options, nil); - id key2 = DecodeKeySlice(slice2, _options, nil); + id key1 = DecodeKeySlice(slice1, _encodingOptions, nil); + id key2 = DecodeKeySlice(slice2, _encodingOptions, nil); return _comparatorBlock ? _comparatorBlock(key1, key2) : 0; } diff --git a/ObjectiveRocks/RocksDBDatabaseOptions.h b/ObjectiveRocks/RocksDBDatabaseOptions.h new file mode 100644 index 0000000..c792f8e --- /dev/null +++ b/ObjectiveRocks/RocksDBDatabaseOptions.h @@ -0,0 +1,36 @@ +// +// RocksDBDatabaseOptions.h +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import + +typedef NS_ENUM(unsigned char, RocksDBLogLevel) +{ + RocksDBLogLevelDebug = 0, + RocksDBLogLevelInfo, + RocksDBLogLevelWarn, + RocksDBLogLevelError, + RocksDBLogLevelFatal +}; + +@interface RocksDBDatabaseOptions : NSObject + +@property (nonatomic, assign) BOOL createIfMissing; +@property (nonatomic, assign) BOOL createMissingColumnFamilies; +@property (nonatomic, assign) BOOL errorIfExists; +@property (nonatomic, assign) BOOL paranoidChecks; +@property (nonatomic, assign) RocksDBLogLevel infoLogLevel; +@property (nonatomic, assign) int maxOpenFiles; +@property (nonatomic, assign) uint64_t maxWriteAheadLogSize; +@property (nonatomic, assign) BOOL disableDataSync; +@property (nonatomic, assign) BOOL useFSync; +@property (nonatomic, assign) size_t maxLogFileSize; +@property (nonatomic, assign) size_t logFileTimeToRoll; +@property (nonatomic, assign) size_t keepLogFileNum; +@property (nonatomic, assign) uint64_t bytesPerSync; + +@end diff --git a/ObjectiveRocks/RocksDBDatabaseOptions.mm b/ObjectiveRocks/RocksDBDatabaseOptions.mm new file mode 100644 index 0000000..1369b5b --- /dev/null +++ b/ObjectiveRocks/RocksDBDatabaseOptions.mm @@ -0,0 +1,166 @@ +// +// RocksDBDatabaseOptions.m +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBDatabaseOptions.h" + +#import + +@interface RocksDBDatabaseOptions () +{ + rocksdb::DBOptions _options; +} +@property (nonatomic, assign) const rocksdb::DBOptions options; +@end + +@implementation RocksDBDatabaseOptions +@synthesize options = _options; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + self = [super init]; + if (self) { + _options = rocksdb::DBOptions(); + } + return self; +} + +#pragma mark - DB Options + +- (BOOL)createIfMissing +{ + return _options.create_if_missing; +} + +- (void)setCreateIfMissing:(BOOL)createIfMissing +{ + _options.create_if_missing = createIfMissing; +} + +- (BOOL)createMissingColumnFamilies +{ + return _options.create_missing_column_families; +} + +- (void)setCreateMissingColumnFamilies:(BOOL)createMissingColumnFamilies +{ + _options.create_missing_column_families = createMissingColumnFamilies; +} + +- (BOOL)errorIfExists +{ + return _options.error_if_exists; +} + +- (void)setErrorIfExists:(BOOL)errorIfExists +{ + _options.error_if_exists = errorIfExists; +} + +- (BOOL)paranoidChecks +{ + return _options.paranoid_checks; +} + +- (void)setParanoidChecks:(BOOL)paranoidChecks +{ + _options.paranoid_checks = paranoidChecks; +} + +- (RocksDBLogLevel)infoLogLevel +{ + return (RocksDBLogLevel)_options.info_log_level; +} + +- (void)setInfoLogLevel:(RocksDBLogLevel)infoLogLevel +{ + _options.info_log_level = (rocksdb::InfoLogLevel)infoLogLevel; +} + +- (int)maxOpenFiles +{ + return _options.max_open_files; +} + +- (void)setMaxOpenFiles:(int)maxOpenFiles +{ + _options.max_open_files = maxOpenFiles; +} + +- (uint64_t)maxWriteAheadLogSize +{ + return _options.max_total_wal_size; +} + +- (void)setMaxWriteAheadLogSize:(uint64_t)maxWriteAheadLogSize +{ + _options.max_total_wal_size = maxWriteAheadLogSize; +} + +- (BOOL)disableDataSync +{ + return _options.disableDataSync; +} + +- (void)setDisableDataSync:(BOOL)disableDataSync +{ + _options.disableDataSync = disableDataSync; +} + +- (BOOL)useFSync +{ + return _options.use_fsync; +} + +- (void)setUseFSync:(BOOL)useFSync +{ + _options.use_fsync = useFSync; +} + +- (size_t)maxLogFileSize +{ + return _options.max_log_file_size; +} + +- (void)setMaxLogFileSize:(size_t)maxLogFileSize +{ + _options.max_log_file_size = maxLogFileSize; +} + +- (size_t)logFileTimeToRoll +{ + return _options.log_file_time_to_roll; +} + +- (void)setLogFileTimeToRoll:(size_t)logFileTimeToRoll +{ + _options.log_file_time_to_roll = logFileTimeToRoll; +} + +- (size_t)keepLogFileNum +{ + return _options.keep_log_file_num; +} + +- (void)setKeepLogFileNum:(size_t)keepLogFileNum +{ + _options.keep_log_file_num = keepLogFileNum; +} + +- (uint64_t)bytesPerSync +{ + return _options.bytes_per_sync; +} + +- (void)setBytesPerSync:(uint64_t)bytesPerSync +{ + _options.bytes_per_sync = bytesPerSync; +} + +@end diff --git a/ObjectiveRocks/RocksDBEncodingOptions.h b/ObjectiveRocks/RocksDBEncodingOptions.h new file mode 100644 index 0000000..533cfaf --- /dev/null +++ b/ObjectiveRocks/RocksDBEncodingOptions.h @@ -0,0 +1,22 @@ +// +// RocksDBEncodingOptions.h +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "RocksDBTypes.h" + +@interface RocksDBEncodingOptions : NSObject + +@property (nonatomic, copy) NSData * (^ keyEncoder)(id key); +@property (nonatomic, copy) id (^ keyDecoder)(NSData *data); +@property (nonatomic, copy) NSData * (^ valueEncoder)(id key, id value); +@property (nonatomic, copy) id (^ valueDecoder)(id key, NSData *data); + +@property (nonatomic, assign) RocksDBType keyType; +@property (nonatomic, assign) RocksDBType valueType; + +@end diff --git a/ObjectiveRocks/RocksDBEncodingOptions.mm b/ObjectiveRocks/RocksDBEncodingOptions.mm new file mode 100644 index 0000000..541a51a --- /dev/null +++ b/ObjectiveRocks/RocksDBEncodingOptions.mm @@ -0,0 +1,27 @@ +// +// RocksDBEncodingOptions.m +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBEncodingOptions.h" + +@implementation RocksDBEncodingOptions + +- (void)setKeyType:(RocksDBType)type +{ + self.keyEncoder = [RocksDBTypes keyEncoderForType:type]; + self.keyDecoder = [RocksDBTypes keyDecoderForType:type]; + _keyType = type; +} + +- (void)setValueType:(RocksDBType)type +{ + self.valueEncoder = [RocksDBTypes valueEncoderForType:type]; + self.valueDecoder = [RocksDBTypes valueDecoderForType:type]; + _valueType = type; +} + +@end diff --git a/ObjectiveRocks/ObjectiveRocksError.h b/ObjectiveRocks/RocksDBError.h similarity index 65% rename from ObjectiveRocks/ObjectiveRocksError.h rename to ObjectiveRocks/RocksDBError.h index fd0e8c7..0e14941 100644 --- a/ObjectiveRocks/ObjectiveRocksError.h +++ b/ObjectiveRocks/RocksDBError.h @@ -1,5 +1,5 @@ // -// ObjectiveRocksError.h +// RocksDBError.h // ObjectiveRocks // // Created by Iska on 19/11/14. @@ -8,11 +8,13 @@ #import -#include +namespace rocksdb { + class Status; +} -extern NSString * const ObjectiveRocksErrorDomain; +extern NSString * const RocksDBErrorDomain; -@interface ObjectiveRocksError : NSObject +@interface RocksDBError : NSObject + (NSError *)errorWithRocksStatus:(rocksdb::Status)status; + (NSError *)errorForMissingConversionBlock; diff --git a/ObjectiveRocks/ObjectiveRocksError.mm b/ObjectiveRocks/RocksDBError.mm similarity index 70% rename from ObjectiveRocks/ObjectiveRocksError.mm rename to ObjectiveRocks/RocksDBError.mm index db2d3e1..ada57cb 100644 --- a/ObjectiveRocks/ObjectiveRocksError.mm +++ b/ObjectiveRocks/RocksDBError.mm @@ -1,16 +1,18 @@ // -// ObjectiveRocksError.m +// RocksDBError.m // ObjectiveRocks // // Created by Iska on 19/11/14. // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import "ObjectiveRocksError.h" +#import "RocksDBError.h" -NSString * const ObjectiveRocksErrorDomain = @"co.braincookie.objectiverocks.error"; +#import -@implementation ObjectiveRocksError +NSString * const RocksDBErrorDomain = @"co.braincookie.objectiverocks.error"; + +@implementation RocksDBError + (NSError *)errorWithRocksStatus:(rocksdb::Status)status { @@ -21,7 +23,7 @@ + (NSError *)errorWithRocksStatus:(rocksdb::Status)status NSLocalizedFailureReasonErrorKey : reason }; - return [NSError errorWithDomain:ObjectiveRocksErrorDomain code:status.code() userInfo:userInfo]; + return [NSError errorWithDomain:RocksDBErrorDomain code:status.code() userInfo:userInfo]; } + (NSError *)errorForMissingConversionBlock @@ -35,7 +37,7 @@ + (NSError *)errorForMissingConversionBlock NSLocalizedRecoverySuggestionErrorKey: recovery }; - return [NSError errorWithDomain:ObjectiveRocksErrorDomain code:3000 userInfo:userInfo]; + return [NSError errorWithDomain:RocksDBErrorDomain code:3000 userInfo:userInfo]; } @end diff --git a/ObjectiveRocks/RocksDBIterator.h b/ObjectiveRocks/RocksDBIterator.h index 1f48f05..02c98af 100644 --- a/ObjectiveRocks/RocksDBIterator.h +++ b/ObjectiveRocks/RocksDBIterator.h @@ -7,7 +7,7 @@ // #import -#import "RocksDBOptions.h" +#import "RocksDBEncodingOptions.h" typedef struct RocksDBIteratorKeyRange { @@ -29,13 +29,14 @@ namespace rocksdb { @interface RocksDBIterator : NSObject -- (instancetype)initWithDBIterator:(rocksdb::Iterator *)iterator andOptions:(RocksDBOptions *)options; +- (instancetype)initWithDBIterator:(rocksdb::Iterator *)iterator + andEncodingOptions:(RocksDBEncodingOptions *)options; - (void)close; - (BOOL)isValid; - (void)seekToFirst; - (void)seekToLast; -- (void)seekToKey:(NSData *)aKey; +- (void)seekToKey:(id)aKey; - (void)next; - (void)previous; - (id)key; @@ -45,4 +46,6 @@ namespace rocksdb { - (void)enumerateKeysInReverse:(BOOL)reverse usingBlock:(void (^)(id key, BOOL *stop))block; - (void)enumerateKeysInRange:(RocksDBIteratorKeyRange)range reverse:(BOOL)reverse usingBlock:(void (^)(id key, BOOL *stop))block; +- (void)enumerateKeysWithPrefix:(id)prefix usingBlock:(void (^)(id key, BOOL *stop))block; + @end diff --git a/ObjectiveRocks/RocksDBIterator.mm b/ObjectiveRocks/RocksDBIterator.mm index 4399059..6994ce4 100644 --- a/ObjectiveRocks/RocksDBIterator.mm +++ b/ObjectiveRocks/RocksDBIterator.mm @@ -14,7 +14,7 @@ @interface RocksDBIterator () { rocksdb::Iterator *_iterator; - RocksDBOptions *_options; + RocksDBEncodingOptions *_options; } @end @@ -22,7 +22,8 @@ @implementation RocksDBIterator #pragma mark - Lifecycle -- (instancetype)initWithDBIterator:(rocksdb::Iterator *)iterator andOptions:(RocksDBOptions *)options +- (instancetype)initWithDBIterator:(rocksdb::Iterator *)iterator + andEncodingOptions:(RocksDBEncodingOptions *)options { self = [super init]; if (self) { @@ -40,9 +41,9 @@ - (void)dealloc - (void)close { @synchronized(self) { - if (_iterator != NULL) { + if (_iterator != nullptr) { delete _iterator; - _iterator = NULL; + _iterator = nullptr; } } } @@ -142,4 +143,27 @@ - (void)enumerateKeysInRange:(RocksDBIteratorKeyRange)range reverse:(BOOL)revers } } +- (void)enumerateKeysWithPrefix:(id)prefix usingBlock:(void (^)(id key, BOOL *stop))block +{ + BOOL stop = NO; + + rocksdb::Slice prefixSlice = SliceFromKey(prefix, _options, nil); + _iterator->Seek(prefixSlice); + + BOOL (^ checkBounds)(rocksdb::Slice key) = ^ BOOL (rocksdb::Slice key) { + return key.starts_with(prefixSlice); + }; + + rocksdb::Slice keySlice = _iterator->key(); + + while (_iterator->Valid() && checkBounds(keySlice)) { + keySlice = _iterator->key(); + + if (block) block(self.key, &stop); + if (stop == YES) break; + + _iterator->Next(); + } +} + @end diff --git a/ObjectiveRocks/RocksDBMergeOperator.h b/ObjectiveRocks/RocksDBMergeOperator.h index cc16c4e..10806b3 100644 --- a/ObjectiveRocks/RocksDBMergeOperator.h +++ b/ObjectiveRocks/RocksDBMergeOperator.h @@ -10,7 +10,11 @@ @interface RocksDBMergeOperator : NSObject -+ (instancetype)operatorWithName:(NSString *)name andBlock:(id (^)(id key, id existingValue, id value))block; -- (instancetype)initWithName:(NSString *)name andBlock:(id (^)(id key, id existingValue, id value))block; ++ (instancetype)operatorWithName:(NSString *)name + andBlock:(id (^)(id key, id existingValue, id value))block; + ++ (instancetype)operatorWithName:(NSString *)name + partialMergeBlock:(NSString * (^)(id key, NSString *leftOperand, NSString *rightOperand))partialMergeBlock + fullMergeBlock:(id (^)(id key, id existingValue, NSArray *operandList))fullMergeBlock; @end diff --git a/ObjectiveRocks/RocksDBMergeOperator.mm b/ObjectiveRocks/RocksDBMergeOperator.mm index e7f709f..d670674 100644 --- a/ObjectiveRocks/RocksDBMergeOperator.mm +++ b/ObjectiveRocks/RocksDBMergeOperator.mm @@ -7,57 +7,48 @@ // #import "RocksDBMergeOperator.h" -#import "RocksDBOptions.h" +#import "RocksDBEncodingOptions.h" #import "RocksDBSlice.h" #import "RocksDBCallbackAssociativeMergeOperator.h" +#import "RocksDBCallbackMergeOperator.h" #import #import +#pragma mark - Extension + @interface RocksDBMergeOperator () { - RocksDBOptions *_options; + RocksDBEncodingOptions *_encodingOptions; NSString *_name; - id (^ _mergeBlock)(id, id existingValue, id value); rocksdb::MergeOperator *_mergeOperator; } -@property (nonatomic, strong) RocksDBOptions *options; +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, copy) NSString *name; @property (nonatomic, assign) rocksdb::MergeOperator *mergeOperator; @end -@implementation RocksDBMergeOperator -@synthesize options = _options; -@synthesize mergeOperator = _mergeOperator; +#pragma mark - Associative Merge Operator -+ (instancetype)operatorWithName:(NSString *)name andBlock:(id (^)(id, id, id))block +@interface RocksDBAssociativeMergeOperator : RocksDBMergeOperator { - return [[self alloc] initWithName:name andBlock:block]; + id (^ _associativeMergeBlock)(id, id existingValue, id value); } +@end + +@implementation RocksDBAssociativeMergeOperator - (instancetype)initWithName:(NSString *)name andBlock:(id (^)(id, id, id))block { self = [super init]; if (self) { - _name = [name copy]; - _mergeBlock = [block copy]; - _mergeOperator = RocksDBCallbackAssociativeMergeOperator((__bridge void *)self, name.UTF8String, &trampoline); + self.name = name; + self.mergeOperator = RocksDBCallbackAssociativeMergeOperator((__bridge void *)self, name.UTF8String, &trampoline); + _associativeMergeBlock = [block copy]; } return self; } -- (NSData *)mergeForKey:(const rocksdb::Slice &)keySlice -withExistingValue:(const rocksdb::Slice *)existingSlice - andValue:(const rocksdb::Slice &)valueSlice -{ - id key = DecodeKeySlice(keySlice, _options, nil); - id previous = (existingSlice == nullptr) ? nil : DecodeValueSlice(key, *existingSlice, _options, nil); - id value = DecodeValueSlice(key, valueSlice, _options, nil); - - id mergeResult = _mergeBlock ? _mergeBlock(key, previous, value): nil; - - return EncodeValue(key, mergeResult, _options, nil); -} - bool trampoline(void* instance, const rocksdb::Slice& key, const rocksdb::Slice* existing_value, @@ -68,8 +59,141 @@ bool trampoline(void* instance, NSData *data = [(__bridge id)instance mergeForKey:key withExistingValue:existing_value andValue:value]; - *new_value = (char *)data.bytes; + new_value->clear(); + new_value->assign((char *)data.bytes, data.length); return true; } +- (NSData *)mergeForKey:(const rocksdb::Slice &)keySlice + withExistingValue:(const rocksdb::Slice *)existingSlice + andValue:(const rocksdb::Slice &)valueSlice +{ + id key = DecodeKeySlice(keySlice, self.encodingOptions, nil); + id previous = (existingSlice == nullptr) ? nil : DecodeValueSlice(key, *existingSlice, self.encodingOptions, nil); + id value = DecodeValueSlice(key, valueSlice, self.encodingOptions, nil); + + id mergeResult = _associativeMergeBlock ? _associativeMergeBlock(key, previous, value): nil; + + return EncodeValue(key, mergeResult, self.encodingOptions, nil); +} + +@end + +#pragma mark - Generic Merge Operator + +@interface RocksDBGenericMergeOperator : RocksDBMergeOperator +{ + NSString * (^ _partialMergeBlock)(id key, NSString *leftOperand, NSString *rightOperand); + id (^ _fullMergeBlock)(id key, id existingValue, NSArray *operandList); +} +@end + +@implementation RocksDBGenericMergeOperator + +- (instancetype)initWithName:(NSString *)name + partialMergeBlock:(NSString * (^)(id key, NSString *leftOperand, NSString *rightOperand))partialMergeBlock + fullMergeBlock:(id (^)(id key, id existingValue, NSArray *operandList))fullMergeBlock +{ + self = [super init]; + if (self) { + self.name = name; + self.mergeOperator = RocksDBCallbackMergeOperator((__bridge void *)self, name.UTF8String, &trampolinePartialMerge, &trampolineFullMerge); + _partialMergeBlock = [partialMergeBlock copy]; + _fullMergeBlock = [fullMergeBlock copy]; + } + return self; +} + +bool trampolinePartialMerge(void* instance, + const rocksdb::Slice& key, + const rocksdb::Slice& left_operand, + const rocksdb::Slice& right_operand, + std::string* new_value, + rocksdb::Logger* logger) +{ + NSData *data = [(__bridge id)instance partialMergeForKey:key + withLeftOperand:left_operand + andRightOperand:right_operand]; + if (data != nil) { + new_value->clear(); + new_value->assign((char *)data.bytes, data.length); + return true; + } + return false; +} + +- (NSData *)partialMergeForKey:(const rocksdb::Slice &)keySlice + withLeftOperand:(const rocksdb::Slice &)leftSlice + andRightOperand:(const rocksdb::Slice &)rightSlice +{ + id key = DecodeKeySlice(keySlice, self.encodingOptions, nil); + id left = DecodeValueSlice(key, leftSlice, self.encodingOptions, nil); + id right = DecodeValueSlice(key, rightSlice, self.encodingOptions, nil); + + NSString * mergeResult = _partialMergeBlock ? _partialMergeBlock(key, left, right): nil; + + return [mergeResult dataUsingEncoding:NSUTF8StringEncoding]; +} + +bool trampolineFullMerge(void* instance, + const rocksdb::Slice& key, + const rocksdb::Slice* existing_value, + const std::deque& operand_list, + std::string* new_value, + rocksdb::Logger* logger) +{ + NSData *data = [(__bridge id)instance fullMergeForKey:key + withExistingValue:existing_value + andOperandList:operand_list]; + + if (data != nil) { + new_value->clear(); + new_value->assign((char *)data.bytes, data.length); + return true; + } + return false; +} + +- (NSData *)fullMergeForKey:(const rocksdb::Slice &)keySlice + withExistingValue:(const rocksdb::Slice *)existingSlice + andOperandList:(const std::deque &)operand_list +{ + id key = DecodeKeySlice(keySlice, self.encodingOptions, nil); + id previous = (existingSlice == nullptr) ? nil : DecodeValueSlice(key, *existingSlice, self.encodingOptions, nil); + + NSMutableArray *operands = [NSMutableArray arrayWithCapacity:operand_list.size()]; + + for (const auto &value : operand_list) { + id decoded = [NSString stringWithCString:value.c_str() encoding:NSUTF8StringEncoding]; + if (decoded != nil) { + [operands addObject:decoded]; + } + } + + id mergeResult = _fullMergeBlock ? _fullMergeBlock(key, previous, operands) : nil; + + return EncodeValue(key, mergeResult, self.encodingOptions, nil);; +} + +@end + +#pragma mark - Merge Operator Factory + +@implementation RocksDBMergeOperator +@synthesize name = _name; +@synthesize encodingOptions = _encodingOptions; +@synthesize mergeOperator = _mergeOperator; + ++ (instancetype)operatorWithName:(NSString *)name andBlock:(id (^)(id, id, id))block +{ + return [[RocksDBAssociativeMergeOperator alloc] initWithName:name andBlock:block]; +} + ++ (instancetype)operatorWithName:(NSString *)name + partialMergeBlock:(NSString * (^)(id key, NSString *leftOperand, NSString *rightOperand))partialMergeBlock + fullMergeBlock:(id (^)(id key, id existingValue, NSArray *operandList))fullMergeBlock +{ + return [[RocksDBGenericMergeOperator alloc] initWithName:name partialMergeBlock:partialMergeBlock fullMergeBlock:fullMergeBlock]; +} + @end diff --git a/ObjectiveRocks/RocksDBOptions.h b/ObjectiveRocks/RocksDBOptions.h index b4b0d47..55493cc 100644 --- a/ObjectiveRocks/RocksDBOptions.h +++ b/ObjectiveRocks/RocksDBOptions.h @@ -7,35 +7,32 @@ // #import + +#import "RocksDBDatabaseOptions.h" +#import "RocksDBEncodingOptions.h" +#import "RocksDBColumnFamilyOptions.h" #import "RocksDBComparator.h" #import "RocksDBMergeOperator.h" - -typedef NS_ENUM(unsigned char, RocksDBLogLevel) -{ - RocksDBLogLevelDebug = 0, - RocksDBLogLevelInfo, - RocksDBLogLevelWarn, - RocksDBLogLevelError, - RocksDBLogLevelFatal -}; - -typedef NS_ENUM(char, RocksDBCompressionType) -{ - RocksDBCompressionNone = 0x0, - RocksDBCompressionSnappy = 0x1, - RocksDBCompressionZlib = 0x2, - RocksDBCompressionBZip2 = 0x3, - RocksDBCompressionLZ4 = 0x4, - RocksDBCompressionLZ4HC = 0x5 -}; +#import "RocksDBPrefixExtractor.h" +#import "RocksDBTypes.h" @interface RocksDBOptions : NSObject +- (instancetype)initWithDatabaseOptions:(RocksDBDatabaseOptions *)dbOptions + andColumnFamilyOptions:(RocksDBColumnFamilyOptions *)columnFamilyOptions; + +@end + +@interface RocksDBOptions (Encoding) + @property (nonatomic, copy) NSData * (^ keyEncoder)(id key); @property (nonatomic, copy) id (^ keyDecoder)(NSData *data); @property (nonatomic, copy) NSData * (^ valueEncoder)(id key, id value); @property (nonatomic, copy) id (^ valueDecoder)(id key, NSData *data); +@property (nonatomic, assign) RocksDBType keyType; +@property (nonatomic, assign) RocksDBType valueType; + @end @interface RocksDBOptions (DBOptions) @@ -60,6 +57,7 @@ typedef NS_ENUM(char, RocksDBCompressionType) @property (nonatomic, strong) RocksDBComparator *comparator; @property (nonatomic, strong) RocksDBMergeOperator *mergeOperator; +@property (nonatomic, strong) RocksDBPrefixExtractor *prefixExtractor; @property (nonatomic, assign) size_t writeBufferSize; @property (nonatomic, assign) int maxWriteBufferNumber; @property (nonatomic, assign) RocksDBCompressionType compressionType; diff --git a/ObjectiveRocks/RocksDBOptions.mm b/ObjectiveRocks/RocksDBOptions.mm index 7552d1c..f0b266c 100644 --- a/ObjectiveRocks/RocksDBOptions.mm +++ b/ObjectiveRocks/RocksDBOptions.mm @@ -7,33 +7,33 @@ // #import "RocksDBOptions.h" -#import "RocksDBComparator.h" #import #import #import +#import -@interface RocksDBComparator () -@property (nonatomic, strong) RocksDBOptions *options; -@property (nonatomic, assign) const rocksdb::Comparator *comparator; +@interface RocksDBDatabaseOptions () +@property (nonatomic, assign) const rocksdb::DBOptions options; @end -@interface RocksDBMergeOperator () -@property (nonatomic, strong) RocksDBOptions *options; -@property (nonatomic, assign) rocksdb::MergeOperator *mergeOperator; +@interface RocksDBColumnFamilyOptions () +@property (nonatomic, assign) rocksdb::ColumnFamilyOptions options; @end @interface RocksDBOptions () { - rocksdb::Options _options; - RocksDBComparator *_comparatorWrapper; - RocksDBMergeOperator *_mergeOperatorWrapper; + RocksDBDatabaseOptions *_databaseOptions; + RocksDBColumnFamilyOptions *_columnFamilyOption; } @property (nonatomic, assign) rocksdb::Options options; +@property (nonatomic, strong) RocksDBDatabaseOptions *databaseOptions; +@property (nonatomic, strong) RocksDBColumnFamilyOptions *columnFamilyOption; @end @implementation RocksDBOptions -@synthesize options = _options; +@synthesize databaseOptions = _databaseOptions; +@synthesize columnFamilyOption = _columnFamilyOption; #pragma mark - Lifecycle @@ -41,197 +41,39 @@ - (instancetype)init { self = [super init]; if (self) { - _options = rocksdb::Options(); + _databaseOptions = [RocksDBDatabaseOptions new]; + _columnFamilyOption = [RocksDBColumnFamilyOptions new]; } return self; } -#pragma mark - DB Options - -- (BOOL)createIfMissing -{ - return _options.create_if_missing; -} - -- (void)setCreateIfMissing:(BOOL)createIfMissing -{ - _options.create_if_missing = createIfMissing; -} - -- (BOOL)createMissingColumnFamilies -{ - return _options.create_missing_column_families; -} - -- (void)setCreateMissingColumnFamilies:(BOOL)createMissingColumnFamilies -{ - _options.create_missing_column_families = createMissingColumnFamilies; -} - -- (BOOL)errorIfExists -{ - return _options.error_if_exists; -} - -- (void)setErrorIfExists:(BOOL)errorIfExists -{ - _options.error_if_exists = errorIfExists; -} - -- (BOOL)paranoidChecks -{ - return _options.paranoid_checks; -} - -- (void)setParanoidChecks:(BOOL)paranoidChecks -{ - _options.paranoid_checks = paranoidChecks; -} - -- (RocksDBLogLevel)infoLogLevel -{ - return (RocksDBLogLevel)_options.info_log_level; -} - -- (void)setInfoLogLevel:(RocksDBLogLevel)infoLogLevel -{ - _options.info_log_level = (rocksdb::InfoLogLevel)infoLogLevel; -} - -- (int)maxOpenFiles -{ - return _options.max_open_files; -} - -- (void)setMaxOpenFiles:(int)maxOpenFiles -{ - _options.max_open_files = maxOpenFiles; -} - -- (uint64_t)maxWriteAheadLogSize -{ - return _options.max_total_wal_size; -} - -- (void)setMaxWriteAheadLogSize:(uint64_t)maxWriteAheadLogSize -{ - _options.max_total_wal_size = maxWriteAheadLogSize; -} - -- (BOOL)disableDataSync -{ - return _options.disableDataSync; -} - -- (void)setDisableDataSync:(BOOL)disableDataSync -{ - _options.disableDataSync = disableDataSync; -} - -- (BOOL)useFSync -{ - return _options.use_fsync; -} - -- (void)setUseFSync:(BOOL)useFSync +- (instancetype)initWithDatabaseOptions:(RocksDBDatabaseOptions *)dbOptions + andColumnFamilyOptions:(RocksDBColumnFamilyOptions *)columnFamilyOptions { - _options.use_fsync = useFSync; -} - -- (size_t)maxLogFileSize -{ - return _options.max_log_file_size; -} - -- (void)setMaxLogFileSize:(size_t)maxLogFileSize -{ - _options.max_log_file_size = maxLogFileSize; -} - -- (size_t)logFileTimeToRoll -{ - return _options.log_file_time_to_roll; -} - -- (void)setLogFileTimeToRoll:(size_t)logFileTimeToRoll -{ - _options.log_file_time_to_roll = logFileTimeToRoll; -} - -- (size_t)keepLogFileNum -{ - return _options.keep_log_file_num; -} - -- (void)setKeepLogFileNum:(size_t)keepLogFileNum -{ - _options.keep_log_file_num = keepLogFileNum; -} - -- (uint64_t)bytesPerSync -{ - return _options.bytes_per_sync; -} - -- (void)setBytesPerSync:(uint64_t)bytesPerSync -{ - _options.bytes_per_sync = bytesPerSync; -} - -#pragma mark - Column Family Options - -- (void)setComparator:(RocksDBComparator *)comparator -{ - _comparatorWrapper = comparator; - _comparatorWrapper.options = self; - _options.comparator = _comparatorWrapper.comparator; -} - -- (RocksDBComparator *)comparator -{ - return _comparatorWrapper; -} - -- (void)setMergeOperator:(RocksDBMergeOperator *)mergeOperator -{ - _mergeOperatorWrapper = mergeOperator; - _mergeOperatorWrapper.options = self; - _options.merge_operator.reset(_mergeOperatorWrapper.mergeOperator); -} - -- (RocksDBMergeOperator *)mergeOperator -{ - return _mergeOperatorWrapper; -} - -- (size_t)writeBufferSize -{ - return _options.write_buffer_size; -} - -- (void)setWriteBufferSize:(size_t)writeBufferSize -{ - _options.write_buffer_size = writeBufferSize; + self = [super init]; + if (self) { + _databaseOptions = dbOptions; + _columnFamilyOption = columnFamilyOptions; + } + return self; } -- (int)maxWriteBufferNumber -{ - return _options.max_write_buffer_number; -} +#pragma mark - Property -- (void)setMaxWriteBufferNumber:(int)maxWriteBufferNumber +- (rocksdb::Options)options { - _options.max_write_buffer_number = maxWriteBufferNumber; + return rocksdb::Options(_databaseOptions.options, _columnFamilyOption.options); } -- (RocksDBCompressionType)compressionType -{ - return (RocksDBCompressionType)_options.compression; -} +#pragma mark - Forward -- (void)setCompressionType:(RocksDBCompressionType)compressionType +- (id)forwardingTargetForSelector:(SEL)aSelector { - _options.compression = (rocksdb::CompressionType)compressionType; + if ([_databaseOptions respondsToSelector:aSelector]) { + return _databaseOptions; + } else { + return _columnFamilyOption; + } } @end diff --git a/ObjectiveRocks/RocksDBPrefixExtractor.h b/ObjectiveRocks/RocksDBPrefixExtractor.h new file mode 100644 index 0000000..8e7d66f --- /dev/null +++ b/ObjectiveRocks/RocksDBPrefixExtractor.h @@ -0,0 +1,25 @@ +// +// RocksDBPrefixExtractor.h +// ObjectiveRocks +// +// Created by Iska on 26/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, RocksDBPrefixType) +{ + RocksDBPrefixFixedLength +}; + +@interface RocksDBPrefixExtractor : NSObject + ++ (instancetype)prefixExtractorWithType:(RocksDBPrefixType)type length:(size_t)length; + +- (instancetype)initWithName:(NSString *)name + transformBlock:(id (^)(id key))transformBlock + prefixCandidateBlock:(BOOL (^)(id key))prefixCandidateBlock + validPrefixBlock:(BOOL (^)(id prefix))validPrefixBlock; + +@end diff --git a/ObjectiveRocks/RocksDBPrefixExtractor.mm b/ObjectiveRocks/RocksDBPrefixExtractor.mm new file mode 100644 index 0000000..f363d94 --- /dev/null +++ b/ObjectiveRocks/RocksDBPrefixExtractor.mm @@ -0,0 +1,107 @@ +// +// RocksDBPrefixExtractor.m +// ObjectiveRocks +// +// Created by Iska on 26/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBPrefixExtractor.h" +#import "RocksDBEncodingOptions.h" +#import "RocksDBSlice.h" +#import "RocksDBCallbackSliceTransform.h" + +#import +#import + +@interface RocksDBPrefixExtractor () +{ + RocksDBEncodingOptions *_encodingOptions; + NSString *_name; + const rocksdb::SliceTransform *_sliceTransform; + + id (^ _transformBlock)(id key); + BOOL (^ _prefixCandidateBlock)(id key); + BOOL (^ _validPrefixBlock)(id prefix); +} +@property (nonatomic, strong) RocksDBEncodingOptions *encodingOptions; +@property (nonatomic, strong) NSString *name; +@property (nonatomic, assign) const rocksdb::SliceTransform *sliceTransform; +@end + +@implementation RocksDBPrefixExtractor +@synthesize encodingOptions = _encodingOptions; +@synthesize name = _name; +@synthesize sliceTransform = _sliceTransform; + ++ (instancetype)prefixExtractorWithType:(RocksDBPrefixType)type length:(size_t)length +{ + switch (type) { + case RocksDBPrefixFixedLength: + return [[self alloc] initWithNativeSliceTransform:rocksdb::NewFixedPrefixTransform(length)]; + } +} + +- (instancetype)initWithNativeSliceTransform:(const rocksdb::SliceTransform *)sliceTransform +{ + self = [super init]; + if (self) { + _name = [NSString stringWithCString:sliceTransform->Name() encoding:NSUTF8StringEncoding]; + _sliceTransform = sliceTransform; + } + return self; +} + +- (instancetype)initWithName:(NSString *)name + transformBlock:(id (^)(id key))transformBlock + prefixCandidateBlock:(BOOL (^)(id key))prefixCandidateBlock + validPrefixBlock:(BOOL (^)(id prefix))validPrefixBlock +{ + self = [super init]; + if (self) { + _name = [name copy]; + _transformBlock = [transformBlock copy]; + _prefixCandidateBlock = [prefixCandidateBlock copy]; + _validPrefixBlock = [validPrefixBlock copy]; + _sliceTransform = RocksDBCallbackSliceTransform((__bridge void *)self, _name.UTF8String, + &trampolineTransform, &trampolineInDomain, &trampolineInRange); + } + return self; +} + +rocksdb::Slice trampolineTransform(void* instance, const rocksdb::Slice& src) +{ + NSData *data = [(__bridge id)instance transformKey:src]; + return SliceFromData(data); +} + +- (NSData *)transformKey:(const rocksdb::Slice &)keySlice +{ + id key = DecodeKeySlice(keySlice, _encodingOptions, nil); + id transformed = _transformBlock(key); + return EncodeKey(transformed, _encodingOptions, nil); +} + +bool trampolineInDomain(void* instance, const rocksdb::Slice& src) +{ + return [(__bridge id)instance isKeyPrefixCandidate:src]; +} + +- (BOOL)isKeyPrefixCandidate:(const rocksdb::Slice &)keySlice +{ + id key = DecodeKeySlice(keySlice, _encodingOptions, nil); + return _prefixCandidateBlock(key); +} + +bool trampolineInRange(void* instance, const rocksdb::Slice& dst) +{ + return [(__bridge id)instance isPrefixValid:dst]; +} + +- (BOOL)isPrefixValid:(const rocksdb::Slice &)prefixSlice +{ + id prefix = DecodeKeySlice(prefixSlice, _encodingOptions, nil); + return _prefixCandidateBlock(prefix); +} + +@end diff --git a/ObjectiveRocks/RocksDBSlice.h b/ObjectiveRocks/RocksDBSlice.h index 14dd566..42e738e 100644 --- a/ObjectiveRocks/RocksDBSlice.h +++ b/ObjectiveRocks/RocksDBSlice.h @@ -7,8 +7,8 @@ // #import -#import "RocksDBOptions.h" -#import "ObjectiveRocksError.h" +#import "RocksDBEncodingOptions.h" +#import "RocksDBError.h" #import @@ -22,7 +22,9 @@ NS_INLINE NSData * DataFromSlice(rocksdb::Slice slice) return [NSData dataWithBytes:slice.data() length:slice.size()]; } -NS_INLINE NSData * EncodeKey(id aKey, RocksDBOptions *options, NSError * __autoreleasing *error) +#pragma mark - Key Encoding + +NS_INLINE NSData * EncodeKey(id aKey, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { if ([aKey isKindOfClass:[NSData class]]) { return aKey; @@ -32,18 +34,44 @@ NS_INLINE NSData * EncodeKey(id aKey, RocksDBOptions *options, NSError * __autor if (options.keyEncoder != nil) { encoded = options.keyEncoder(aKey); } else if (error && *error == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *temp = [RocksDBError errorForMissingConversionBlock]; *error = temp; } return encoded; } -NS_INLINE rocksdb::Slice SliceFromKey(id aKey, RocksDBOptions *options, NSError * __autoreleasing *error) +NS_INLINE rocksdb::Slice SliceFromKey(id aKey, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { return SliceFromData(EncodeKey(aKey, options, error)); } -NS_INLINE NSData * EncodeValue(id aKey, id value, RocksDBOptions *options, NSError * __autoreleasing *error) +NS_INLINE id DecodeKeySlice(rocksdb::Slice slice, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) +{ + id key = DataFromSlice(slice); + if (options.keyDecoder != nil) { + key = options.keyDecoder(key); + } else if (error && *error == nil) { + NSError *temp = [RocksDBError errorForMissingConversionBlock]; + *error = temp; + } + return key; +} + +NS_INLINE id DecodeKeyData(NSData *data, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) +{ + id key = nil; + if (options.keyDecoder != nil) { + key = options.keyDecoder(data); + } else if (error && *error == nil) { + NSError *temp = [RocksDBError errorForMissingConversionBlock]; + *error = temp; + } + return key; +} + +#pragma mark - Value Encoding + +NS_INLINE NSData * EncodeValue(id aKey, id value, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { if ([value isKindOfClass:[NSData class]]) { return value; @@ -53,35 +81,36 @@ NS_INLINE NSData * EncodeValue(id aKey, id value, RocksDBOptions *options, NSErr if (options.valueEncoder != nil) { encoded = options.valueEncoder(aKey, value); } else if (error && *error == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *temp = [RocksDBError errorForMissingConversionBlock]; *error = temp; } return encoded; } -NS_INLINE rocksdb::Slice SliceFromValue(id aKey, id value, RocksDBOptions *options, NSError * __autoreleasing *error) +NS_INLINE rocksdb::Slice SliceFromValue(id aKey, id value, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { return SliceFromData(EncodeValue(aKey, value, options, error)); } -NS_INLINE id DecodeKeySlice(rocksdb::Slice slice, RocksDBOptions *options, NSError * __autoreleasing *error) +NS_INLINE id DecodeValueSlice(id aKey, rocksdb::Slice slice, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { - id key = DataFromSlice(slice); - if (options.keyDecoder != nil) { - key = options.keyDecoder(key); + id value = DataFromSlice(slice); + if (options.valueDecoder != nil) { + value = options.valueDecoder(aKey, value); } else if (error && *error == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *temp = [RocksDBError errorForMissingConversionBlock]; *error = temp; } - return key; + return value; } -NS_INLINE id DecodeValueSlice(id aKey, rocksdb::Slice slice, RocksDBOptions *options, NSError * __autoreleasing *error) + +NS_INLINE id DecodeValueData(id aKey, NSData *data, RocksDBEncodingOptions *options, NSError * __autoreleasing *error) { - id value = DataFromSlice(slice); + id value = nil; if (options.valueDecoder != nil) { - value = options.valueDecoder(aKey, value); + value = options.valueDecoder(aKey, data); } else if (error && *error == nil) { - NSError *temp = [ObjectiveRocksError errorForMissingConversionBlock]; + NSError *temp = [RocksDBError errorForMissingConversionBlock]; *error = temp; } return value; diff --git a/ObjectiveRocks/RocksDBSnapshot.h b/ObjectiveRocks/RocksDBSnapshot.h index 1450757..c8fb837 100644 --- a/ObjectiveRocks/RocksDBSnapshot.h +++ b/ObjectiveRocks/RocksDBSnapshot.h @@ -13,11 +13,14 @@ namespace rocksdb { class DB; + class ColumnFamilyHandle; } @interface RocksDBSnapshot : RocksDB -- (instancetype)initWithDBInstance:(rocksdb::DB *)db andReadOptions:(RocksDBReadOptions *)readOptions; +- (instancetype)initWithDBInstance:(rocksdb::DB *)db + columnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andReadOptions:(RocksDBReadOptions *)readOptions; @end diff --git a/ObjectiveRocks/RocksDBSnapshot.mm b/ObjectiveRocks/RocksDBSnapshot.mm index 4dbe268..6a0e42d 100644 --- a/ObjectiveRocks/RocksDBSnapshot.mm +++ b/ObjectiveRocks/RocksDBSnapshot.mm @@ -12,6 +12,7 @@ @interface RocksDB (Private) @property (nonatomic, assign) rocksdb::DB *db; +@property (nonatomic, assign) rocksdb::ColumnFamilyHandle *columnFamily; @property (nonatomic, retain) RocksDBOptions *options; @property (nonatomic, retain) RocksDBReadOptions *readOptions; @property (nonatomic, retain) RocksDBWriteOptions *writeOptions; @@ -23,28 +24,26 @@ @interface RocksDBReadOptions (Private) @implementation RocksDBSnapshot -- (instancetype)initWithDBInstance:(rocksdb::DB *)db andReadOptions:(RocksDBReadOptions *)readOptions +- (instancetype)initWithDBInstance:(rocksdb::DB *)db + columnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andReadOptions:(RocksDBReadOptions *)readOptions { self = [super init]; if (self) { self.db = db; + self.columnFamily = columnFamily; self.readOptions = readOptions; } return self; } -- (void)dealloc -{ - [self close]; -} - - (void)close { @synchronized(self) { rocksdb::ReadOptions options = self.readOptions.options; - if (options.snapshot != NULL) { + if (options.snapshot != nullptr) { self.db->ReleaseSnapshot(options.snapshot); - options.snapshot = NULL; + options.snapshot = nullptr; self.readOptions.options = options; } } diff --git a/ObjectiveRocks/RocksDBSnapshotUnavailable.h b/ObjectiveRocks/RocksDBSnapshotUnavailable.h index 218ede9..c591662 100644 --- a/ObjectiveRocks/RocksDBSnapshotUnavailable.h +++ b/ObjectiveRocks/RocksDBSnapshotUnavailable.h @@ -24,6 +24,11 @@ NA_SELECTOR(- (BOOL)mergeObject:(id)anObject forKey:(id)aKey error:(NSError **)e NA_SELECTOR(- (BOOL)mergeObject:(id)anObject forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions) \ NA_SELECTOR(- (BOOL)mergeObject:(id)anObject forKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions) \ \ +NA_SELECTOR(- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey) \ +NA_SELECTOR(- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error) \ +NA_SELECTOR(- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions) \ +NA_SELECTOR(- (BOOL)mergeOperation:(NSString *)aMerge forKey:(id)aKey error:(NSError **)error writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions) \ +\ NA_SELECTOR(- (BOOL)mergeData:(NSData *)data forKey:(NSData *)aKey) \ NA_SELECTOR(- (BOOL)mergeData:(NSData *)data forKey:(NSData *)aKey error:(NSError **)error) \ NA_SELECTOR(- (BOOL)mergeData:(NSData *)data forKey:(NSData *)aKey writeOptions:(void (^)(RocksDBWriteOptions *writeOptions))writeOptions) \ diff --git a/ObjectiveRocks/RocksDBTypes.h b/ObjectiveRocks/RocksDBTypes.h new file mode 100644 index 0000000..a2e0b79 --- /dev/null +++ b/ObjectiveRocks/RocksDBTypes.h @@ -0,0 +1,25 @@ +// +// RocksDBTypes.h +// ObjectiveRocks +// +// Created by Iska on 25/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, RocksDBType) +{ + RocksDBTypeNSString, + RocksDBTypeNSJSONSerializable +}; + +@interface RocksDBTypes : NSObject + ++ (NSData *(^)(id))keyEncoderForType:(RocksDBType)type; ++ (id (^)(NSData *))keyDecoderForType:(RocksDBType)type; + ++ (NSData *(^)(id, id))valueEncoderForType:(RocksDBType)type; ++ (id (^)(id, NSData *))valueDecoderForType:(RocksDBType)type; + +@end diff --git a/ObjectiveRocks/RocksDBTypes.m b/ObjectiveRocks/RocksDBTypes.m new file mode 100644 index 0000000..08d31bf --- /dev/null +++ b/ObjectiveRocks/RocksDBTypes.m @@ -0,0 +1,106 @@ +// +// RocksDBTypes.m +// ObjectiveRocks +// +// Created by Iska on 25/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBTypes.h" + +@implementation RocksDBTypes + ++ (NSData *(^)(id))keyEncoderForType:(RocksDBType)type +{ + switch (type) { + case RocksDBTypeNSString: + return ^NSData * (id key) { + return [key dataUsingEncoding:NSUTF8StringEncoding]; + }; + + case RocksDBTypeNSJSONSerializable: + return ^NSData * (id key) { + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:key + options:0 + error:&error]; + if (error) { + NSLog(@"Error encoding key: %@", error.debugDescription); + } + return data; + }; + } +} + ++ (id (^)(NSData *))keyDecoderForType:(RocksDBType)type +{ + switch (type) { + case RocksDBTypeNSString: + return ^id (NSData *data) { + if (data == nil) return nil; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + }; + + case RocksDBTypeNSJSONSerializable: + return ^id (NSData * data) { + if (data == nil) return nil; + NSError *error = nil; + id obj = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&error]; + if (error) { + NSLog(@"Error decoding key: %@", error.debugDescription); + } + return obj; + }; + } +} + ++ (NSData *(^)(id, id))valueEncoderForType:(RocksDBType)type +{ + switch (type) { + case RocksDBTypeNSString: + return ^NSData * (id key, id value) { + return [value dataUsingEncoding:NSUTF8StringEncoding]; + }; + + case RocksDBTypeNSJSONSerializable: + return ^NSData * (id key, id value) { + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:value + options:0 + error:&error]; + if (error) { + NSLog(@"Error encoding value: %@", error.debugDescription); + } + return data; + }; + + } +} + ++ (id (^)(id, NSData *))valueDecoderForType:(RocksDBType)type +{ + switch (type) { + case RocksDBTypeNSString: + return ^id (id key, NSData *data) { + if (data == nil) return nil; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + }; + + case RocksDBTypeNSJSONSerializable: + return ^id (id key, NSData * data) { + if (data == nil) return nil; + NSError *error = nil; + id obj = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&error]; + if (error) { + NSLog(@"Error decoding value: %@", error.debugDescription); + } + return obj; + }; + } +} + +@end diff --git a/ObjectiveRocks/RocksDBWriteBatch.h b/ObjectiveRocks/RocksDBWriteBatch.h index 6889647..dfb1868 100644 --- a/ObjectiveRocks/RocksDBWriteBatch.h +++ b/ObjectiveRocks/RocksDBWriteBatch.h @@ -7,17 +7,36 @@ // #import -#import "RocksDBOptions.h" +#import "RocksDBEncodingOptions.h" + +@class RocksDBColumnFamily; + +namespace rocksdb { + class ColumnFamilyHandle; +} @interface RocksDBWriteBatch : NSObject -- (instancetype)initWithOptions:(RocksDBOptions *)options; +- (instancetype)initWithColumnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andEncodingOptions:(RocksDBEncodingOptions *)options; - (void)setObject:(id)anObject forKey:(id)aKey; - (void)setData:(NSData *)data forKey:(NSData *)aKey; +- (void)setObject:(id)anObject forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; +- (void)setData:(NSData *)data forKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; + +- (void)mergeOperation:(NSString *)aMerge forKey:(id)aKey; +- (void)mergeObject:(id)anObject forKey:(id)aKey; +- (void)mergeData:(NSData *)data forKey:(NSData *)aKey; +- (void)mergeOperation:(NSString *)aMerge forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; +- (void)mergeObject:(id)anObject forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; +- (void)mergeData:(NSData *)data forKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; + - (void)deleteObjectForKey:(id)aKey; - (void)deleteDataForKey:(NSData *)aKey; +- (void)deleteObjectForKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; +- (void)deleteDataForKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; - (void)clear; diff --git a/ObjectiveRocks/RocksDBWriteBatch.mm b/ObjectiveRocks/RocksDBWriteBatch.mm index 44ddb58..9340d4a 100644 --- a/ObjectiveRocks/RocksDBWriteBatch.mm +++ b/ObjectiveRocks/RocksDBWriteBatch.mm @@ -7,14 +7,20 @@ // #import "RocksDBWriteBatch.h" +#import "RocksDBColumnFamily.h" #import "RocksDBSlice.h" #import +@interface RocksDBColumnFamily (Private) +@property (nonatomic, assign) rocksdb::ColumnFamilyHandle *columnFamily; +@end + @interface RocksDBWriteBatch () { - RocksDBOptions *_options; + RocksDBEncodingOptions *_encodingOptions; rocksdb::WriteBatch _writeBatch; + rocksdb::ColumnFamilyHandle *_columnFamily; } @property (nonatomic, readonly) rocksdb::WriteBatch writeBatch; @end @@ -24,40 +30,121 @@ @implementation RocksDBWriteBatch #pragma mark - Lifecycle -- (instancetype)initWithOptions:(RocksDBOptions *)options +- (instancetype)initWithColumnFamily:(rocksdb::ColumnFamilyHandle *)columnFamily + andEncodingOptions:(RocksDBEncodingOptions *)options { self = [super init]; if (self) { - _options = options; + _columnFamily = columnFamily; + _encodingOptions = options; } return self; } -#pragma mark - CRD +#pragma mark - Put - (void)setObject:(id)anObject forKey:(id)aKey { - [self setData:EncodeValue(aKey, anObject, _options, nil) - forKey:EncodeKey(aKey, _options, nil)]; + [self setObject:anObject forKey:aKey inColumnFamily:nil]; } - (void)setData:(NSData *)data forKey:(NSData *)aKey +{ + [self setData:data forKey:aKey inColumnFamily:nil]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily +{ + [self setData:EncodeValue(aKey, anObject, _encodingOptions, nil) + forKey:EncodeKey(aKey, _encodingOptions, nil) + inColumnFamily:columnFamily]; +} + +- (void)setData:(NSData *)data forKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily { if (aKey != nil && data != nil) { - _writeBatch.Put(SliceFromData(aKey), + rocksdb::ColumnFamilyHandle *handle = _columnFamily; + if (columnFamily != nil) { + handle = columnFamily.columnFamily; + } + + _writeBatch.Put(handle, + SliceFromData(aKey), SliceFromData(data)); } } +#pragma mark - Merge + +- (void)mergeOperation:(NSString *)aMerge forKey:(id)aKey +{ + [self mergeOperation:aMerge forKey:aKey inColumnFamily:nil]; +} + +- (void)mergeObject:(id)anObject forKey:(id)aKey +{ + [self mergeObject:anObject forKey:aKey inColumnFamily:nil]; +} + +- (void)mergeData:(NSData *)data forKey:(NSData *)aKey +{ + [self mergeData:data forKey:aKey inColumnFamily:nil]; +} + +- (void)mergeOperation:(NSString *)aMerge forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily +{ + [self mergeData:[aMerge dataUsingEncoding:NSUTF8StringEncoding] + forKey:EncodeKey(aKey, _encodingOptions, nil) + inColumnFamily:columnFamily]; +} + +- (void)mergeObject:(id)anObject forKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily +{ + [self mergeData:EncodeValue(aKey, anObject, _encodingOptions, nil) + forKey:EncodeKey(aKey, _encodingOptions, nil) + inColumnFamily:columnFamily]; +} + +- (void)mergeData:(NSData *)data forKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily; +{ + if (aKey != nil && data != nil) { + rocksdb::ColumnFamilyHandle *handle = _columnFamily; + if (columnFamily != nil) { + handle = columnFamily.columnFamily; + } + _writeBatch.Merge(handle, + SliceFromData(aKey), + SliceFromData(data)); + } +} + +#pragma mark - Delete + - (void)deleteObjectForKey:(id)aKey { - [self deleteDataForKey:EncodeKey(aKey, _options, nil)]; + [self deleteObjectForKey:aKey inColumnFamily:nil]; } - (void)deleteDataForKey:(NSData *)aKey +{ + [self deleteDataForKey:aKey inColumnFamily:nil]; +} + +- (void)deleteObjectForKey:(id)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily +{ + [self deleteDataForKey:EncodeKey(aKey, _encodingOptions, nil) + inColumnFamily:columnFamily]; +} + +- (void)deleteDataForKey:(NSData *)aKey inColumnFamily:(RocksDBColumnFamily *)columnFamily { if (aKey != nil) { - _writeBatch.Delete(SliceFromData(aKey)); + rocksdb::ColumnFamilyHandle *handle = _columnFamily; + if (columnFamily != nil) { + handle = columnFamily.columnFamily; + } + _writeBatch.Delete(handle, + SliceFromData(aKey)); } } diff --git a/ObjectiveRocksTests/RocksDBBasicTests.mm b/ObjectiveRocksTests/RocksDBBasicTests.mm new file mode 100644 index 0000000..3c9a54d --- /dev/null +++ b/ObjectiveRocksTests/RocksDBBasicTests.mm @@ -0,0 +1,93 @@ +// +// RocksDBTests.m +// ObjectiveRocks +// +// Created by Iska on 15/11/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBTests.h" + +@interface RocksDBBasicTests : RocksDBTests + +@end + +@implementation RocksDBBasicTests + +- (void)testDB_Open_ErrorIfExists +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + [_rocks close]; + + RocksDB *db = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.errorIfExists = YES; + }]; + + XCTAssertNil(db); +} + +- (void)testDB_CRUD +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + [_rocks setDefaultReadOptions:^(RocksDBReadOptions *readOptions) { + readOptions.fillCache = YES; + readOptions.verifyChecksums = YES; + } andWriteOptions:^(RocksDBWriteOptions *writeOptions) { + writeOptions.syncWrites = YES; + }]; + + + [_rocks setData:Data(@"value 1") forKey:Data(@"key 1")]; + [_rocks setData:Data(@"value 2") forKey:Data(@"key 2")]; + [_rocks setData:Data(@"value 3") forKey:Data(@"key 3")]; + + XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 1")], Data(@"value 1")); + XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 2")], Data(@"value 2")); + XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 3")], Data(@"value 3")); + + [_rocks deleteDataForKey:Data(@"key 2")]; + XCTAssertNil([_rocks dataForKey:Data(@"key 2")]); + + NSError *error = nil; + BOOL ok = [_rocks deleteDataForKey:Data(@"key 2") error:&error]; + XCTAssertTrue(ok); + XCTAssertNil(error); +} + +- (void)testDB_CRUD_Encoded +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSString; + }]; + [_rocks setDefaultReadOptions:^(RocksDBReadOptions *readOptions) { + readOptions.fillCache = YES; + readOptions.verifyChecksums = YES; + } andWriteOptions:^(RocksDBWriteOptions *writeOptions) { + writeOptions.syncWrites = YES; + }]; + + + [_rocks setObject:@"value 1" forKey:@"key 1"]; + [_rocks setObject:@"value 2" forKey:@"key 2"]; + [_rocks setObject:@"value 3" forKey:@"key 3"]; + + XCTAssertEqualObjects([_rocks objectForKey:@"key 1"], @"value 1"); + XCTAssertEqualObjects([_rocks objectForKey:@"key 2"], @"value 2"); + XCTAssertEqualObjects([_rocks objectForKey:@"key 3"], @"value 3"); + + [_rocks deleteObjectForKey:@"key 2"]; + XCTAssertNil([_rocks objectForKey:@"key 2"]); + + NSError *error = nil; + BOOL ok = [_rocks deleteObjectForKey:@"key 2" error:&error]; + XCTAssertTrue(ok); + XCTAssertNil(error); +} + +@end diff --git a/ObjectiveRocksTests/RocksDBColumnFamilyTests.mm b/ObjectiveRocksTests/RocksDBColumnFamilyTests.mm new file mode 100644 index 0000000..452b5ed --- /dev/null +++ b/ObjectiveRocksTests/RocksDBColumnFamilyTests.mm @@ -0,0 +1,293 @@ +// +// RocksDBColumnFamilyTests.m +// ObjectiveRocks +// +// Created by Iska on 29/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBTests.h" + +@interface RocksDBColumnFamilyTests : RocksDBTests + +@end + +@implementation RocksDBColumnFamilyTests + +- (void)testColumnFamilies_List +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + [_rocks close]; + + NSArray *names = [RocksDB listColumnFamiliesInDatabaseAtPath:_path]; + + XCTAssertTrue(names.count == 1); + XCTAssertEqualObjects(names[0], @"default"); +} + +- (void)testColumnFamilies_Create +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + + RocksDBColumnFamily *columnFamily = [_rocks createColumnFamilyWithName:@"new_cf" andOptions:nil]; + + [columnFamily close]; + [_rocks close]; + + NSArray *names = [RocksDB listColumnFamiliesInDatabaseAtPath:_path]; + + XCTAssertTrue(names.count == 2); + XCTAssertEqualObjects(names[0], @"default"); + XCTAssertEqualObjects(names[1], @"new_cf"); +} + +- (void)testColumnFamilies_Drop +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + + RocksDBColumnFamily *columnFamily = [_rocks createColumnFamilyWithName:@"new_cf" andOptions:nil]; + + [columnFamily drop]; + [columnFamily close]; + [_rocks close]; + + NSArray *names = [RocksDB listColumnFamiliesInDatabaseAtPath:_path]; + + XCTAssertTrue(names.count == 1); + XCTAssertEqualObjects(names[0], @"default"); +} + +- (void)testColumnFamilies_Open +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + }]; + + RocksDBColumnFamily *columnFamily = [_rocks createColumnFamilyWithName:@"new_cf" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorBytewiseDescending]; + }]; + + [columnFamily close]; + [_rocks close]; + + NSArray *names = [RocksDB listColumnFamiliesInDatabaseAtPath:_path]; + + XCTAssertTrue(names.count == 2); + XCTAssertEqualObjects(names[0], @"default"); + XCTAssertEqualObjects(names[1], @"new_cf"); + + RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + [descriptor addColumnFamilyWithName:@"default" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + }]; + [descriptor addColumnFamilyWithName:@"new_cf" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorBytewiseDescending]; + }]; + + _rocks = [[RocksDB alloc] initWithPath:_path columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + options.createIfMissing = YES; + }]; + + XCTAssertNotNil(_rocks); + + XCTAssertTrue(_rocks.columnFamilies.count == 2); + + RocksDBColumnFamily *defaultColumnFamily = _rocks.columnFamilies[0]; + RocksDBColumnFamily *newColumnFamily = _rocks.columnFamilies[1]; + + XCTAssertNotNil(defaultColumnFamily); + XCTAssertNotNil(newColumnFamily); + + [defaultColumnFamily close]; + [newColumnFamily close]; +} + +- (void)testColumnFamilies_Open_ComparatorMismatch +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + }]; + + RocksDBColumnFamily *columnFamily = [_rocks createColumnFamilyWithName:@"new_cf" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorBytewiseDescending]; + }]; + + [columnFamily close]; + [_rocks close]; + + NSArray *names = [RocksDB listColumnFamiliesInDatabaseAtPath:_path]; + + XCTAssertTrue(names.count == 2); + XCTAssertEqualObjects(names[0], @"default"); + XCTAssertEqualObjects(names[1], @"new_cf"); + + RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + [descriptor addColumnFamilyWithName:@"default" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + }]; + [descriptor addColumnFamilyWithName:@"new_cf" andOptions:^(RocksDBColumnFamilyOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + }]; + + _rocks = [[RocksDB alloc] initWithPath:_path columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + options.createIfMissing = YES; + }]; + + XCTAssertNil(_rocks); +} + +- (void)testColumnFamilies_CRUD +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + + [_rocks setData:Data(@"df_value") forKey:Data(@"df_key1")]; + [_rocks setData:Data(@"df_value") forKey:Data(@"df_key2")]; + + RocksDBColumnFamily *columnFamily = [_rocks createColumnFamilyWithName:@"new_cf" andOptions:nil]; + + [columnFamily setData:Data(@"cf_value") forKey:Data(@"cf_key1")]; + [columnFamily setData:Data(@"cf_value") forKey:Data(@"cf_key2")]; + + [columnFamily close]; + [_rocks close]; + + RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + [descriptor addColumnFamilyWithName:@"default" andOptions:nil]; + [descriptor addColumnFamilyWithName:@"new_cf" andOptions:nil]; + + _rocks = [[RocksDB alloc] initWithPath:_path columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + options.createIfMissing = YES; + }]; + + RocksDBColumnFamily *defaultColumnFamily = _rocks.columnFamilies[0]; + RocksDBColumnFamily *newColumnFamily = _rocks.columnFamilies[1]; + + XCTAssertEqualObjects([_rocks dataForKey:Data(@"df_key1")], Data(@"df_value")); + XCTAssertEqualObjects([_rocks dataForKey:Data(@"df_key2")], Data(@"df_value")); + XCTAssertNil([_rocks dataForKey:Data(@"cf_key1")]); + XCTAssertNil([_rocks dataForKey:Data(@"cf_key2")]); + + XCTAssertEqualObjects([defaultColumnFamily dataForKey:Data(@"df_key1")], Data(@"df_value")); + XCTAssertEqualObjects([defaultColumnFamily dataForKey:Data(@"df_key2")], Data(@"df_value")); + + XCTAssertNil([defaultColumnFamily dataForKey:Data(@"cf_key1")]); + XCTAssertNil([defaultColumnFamily dataForKey:Data(@"cf_key2")]); + + XCTAssertEqualObjects([newColumnFamily dataForKey:Data(@"cf_key1")], Data(@"cf_value")); + XCTAssertEqualObjects([newColumnFamily dataForKey:Data(@"cf_key2")], Data(@"cf_value")); + + XCTAssertNil([newColumnFamily dataForKey:Data(@"df_key1")]); + XCTAssertNil([newColumnFamily dataForKey:Data(@"df_key2")]); + + [newColumnFamily deleteDataForKey:Data(@"cf_key1")]; + XCTAssertNil([newColumnFamily dataForKey:Data(@"cf_key1")]); + + [newColumnFamily deleteDataForKey:Data(@"cf_key1")]; + XCTAssertNil([newColumnFamily dataForKey:Data(@"cf_key1")]); + + [defaultColumnFamily close]; + [newColumnFamily close]; +} + +- (void)testColumnFamilies_WriteBatch +{ + RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + [descriptor addColumnFamilyWithName:@"default" andOptions:nil]; + [descriptor addColumnFamilyWithName:@"new_cf" andOptions:nil]; + + _rocks = [[RocksDB alloc] initWithPath:_path columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + options.createIfMissing = YES; + options.createMissingColumnFamilies = YES; + }]; + + RocksDBColumnFamily *defaultColumnFamily = _rocks.columnFamilies[0]; + RocksDBColumnFamily *newColumnFamily = _rocks.columnFamilies[1]; + + [newColumnFamily setData:Data(@"xyz_value") forKey:Data(@"xyz")]; + + RocksDBWriteBatch *batch = [newColumnFamily writeBatch]; + + [batch setData:Data(@"cf_value1") forKey:Data(@"cf_key1")]; + [batch setData:Data(@"df_value") forKey:Data(@"df_key") inColumnFamily:defaultColumnFamily]; + [batch setData:Data(@"cf_value2") forKey:Data(@"cf_key2")]; + [batch deleteDataForKey:Data(@"xyz") inColumnFamily:defaultColumnFamily]; + [batch deleteDataForKey:Data(@"xyz")]; + + [_rocks applyWriteBatch:batch withWriteOptions:nil]; + + XCTAssertEqualObjects([defaultColumnFamily dataForKey:Data(@"df_key")], Data(@"df_value")); + XCTAssertNil([defaultColumnFamily dataForKey:Data(@"df_key1")]); + XCTAssertNil([defaultColumnFamily dataForKey:Data(@"df_key2")]); + + XCTAssertEqualObjects([newColumnFamily dataForKey:Data(@"cf_key1")], Data(@"cf_value1")); + XCTAssertEqualObjects([newColumnFamily dataForKey:Data(@"cf_key2")], Data(@"cf_value2")); + XCTAssertNil([newColumnFamily dataForKey:Data(@"df_key")]); + + XCTAssertNil([defaultColumnFamily dataForKey:Data(@"xyz")]); + XCTAssertNil([newColumnFamily dataForKey:Data(@"xyz")]); + + [defaultColumnFamily close]; + [newColumnFamily close]; +} + +- (void)testColumnFamilies_Iterator +{ + RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + [descriptor addColumnFamilyWithName:@"default" andOptions:nil]; + [descriptor addColumnFamilyWithName:@"new_cf" andOptions:nil]; + + _rocks = [[RocksDB alloc] initWithPath:_path columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + options.createIfMissing = YES; + options.createMissingColumnFamilies = YES; + }]; + + RocksDBColumnFamily *defaultColumnFamily = _rocks.columnFamilies[0]; + RocksDBColumnFamily *newColumnFamily = _rocks.columnFamilies[1]; + + [defaultColumnFamily setData:Data(@"df_value1") forKey:Data(@"df_key1")]; + [defaultColumnFamily setData:Data(@"df_value2") forKey:Data(@"df_key2")]; + + [newColumnFamily setData:Data(@"cf_value1") forKey:Data(@"cf_key1")]; + [newColumnFamily setData:Data(@"cf_value2") forKey:Data(@"cf_key2")]; + + RocksDBIterator *dfIterator = [defaultColumnFamily iterator]; + + NSMutableArray *actual = [NSMutableArray array]; + for ([dfIterator seekToFirst]; [dfIterator isValid]; [dfIterator next]) { + [actual addObject:Str([dfIterator key])]; + [actual addObject:Str([dfIterator value])]; + } + + NSArray *expected = @[ @"df_key1", @"df_value1", @"df_key2", @"df_value2" ]; + XCTAssertEqualObjects(actual, expected); + + [dfIterator close]; + + RocksDBIterator *cfIterator = [newColumnFamily iterator]; + + actual = [NSMutableArray array]; + for ([cfIterator seekToFirst]; [cfIterator isValid]; [cfIterator next]) { + [actual addObject:Str([cfIterator key])]; + [actual addObject:Str([cfIterator value])]; + } + + expected = @[ @"cf_key1", @"cf_value1", @"cf_key2", @"cf_value2" ]; + XCTAssertEqualObjects(actual, expected); + + [cfIterator close]; + + [defaultColumnFamily close]; + [newColumnFamily close]; +} + +@end diff --git a/ObjectiveRocksTests/RocksDBComparatorTests.mm b/ObjectiveRocksTests/RocksDBComparatorTests.mm index 0ca445f..d28683e 100644 --- a/ObjectiveRocksTests/RocksDBComparatorTests.mm +++ b/ObjectiveRocksTests/RocksDBComparatorTests.mm @@ -6,164 +6,250 @@ // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" +#import "RocksDBTests.h" -#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] -#define Str(x) [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding] +@interface RocksDBComparatorTests : RocksDBTests -@interface RocksDBComparatorTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} @end @implementation RocksDBComparatorTests -- (void)setUp +- (void)testComparator_Native_Bytewise_Ascending { - [super setUp]; + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorBytewiseAscending]; + }]; - _path = [[NSBundle bundleForClass:[self class]] resourcePath]; - _path = [_path stringByAppendingPathComponent:@"ObjectiveRocks"]; - [self cleanupDB]; -} + [_rocks setData:Data(@"abc1") forKey:Data(@"abc1")]; + [_rocks setData:Data(@"abc2") forKey:Data(@"abc2")]; + [_rocks setData:Data(@"abc3") forKey:Data(@"abc3")]; -- (void)tearDown -{ - [_rocks close]; - [self cleanupDB]; - [super tearDown]; + RocksDBIterator *iterator = [_rocks iterator]; + + [iterator seekToFirst]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc1")); + XCTAssertEqualObjects(iterator.value, Data(@"abc1")); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc2")); + XCTAssertEqualObjects(iterator.value, Data(@"abc2")); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc3")); + XCTAssertEqualObjects(iterator.value, Data(@"abc3")); + + [iterator next]; + + XCTAssertFalse(iterator.isValid); + + [iterator seekToLast]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc3")); + XCTAssertEqualObjects(iterator.value, Data(@"abc3")); + + [iterator seekToKey:Data(@"abc")]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc1")); + XCTAssertEqualObjects(iterator.value, Data(@"abc1")); + + [iterator close]; } -- (void)cleanupDB +- (void)testComparator_Native_Bytewise_Descending { - [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorBytewiseDescending]; + }]; + + [_rocks setData:Data(@"abc1") forKey:Data(@"abc1")]; + [_rocks setData:Data(@"abc2") forKey:Data(@"abc2")]; + [_rocks setData:Data(@"abc3") forKey:Data(@"abc3")]; + + RocksDBIterator *iterator = [_rocks iterator]; + + [iterator seekToFirst]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc3")); + XCTAssertEqualObjects(iterator.value, Data(@"abc3")); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc2")); + XCTAssertEqualObjects(iterator.value, Data(@"abc2")); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc1")); + XCTAssertEqualObjects(iterator.value, Data(@"abc1")); + + [iterator next]; + + XCTAssertFalse(iterator.isValid); + + [iterator seekToLast]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc1")); + XCTAssertEqualObjects(iterator.value, Data(@"abc1")); + + [iterator seekToKey:Data(@"abc")]; + + XCTAssertFalse(iterator.isValid); + + [iterator seekToKey:Data(@"abc999")]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"abc3")); + XCTAssertEqualObjects(iterator.value, Data(@"abc3")); + + [iterator close]; } -- (void)testDB_Comparator_StringCompare +- (void)testComparator_StringCompare_Ascending { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.comparator = [RocksDBComparator comaparatorWithName:@"comparator" andBlock:^int (id key1, id key2) { - return [Str(key1) compare:Str(key2)]; - }]; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareAscending]; + options.keyType = RocksDBTypeNSString; }]; - /* Expected Array: [A0, A1, A11, A12 ... A2, A21 ...] */ + NSMutableArray *expected = [NSMutableArray array]; - for (int i = 0; i < 26; i++) { + for (int i = 0; i < 10000; i++) { NSString *str = [NSString stringWithFormat:@"A%d", i]; [expected addObject:str]; [_rocks setData:Data(str) forKey:Data(str)]; } + + /* Expected Array: [A0, A1, A10, A100, A1000, A1001, A1019, A102, A1020, ...] */ [expected sortUsingSelector:@selector(compare:)]; __block NSUInteger idx = 0; RocksDBIterator *iterator = [_rocks iterator]; [iterator enumerateKeysUsingBlock:^(id key, BOOL *stop) { - XCTAssertEqualObjects(Str(key), expected[idx]); + XCTAssertEqualObjects(key, expected[idx]); idx++; }]; } -- (void)testDB_Comparator_StringLengthCompare_Asc +- (void)testComparator_StringCompare_Descending { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.comparator = [RocksDBComparator comaparatorWithName:@"comparator" andBlock:^int (id key1, id key2) { - if (Str(key1).length > Str(key2).length) return 1; - if (Str(key1).length < Str(key2).length) return -1; - return 0; - }]; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorStringCompareDescending]; + options.keyType = RocksDBTypeNSString; }]; - /* Expected Array: [A, BB, CCC, ... , Y{25}, Z{26}] */ + NSMutableArray *expected = [NSMutableArray array]; - for (unichar i = 65; i <= 90; i++) { - NSString *str = [NSString stringWithCharacters:&i length:1]; - str = [str stringByPaddingToLength:(i-64) withString:str startingAtIndex:0]; + for (int i = 0; i < 10000; i++) { + NSString *str = [NSString stringWithFormat:@"A%d", i]; [expected addObject:str]; [_rocks setData:Data(str) forKey:Data(str)]; } - __block NSUInteger idx = 0; + /* Expected Array: [A9999, A9998 .. A9990, A999, A9989, ...] */ + [expected sortUsingSelector:@selector(compare:)]; + + __block NSUInteger idx = 9999; RocksDBIterator *iterator = [_rocks iterator]; [iterator enumerateKeysUsingBlock:^(id key, BOOL *stop) { - XCTAssertEqualObjects(Str(key), expected[idx]); - idx++; + XCTAssertEqualObjects(key, expected[idx]); + idx--; }]; } -- (void)testDB_Comparator_StringLengthCompare_Desc +- (void)testComparator_Number_Ascending { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.comparator = [RocksDBComparator comaparatorWithName:@"comparator" andBlock:^int (id key1, id key2) { - if (Str(key1).length > Str(key2).length) return -1; - if (Str(key1).length < Str(key2).length) return 1; - return 0; - }]; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorNumberAscending]; + + options.keyEncoder = ^ NSData * (id key) { + u_int32_t r = [key unsignedIntValue]; + return NumData(r); + }; + options.keyDecoder = ^ id (NSData *data) { + if (data == nil) return nil; + u_int32_t r; + Val(data, r); + return @(r); + }; }]; - /* Expected Array: [Z{26}, Y{25}, ..., CCC, BB, A] */ - NSMutableArray *expected = [NSMutableArray array]; - for (unichar i = 90; i >= 65; i--) { - NSString *str = [NSString stringWithCharacters:&i length:1]; - str = [str stringByPaddingToLength:(i-64) withString:str startingAtIndex:0]; - [expected addObject:str]; - [_rocks setData:Data(str) forKey:Data(str)]; + for (int i = 0; i < 10000; i++) { + u_int32_t r = arc4random_uniform(UINT32_MAX); + if ([_rocks objectForKey:@(r)] != nil) { + i--; + } else { + [_rocks setObject:Data(@"value") forKey:@(r)]; + } } - __block NSUInteger idx = 0; + __block NSUInteger count = 0; + __block NSNumber *lastKey = [NSNumber numberWithUnsignedInt:0]; + RocksDBIterator *iterator = [_rocks iterator]; [iterator enumerateKeysUsingBlock:^(id key, BOOL *stop) { - XCTAssertEqualObjects(Str(key), expected[idx]); - idx++; + XCTAssertGreaterThan(key, lastKey); + lastKey = key; + count++; }]; + + XCTAssertEqual(count, 10000); } -- (void)testDB_Comparator_StringLengthCompare_Desc_Encoded +- (void)testComparator_Number_Descending { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.comparator = [RocksDBComparator comaparatorWithName:@"comparator" andBlock:^int (NSString *key1, NSString *key2) { - if (key1.length > key2.length) return -1; - if (key1.length < key2.length) return 1; - return 0; - }]; + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorNumberDescending]; options.keyEncoder = ^ NSData * (id key) { - return [key dataUsingEncoding:NSUTF8StringEncoding]; + u_int32_t r = [key unsignedIntValue]; + return NumData(r); }; - options.keyDecoder = ^ NSString * (NSData *data) { - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; - options.valueEncoder = ^ NSData * (id key, id value) { - return [value dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.valueDecoder = ^ NSString * (id key, NSData * data) { + options.keyDecoder = ^ id (NSData *data) { if (data == nil) return nil; - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + u_int32_t r; + Val(data, r); + return @(r); }; }]; - /* Expected Array: [Z{26}, Y{25}, ..., CCC, BB, A] */ - NSMutableArray *expected = [NSMutableArray array]; - for (unichar i = 90; i >= 65; i--) { - NSString *str = [NSString stringWithCharacters:&i length:1]; - str = [str stringByPaddingToLength:(i-64) withString:str startingAtIndex:0]; - [expected addObject:str]; - [_rocks setObject:str forKey:str]; + for (int i = 0; i < 10000; i++) { + u_int32_t r = arc4random_uniform(UINT32_MAX); + if ([_rocks objectForKey:@(r)] != nil) { + i--; + } else { + [_rocks setObject:Data(@"value") forKey:@(r)]; + } } - __block NSUInteger idx = 0; + __block NSUInteger count = 0; + __block NSNumber *lastKey = [NSNumber numberWithUnsignedInt:UINT32_MAX]; + RocksDBIterator *iterator = [_rocks iterator]; [iterator enumerateKeysUsingBlock:^(id key, BOOL *stop) { - XCTAssertEqualObjects(key, expected[idx]); - idx++; + XCTAssertLessThan(key, lastKey); + lastKey = key; + count++; }]; + + XCTAssertEqual(count, 10000); } @end diff --git a/ObjectiveRocksTests/RocksDBIteratorTests.mm b/ObjectiveRocksTests/RocksDBIteratorTests.mm index 0b46d01..2cf088e 100644 --- a/ObjectiveRocksTests/RocksDBIteratorTests.mm +++ b/ObjectiveRocksTests/RocksDBIteratorTests.mm @@ -6,42 +6,14 @@ // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" +#import "RocksDBTests.h" -#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] -#define Str(x) [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding] +@interface RocksDBIteratorTests : RocksDBTests -@interface RocksDBIteratorTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} @end @implementation RocksDBIteratorTests -- (void)setUp -{ - [super setUp]; - - _path = [[NSBundle bundleForClass:[self class]] resourcePath]; - _path = [_path stringByAppendingPathComponent:@"ObjectiveRocks"]; - [self cleanupDB]; -} - -- (void)tearDown -{ - [_rocks close]; - [self cleanupDB]; - [super tearDown]; -} - -- (void)cleanupDB -{ - [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; -} - - (void)testDB_Iterator { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { @@ -64,6 +36,50 @@ - (void)testDB_Iterator [iterator close]; } +- (void)testDB_Iterator_Seek +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + }]; + + [_rocks setData:Data(@"value 1") forKey:Data(@"key 1")]; + [_rocks setData:Data(@"value 2") forKey:Data(@"key 2")]; + + RocksDBIterator *iterator = [_rocks iterator]; + + [iterator seekToFirst]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"key 1")); + XCTAssertEqualObjects(iterator.value, Data(@"value 1")); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"key 2")); + XCTAssertEqualObjects(iterator.value, Data(@"value 2")); + + [iterator next]; + + XCTAssertFalse(iterator.isValid); + + [iterator seekToLast]; + [iterator previous]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"key 1")); + XCTAssertEqualObjects(iterator.value, Data(@"value 1")); + + [iterator seekToFirst]; + [iterator seekToLast]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, Data(@"key 2")); + XCTAssertEqualObjects(iterator.value, Data(@"value 2")); + + [iterator close]; +} + - (void)testDB_Iterator_Reverse { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { @@ -159,19 +175,8 @@ - (void)testDB_Iterator_Encoded { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.keyEncoder = ^ NSData * (id key) { - return [key dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.keyDecoder = ^ NSString * (NSData *data) { - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; - options.valueEncoder = ^ NSData * (id key, id value) { - return [value dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.valueDecoder = ^ NSString * (id key, NSData * data) { - if (data == nil) return nil; - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSString; }]; [_rocks setObject:@"value 1" forKey:@"Key 1"]; diff --git a/ObjectiveRocksTests/RocksDBMergeOperatorTests.mm b/ObjectiveRocksTests/RocksDBMergeOperatorTests.mm index 83376ad..99edc8d 100644 --- a/ObjectiveRocksTests/RocksDBMergeOperatorTests.mm +++ b/ObjectiveRocksTests/RocksDBMergeOperatorTests.mm @@ -6,94 +6,61 @@ // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" +#import "RocksDBTests.h" -#define NumData(x) [NSData dataWithBytes:&x length:sizeof(x)] -#define StrData(x) [x dataUsingEncoding:NSUTF8StringEncoding] -#define Val(data, x) [data getBytes:&x length:sizeof(x)]; +@interface RocksDBMergeOperatorTests : RocksDBTests -@interface RocksDBMergeOperatorTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} @end @implementation RocksDBMergeOperatorTests -- (void)setUp -{ - [super setUp]; - - _path = [[NSBundle bundleForClass:[self class]] resourcePath]; - _path = [_path stringByAppendingPathComponent:@"ObjectiveRocks"]; - [self cleanupDB]; -} - -- (void)tearDown -{ - [_rocks close]; - [self cleanupDB]; - [super tearDown]; -} - -- (void)cleanupDB -{ - [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; -} - - (void)testAssociativeMergeOperator { - RocksDBMergeOperator *addOperator = [RocksDBMergeOperator operatorWithName:@"operator" - andBlock:^id (id key, id existingValue, id value) { - uint64_t prev = 0; - if (existingValue != nil) { - [existingValue getBytes:&prev length:sizeof(uint64_t)]; - } + RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator" + andBlock:^id (id key, id existingValue, id value) { + uint64_t prev = 0; + if (existingValue != nil) { + [existingValue getBytes:&prev length:sizeof(uint64_t)]; + } - uint64_t plus; - [value getBytes:&plus length:sizeof(uint64_t)]; + uint64_t plus; + [value getBytes:&plus length:sizeof(uint64_t)]; - uint64_t result = prev + plus; - return [NSData dataWithBytes:&result length:sizeof(uint64_t)]; - }]; + uint64_t result = prev + plus; + return [NSData dataWithBytes:&result length:sizeof(uint64_t)]; + }]; _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.mergeOperator = addOperator; + options.mergeOperator = mergeOp; }]; uint64_t value = 1; - [_rocks mergeData:NumData(value) forKey:StrData(@"Key 1")]; + [_rocks mergeData:NumData(value) forKey:Data(@"Key 1")]; value = 5; - [_rocks mergeData:NumData(value) forKey:StrData(@"Key 1")]; + [_rocks mergeData:NumData(value) forKey:Data(@"Key 1")]; uint64_t res; - Val([_rocks dataForKey:StrData(@"Key 1")], res); - + Val([_rocks dataForKey:Data(@"Key 1")], res); + XCTAssertTrue(res == 6); } -- (void)testAssociativeMergeOperator_Encoded +- (void)testAssociativeMergeOperator_NumberAdd_Encoded { - RocksDBMergeOperator *addOperator = [RocksDBMergeOperator operatorWithName:@"operator" - andBlock:^id (id key, NSNumber *existingValue, NSNumber *value) { - NSNumber *result = @(existingValue.floatValue + value.floatValue); - return result; - }]; + RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator" + andBlock:^id (id key, NSNumber *existingValue, NSNumber *value) { + NSNumber *result = @(existingValue.floatValue + value.floatValue); + return result; + }]; _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.mergeOperator = addOperator; + options.mergeOperator = mergeOp; + + options.keyType = RocksDBTypeNSString; - options.keyEncoder = ^ NSData * (id key) { - return [key dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.keyDecoder = ^ NSString * (NSData *data) { - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; options.valueEncoder = ^ NSData * (id key, id value) { float val = [value floatValue]; NSData *data = [NSData dataWithBytes:&val length:sizeof(val)]; @@ -116,4 +83,94 @@ - (void)testAssociativeMergeOperator_Encoded XCTAssertEqualWithAccuracy([[_rocks objectForKey:@"Key 1"] floatValue], 300.666, 0.0001); } +- (void)testAssociativeMergeOperator_DictionaryPut_Encoded +{ + RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator" + andBlock:^id (id key, NSMutableDictionary *existingValue, id value) { + if (existingValue != nil) { + [existingValue addEntriesFromDictionary:value]; + return existingValue; + } else { + return value; + } + }]; + + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.mergeOperator = mergeOp; + + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSJSONSerializable; + }]; + + [_rocks setObject:@{@"Key 1": @"Value 1"} forKey:@"Dict Key"]; + + [_rocks mergeObject:@{@"Key 1": @"Value 1 New"} forKey:@"Dict Key"]; + + [_rocks mergeObject:@{@"Key 2": @"Value 2"} forKey:@"Dict Key"]; + + [_rocks mergeObject:@{@"Key 3": @"Value 3"} forKey:@"Dict Key"]; + + [_rocks mergeObject:@{@"Key 4": @"Value 4"} forKey:@"Dict Key"]; + + [_rocks mergeObject:@{@"Key 5": @"Value 5"} forKey:@"Dict Key"]; + + NSDictionary *expected = @{@"Key 1" : @"Value 1 New", + @"Key 2" : @"Value 2", + @"Key 3" : @"Value 3", + @"Key 4" : @"Value 4", + @"Key 5" : @"Value 5"}; + + XCTAssertEqualObjects([_rocks objectForKey:@"Dict Key"], expected); +} + +- (void)testMergeOperator_DictionaryUpdate_Encoded +{ + RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator" + partialMergeBlock:^id(id key, NSString *leftOperand, NSString *rightOperand) { + NSString *left = [leftOperand componentsSeparatedByString:@":"][0]; + NSString *right = [rightOperand componentsSeparatedByString:@":"][0]; + if ([left isEqualToString:right]) { + return rightOperand; + } + return nil; + } fullMergeBlock:^id(id key, NSMutableDictionary *existing, NSArray *operands) { + for (NSString *op in operands) { + NSArray *comp = [op componentsSeparatedByString:@":"]; + NSString *action = comp[1]; + if ([action isEqualToString:@"DELETE"]) { + [existing removeObjectForKey:comp[0]]; + } else { + existing[comp[0]] = comp[2]; + } + } + return existing; + }]; + + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.mergeOperator = mergeOp; + + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSJSONSerializable; + }]; + + NSDictionary *object = @{@"Key 1" : @"Value 1", + @"Key 2" : @"Value 2", + @"Key 3" : @"Value 3"}; + + [_rocks setObject:object forKey:@"Dict Key"]; + + [_rocks mergeOperation:@"Key 1:UPDATE:Value X" forKey:@"Dict Key"]; + [_rocks mergeOperation:@"Key 4:INSERT:Value 4" forKey:@"Dict Key"]; + [_rocks mergeOperation:@"Key 2:DELETE" forKey:@"Dict Key"]; + [_rocks mergeOperation:@"Key 1:UPDATE:Value 1 New" forKey:@"Dict Key"]; + + NSDictionary *expected = @{@"Key 1" : @"Value 1 New", + @"Key 3" : @"Value 3", + @"Key 4" : @"Value 4"}; + + XCTAssertEqualObjects([_rocks objectForKey:@"Dict Key"], expected); +} + @end diff --git a/ObjectiveRocksTests/RocksDBPrefixExtractorTests.mm b/ObjectiveRocksTests/RocksDBPrefixExtractorTests.mm new file mode 100644 index 0000000..c8d1e01 --- /dev/null +++ b/ObjectiveRocksTests/RocksDBPrefixExtractorTests.mm @@ -0,0 +1,69 @@ +// +// RocksDBPrefixExtractorTests.m +// ObjectiveRocks +// +// Created by Iska on 26/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "RocksDBTests.h" + +#import "ObjectiveRocks.h" + +@interface RocksDBPrefixExtractorTests : RocksDBTests + +@end + +@implementation RocksDBPrefixExtractorTests + +- (void)testPrefixExtractor_FixedLength +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.prefixExtractor = [RocksDBPrefixExtractor prefixExtractorWithType:RocksDBPrefixFixedLength length:3]; + + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSString; + }]; + + [_rocks setObject:@"x" forKey:@"100A"]; + [_rocks setObject:@"x" forKey:@"100B"]; + + [_rocks setObject:@"x" forKey:@"101A"]; + [_rocks setObject:@"x" forKey:@"101B"]; + + RocksDBIterator *iterator = [_rocks iterator]; + + + NSMutableArray *keys = [NSMutableArray array]; + [iterator enumerateKeysWithPrefix:@"100" usingBlock:^(id key, BOOL *stop) { + [keys addObject:key]; + }]; + + XCTAssertEqual(keys.count, 2); + + NSArray *expected = @[@"100A", @"100B"]; + XCTAssertEqualObjects(keys, expected); + + keys = [NSMutableArray array]; + [iterator enumerateKeysWithPrefix:@"101" usingBlock:^(id key, BOOL *stop) { + [keys addObject:key]; + }]; + + XCTAssertEqual(keys.count, 2); + + expected = @[@"101A", @"101B"]; + XCTAssertEqualObjects(keys, expected); + + [iterator seekToKey:@"100XYZ"]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, @"100A"); + + [iterator next]; + + XCTAssertTrue(iterator.isValid); + XCTAssertEqualObjects(iterator.key, @"100B"); +} + +@end diff --git a/ObjectiveRocksTests/RocksDBSnapshotTests.mm b/ObjectiveRocksTests/RocksDBSnapshotTests.mm index 247ed30..ab6f606 100644 --- a/ObjectiveRocksTests/RocksDBSnapshotTests.mm +++ b/ObjectiveRocksTests/RocksDBSnapshotTests.mm @@ -6,42 +6,14 @@ // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" +#import "RocksDBTests.h" -#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] -#define Str(x) [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding] +@interface RocksDBSnapshotTests : RocksDBTests -@interface RocksDBSnapshotTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} @end @implementation RocksDBSnapshotTests -- (void)setUp -{ - [super setUp]; - - _path = [[NSBundle bundleForClass:[self class]] resourcePath]; - _path = [_path stringByAppendingPathComponent:@"ObjectiveRocks"]; - [self cleanupDB]; -} - -- (void)tearDown -{ - [_rocks close]; - [self cleanupDB]; - [super tearDown]; -} - -- (void)cleanupDB -{ - [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; -} - - (void)testSnapshot { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { diff --git a/ObjectiveRocksTests/RocksDBTests.h b/ObjectiveRocksTests/RocksDBTests.h new file mode 100644 index 0000000..d2bfde1 --- /dev/null +++ b/ObjectiveRocksTests/RocksDBTests.h @@ -0,0 +1,41 @@ +// +// RocksDBTests.h +// ObjectiveRocks +// +// Created by Iska on 24/12/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "ObjectiveRocks.h" + +#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] +#define Str(x) [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding] + +#define NumData(x) [NSData dataWithBytes:&x length:sizeof(x)] +#define Val(data, x) [data getBytes:&x length:sizeof(x)]; + +@interface RocksDBTests : XCTestCase +{ + NSString *_path; + RocksDB *_rocks; +} +@end + +@interface NSMutableArray (Shuffle) +- (void)shuffle; +@end + +@implementation NSMutableArray (Shuffle) + +- (void)shuffle +{ + NSUInteger count = [self count]; + for (NSUInteger i = 0; i < count; ++i) { + NSInteger remainingCount = count - i; + NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount); + [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex]; + } +} + +@end \ No newline at end of file diff --git a/ObjectiveRocksTests/RocksDBTests.mm b/ObjectiveRocksTests/RocksDBTests.mm index 4ffd188..f698cf6 100644 --- a/ObjectiveRocksTests/RocksDBTests.mm +++ b/ObjectiveRocksTests/RocksDBTests.mm @@ -1,23 +1,12 @@ // -// RocksDBTests.m +// ObjectiveRocksTests.m // ObjectiveRocks // -// Created by Iska on 15/11/14. +// Created by Iska on 24/12/14. // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" - -#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] -#define Str(x) [[NSString alloc] initWithData:x encoding:NSUTF8StringEncoding] - -@interface RocksDBTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} -@end +#import "RocksDBTests.h" @implementation RocksDBTests @@ -42,91 +31,4 @@ - (void)cleanupDB [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; } -- (void)testDB_Open_ErrorIfExists -{ - _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { - options.createIfMissing = YES; - }]; - [_rocks close]; - - RocksDB *db = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { - options.errorIfExists = YES; - }]; - - XCTAssertNil(db); -} - -- (void)testDB_CRUD -{ - _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { - options.createIfMissing = YES; - }]; - [_rocks setDefaultReadOptions:^(RocksDBReadOptions *readOptions) { - readOptions.fillCache = YES; - readOptions.verifyChecksums = YES; - } andWriteOptions:^(RocksDBWriteOptions *writeOptions) { - writeOptions.syncWrites = YES; - }]; - - - [_rocks setData:Data(@"value 1") forKey:Data(@"key 1")]; - [_rocks setData:Data(@"value 2") forKey:Data(@"key 2")]; - [_rocks setData:Data(@"value 3") forKey:Data(@"key 3")]; - - XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 1")], Data(@"value 1")); - XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 2")], Data(@"value 2")); - XCTAssertEqualObjects([_rocks dataForKey:Data(@"key 3")], Data(@"value 3")); - - [_rocks deleteDataForKey:Data(@"key 2")]; - XCTAssertNil([_rocks dataForKey:Data(@"key 2")]); - - NSError *error = nil; - BOOL ok = [_rocks deleteDataForKey:Data(@"key 2") error:&error]; - XCTAssertTrue(ok); - XCTAssertNil(error); -} - -- (void)testDB_CRUD_Encoded -{ - _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { - options.createIfMissing = YES; - options.keyEncoder = ^ NSData * (id key) { - return [key dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.keyDecoder = ^ NSString * (NSData *data) { - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; - options.valueEncoder = ^ NSData * (id key, id value) { - return [value dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.valueDecoder = ^ NSString * (id key, NSData * data) { - if (data == nil) return nil; - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; - }]; - [_rocks setDefaultReadOptions:^(RocksDBReadOptions *readOptions) { - readOptions.fillCache = YES; - readOptions.verifyChecksums = YES; - } andWriteOptions:^(RocksDBWriteOptions *writeOptions) { - writeOptions.syncWrites = YES; - }]; - - - [_rocks setObject:@"value 1" forKey:@"key 1"]; - [_rocks setObject:@"value 2" forKey:@"key 2"]; - [_rocks setObject:@"value 3" forKey:@"key 3"]; - - XCTAssertEqualObjects([_rocks objectForKey:@"key 1"], @"value 1"); - XCTAssertEqualObjects([_rocks objectForKey:@"key 2"], @"value 2"); - XCTAssertEqualObjects([_rocks objectForKey:@"key 3"], @"value 3"); - - [_rocks deleteObjectForKey:@"key 2"]; - XCTAssertNil([_rocks objectForKey:@"key 2"]); - - NSError *error = nil; - BOOL ok = [_rocks deleteObjectForKey:@"key 2" error:&error]; - XCTAssertTrue(ok); - XCTAssertNil(error); -} - @end diff --git a/ObjectiveRocksTests/RocksDBWriteBatchTests.mm b/ObjectiveRocksTests/RocksDBWriteBatchTests.mm index 550ce96..1a3267e 100644 --- a/ObjectiveRocksTests/RocksDBWriteBatchTests.mm +++ b/ObjectiveRocksTests/RocksDBWriteBatchTests.mm @@ -6,41 +6,14 @@ // Copyright (c) 2014 BrainCookie. All rights reserved. // -#import -#import "ObjectiveRocks.h" +#import "RocksDBTests.h" -#define Data(x) [x dataUsingEncoding:NSUTF8StringEncoding] +@interface RocksDBWriteBatchTests : RocksDBTests -@interface RocksDBWriteBatchTests : XCTestCase -{ - NSString *_path; - RocksDB *_rocks; -} @end @implementation RocksDBWriteBatchTests -- (void)setUp -{ - [super setUp]; - - _path = [[NSBundle bundleForClass:[self class]] resourcePath]; - _path = [_path stringByAppendingPathComponent:@"ObjectiveRocks"]; - [self cleanupDB]; -} - -- (void)tearDown -{ - [_rocks close]; - [self cleanupDB]; - [super tearDown]; -} - -- (void)cleanupDB -{ - [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; -} - - (void)testWriteBatch_Perform { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { @@ -107,7 +80,7 @@ - (void)testWriteBatch_Apply options.createIfMissing = YES; }]; - RocksDBWriteBatch *batch = [RocksDBWriteBatch new]; + RocksDBWriteBatch *batch = [_rocks writeBatch]; [batch setData:Data(@"Value 1") forKey:Data(@"Key 1")]; [batch setData:Data(@"Value 2") forKey:Data(@"Key 2")]; @@ -129,7 +102,7 @@ - (void)testWriteBatch_Apply_DeleteOps [_rocks setData:Data(@"Value 1") forKey:Data(@"Key 1")]; - RocksDBWriteBatch *batch = [RocksDBWriteBatch new]; + RocksDBWriteBatch *batch = [_rocks writeBatch]; [batch deleteDataForKey:Data(@"Key 1")]; [batch setData:Data(@"Value 2") forKey:Data(@"Key 2")]; @@ -143,6 +116,35 @@ - (void)testWriteBatch_Apply_DeleteOps XCTAssertEqualObjects([_rocks dataForKey:Data(@"Key 4")], nil); } +- (void)testWriteBatch_Apply_MergeOps +{ + _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + options.mergeOperator = [RocksDBMergeOperator operatorWithName:@"merge" andBlock:^id(id key, id existingValue, id value) { + NSMutableString *result = [NSMutableString string]; + if (existingValue != nil) { + [result setString:Str(existingValue)]; + } + [result appendString:@","]; + [result appendString:Str(value)]; + return Data(result); + }]; + }]; + + [_rocks setData:Data(@"Value 1") forKey:Data(@"Key 1")]; + + RocksDBWriteBatch *batch = [_rocks writeBatch]; + + [batch deleteDataForKey:Data(@"Key 1")]; + [batch setData:Data(@"Value 2") forKey:Data(@"Key 2")]; + [batch setData:Data(@"Value 3") forKey:Data(@"Key 3")]; + [batch mergeData:Data(@"Value 2 New") forKey:Data(@"Key 2")]; + + [_rocks applyWriteBatch:batch withWriteOptions:nil]; + + XCTAssertEqualObjects([_rocks dataForKey:Data(@"Key 2")], Data(@"Value 2,Value 2 New")); +} + - (void)testWriteBatch_Apply_ClearOps { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { @@ -151,7 +153,7 @@ - (void)testWriteBatch_Apply_ClearOps [_rocks setData:Data(@"Value 1") forKey:Data(@"Key 1")]; - RocksDBWriteBatch *batch = [RocksDBWriteBatch new]; + RocksDBWriteBatch *batch = [_rocks writeBatch]; [batch deleteDataForKey:Data(@"Key 1")]; [batch setData:Data(@"Value 2") forKey:Data(@"Key 2")]; @@ -175,7 +177,7 @@ - (void)testWriteBatch_Count [_rocks setData:Data(@"Value 1") forKey:Data(@"Key 1")]; - RocksDBWriteBatch *batch = [RocksDBWriteBatch new]; + RocksDBWriteBatch *batch = [_rocks writeBatch]; [batch deleteDataForKey:Data(@"Key 1")]; @@ -204,19 +206,8 @@ - (void)testWriteBatch_Encoded { _rocks = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.keyEncoder = ^ NSData * (id key) { - return [key dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.keyDecoder = ^ NSString * (NSData *data) { - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; - options.valueEncoder = ^ NSData * (id key, id value) { - return [value dataUsingEncoding:NSUTF8StringEncoding]; - }; - options.valueDecoder = ^ NSString * (id key, NSData * data) { - if (data == nil) return nil; - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - }; + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSString; }]; [_rocks setObject:@"Value 1" forKey:@"Key 1"]; diff --git a/README.md b/README.md index 299fc45..413d1e5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,46 @@ # ObjectiveRocks -ObjectiveRocks is an Objective-C wrapper of Facebook's [RocksDB](https://github.com/facebook/rocksdb) - A Persistent Key-Value Store for Flash and RAM Storage. If you are interested in the internals of RocksDB, please refer to the [RocksDB Wiki](https://github.com/facebook/rocksdb/wiki). +ObjectiveRocks is an Objective-C wrapper of Facebook's [RocksDB](https://github.com/facebook/rocksdb) - A Persistent Key-Value Store for Flash and RAM Storage. -ObjectiveRocks provides an easy and straightforward interface to RocksDB and an Objective-C friendly API while abstracting away the underlying C++ implementation, so you don't have to deal with it. +#Quick Overview + +RocksDB is a key-value store, where the keys and values are arbitrarily-sized byte streams. The keys are ordered within the key value store according to a specified comparator function. RocksDB supports atomic reads and writes, snapshots, iteration and features many configuration options. + +ObjectiveRocks provides an easy interface to RocksDB and an Objective-C friendly API that abstracts away the underlying C++ implementation, so you don't have to deal with it. While there is no need to learn the details about RocksDB to use this wrapper, a basic understanding of the internals is recommended and would explain the design decisions behind the, somewhat opinionated, API. + +If you are interested in the internals of RocksDB, please refer to the [RocksDB Wiki](https://github.com/facebook/rocksdb/wiki). + +## The Minimum You Need to Know + +* Keys and values are byte arrays +* All data in the database is logically arranged in sorted order via a given `Comparator` +* RocksDB supports `Column Families` + * Column Families provide a way to logically partition the database, think collections in MongoDB + * Can be configured independently + * Can be added/dropped on the fly + * Key-value pairs are associated with exactly one `Column Family` in the database. + * If no column family is specified, the `default` column family is used +* RocksDB provides three basic operations: + * Get(key) + * Put(key, value) + * Delete(key) +* Applications can define a merge operation via a `Merge Operator` + * A merge is an atomic Read-Modify-Write +* RocksDB features an `Iterator` API to perform `RangeScan` on the database +* RocksDB provides a `Snapshot` API allows an application to create a point-in-time view of a database +* There are many configuration options: + * DBOptions: Controls the behavior of the database + * ColumnFamilyOptions: Controls the behavior of column families + * ReadOptions: apply to single read operations + * WriteOptions: apply to single write operations + +## RocksDB Lite + +ObjectiveRocks builds the RocksDB Lite version which is suitable for iOS. Later versions will include an OSX target that compiles and wraps the full feature set. + +## Installation + +Clone the repository and add ObjectiveRocks as a sub-project in Xcode. Notice that ObjectiveRocks depends on RocksDB and includes it as a Git submodule. # Usage @@ -29,6 +67,48 @@ RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db" andDBOptions:^(RocksDB The [configuration guide](#configuration) lists all currently available options along with their description. +## Open Column Families + +Once you have a `RocksDB` instance you can create and drop column families on the fly: + +```objective-c +RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db"]; + +RocksDBColumnFamily *columnFamily = [db createColumnFamilyWithName:@"new_column_family" andOptions:^(RocksDBColumnFamilyOptions *options) { + // Options for the new column family +}; +// Do stuff with columnFamily and close it when you're done +[columnFamily close]; + +// To drop it +[columnFamily drop]; +``` + +> Notice that the `RocksDBColumnFamily` is a subclass of `RocksDB` + +If the database already contains Column Families other than the default, then you need to specify all Column Families that currently exist in the database when opening it, including the default one. You specify the Column Families using a `RocksDBColumnFamiliesDescriptor` object: + +```objective-c +RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new]; + +[descriptor addColumnFamilyWithName:@"default" andOptions:^(RocksDBColumnFamilyOptions *options) { + // Options for the default column family +}]; +[descriptor addColumnFamilyWithName:@"stuff_column_family" andOptions:^(RocksDBColumnFamilyOptions *options) { + // Options for the stuff_column_family +}]; + +RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db" columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) { + // Database options here +}]; + +NSArray *columnFamilies = db.columnFamilies; +RocksDBColumnFamily *defaultColumnFamily = columnFamilies[0]; +RocksDBColumnFamily *stuffColumnFamily = columnFamilies[1]; +// At this point you can either use the db instance or +// the defaultColumnFamily instance to access the default column family +``` + ## Basic Operations The database provides three basic methods, `Put`, `Get`, and `Delete` to store/query data. Keys and values in RocksDB are arbitrary byte arrays: @@ -46,6 +126,8 @@ NSData *get = [db getDataForKey:key]; [db deleteDataForKey:key]; ``` +## Key-Value Encoding/Decoding + Since working with `NSData` objects is cumbersome, ObjectiveRocks offers an easy mechanism to encode/decode arbitrary objects to/from `NSData`. For example, if you have `NSString` keys and `NSDictionary` objects, you could define your own conversion blocks like this: @@ -80,6 +162,28 @@ NSMutableDictionary *dictionary = [db objectForKey:@"Hello"]; The `valueEncoder` and `valueDecoder` blocks take the `key` as first parameter to allow for multiplexing the conversion, i.e. storing different kinds of objects depending on the given `key`. +## Built-In Encoding Types + +ObjectiveRocks provides built-in support for some common key/value types. So the previous example can be written like this: + +```objective-c +RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) { + options.createIfMissing = YES; + + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSJSONSerializable; +}]; +``` + +The following types are currently supported: + +* `RocksDBTypeNSString` for NSString keys/values +* `RocksDBTypeNSJSONSerializable` for keys/values that can be serialized via `NSJSONSerialization`, i.e. all object that have the following properties: + * The top level object is an NSArray or NSDictionary. + * All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull. + * All dictionary keys are instances of NSString. + * Numbers are not NaN or infinity. + > All further examples will use the `id`-based API assuming that the key-value encoders are in place ### Read & Write Errors @@ -112,7 +216,7 @@ Each single read or write operation can be tuned via specific options: }]; ``` -Default options can also be set on a DB instance: +Default options can also be set on a `RocksDB` or `RocksDBColumnFamily` instance: ```objective-c [db setDefaultReadOptions:^(RocksDBReadOptions *readOptions) { @@ -147,16 +251,44 @@ You can atomically apply a set of updates to the database using a `WriteBatch`. ```objective-c [db setObject:@"Value 1" forKey:@"Key 1"]; -RocksDBWriteBatch *batch = [RocksDBWriteBatch new]; +RocksDBWriteBatch *batch = [db writeBatch]; [batch setObject:@"Value 2" forKey:@"Key 2"]; [batch setObject:@"Value 3" forKey:@"Key 3"]; [batch deleteObjectForKey:@"Key 1"]; ... [db applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) { - // write options here + // Write options here }]; ``` +The Write Batch object operates per default on the Column Family associated with the DB instance, which was used to create it. However, you can also specify the Column Family in a Write Batch associated with another one, in order to achieve an atomic write across multiple Column Families. In this case it doesn't matter on which instance you apply the batch: + +```objective-c +RocksDB *db = ...; +RocksDBColumnFamily *stuffColumnFamily = .../ + +// Write Batch for default column family +RocksDBWriteBatch *batch = [db writeBatch]; + +// Write Batch for stuffColumnFamily +RocksDBWriteBatch *cfBatch = [stuffColumnFamily writeBatch]; + +[batch setObject:@"Value 1" forKey:@"Key 1"]; +[batch setObject:@"Value 2" forKey:@"Key 2" inColumnFamily:stuffColumnFamily]; + +// You can apply the Write Batch object either on the DB instance +// or the stuffColumnFamily instance. +// The following two calls have the same effect: +/** + [db applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) { + // Write options here + }]; + [stuffColumnFamily applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) { + // Write options here + }]; +*/ +``` + ## Iteration Iteration is provided via the `RocksDBIterator` class. @@ -164,15 +296,20 @@ Iteration is provided via the `RocksDBIterator` class. You can either iterate manually: ```objective-c +RocksDB *db = ...; +RocksDBColumnFamily *stuffColumnFamily = .../ + RocksDBIterator *iterator = [db iterator]; -// alternatively, you can get an iterator with specific read options +RocksDBIterator *cfIterator = [stuffColumnFamily iterator]; + +// Alternatively, you can get an iterator with specific read options iterator = [db iteratorWithReadOptions:^(RocksDBReadOptions *readOptions) { - // read options here + // Read options here }]; for ([iterator seekToKey:@"start"]; [iterator isValid]; [iterator next]) { NSLog(@"%@: %@", [iterator key], [iterator value]); - // iterates all keys starting from key "start" + // Iterates all keys starting from key "start" } ``` @@ -197,7 +334,7 @@ RocksDBIterator *iterator = [db iterator]; // D, C, B, A }]; -// enumeration in a given key-range [start, end) +// Enumeration in a given key-range [start, end) RocksDBIteratorKeyRange range = RocksDBMakeKeyRange(@"C", @"A"); [db enumerateKeysInRange:range reverse:YES usingBlock:^(id key, BOOL *stop) { @@ -208,15 +345,15 @@ RocksDBIteratorKeyRange range = RocksDBMakeKeyRange(@"C", @"A"); ## Snapshot -A snapshot provide consistent read-only view over the entire state of the key-value store. Do not forget to close the snapshot when it's no longer needed. +A Snapshot provides consistent read-only view over the state of the key-value store. Do not forget to close the snapshot when it's no longer needed: ```objective-c [db setObject:@"Value 1" forKey:@"A"]; RocksDBSnapshot *snapshot = [db snapshot]; -// alternatively, you can get a snapshot with specific read options +// Alternatively, you can get a snapshot with specific read options snapshot = [db snapshotWithReadOptions:^(RocksDBReadOptions *readOptions) { - // read options here + // Read options here }]; [db deleteObjectForKey:@"A"]; @@ -232,47 +369,143 @@ NSString *value2 = [snapshot objectForKey:@"B"]; ## Keys Comparator -The keys are ordered within the key-value store according to a user-specified comparator function. The default ordering function for keys orders the bytes lexicographically. +The keys are ordered within the key-value store according to a specified comparator function. The default ordering function for keys orders the bytes lexicographically. -This behavior can be changed by supplying a custom comparator when opening a database using the `RocksDBComparator`. +This behavior can be changed by supplying a custom Comparator when opening a database using the `RocksDBComparator`. Say you have `NSString` keys and you want them to be ordered using a case-insensitive, localized, comparison: ```objective-c -RocksDBComparator *localizedKeys = [RocksDBComparator comaparatorWithName:@"LocalizedKeys" andBlock:^int (id key1, id key2) { +RocksDBComparator *localizedKeys = [[RocksDBComparator alloc] initWithName:@"LocalizedKeys" andBlock:^int (id key1, id key2) { return [key1 localizedCaseInsensitiveCompare:key2]; ]; RocksDB *db = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { - options.createIfMissing = YES; options.comparator = localizedKeys; }]; ``` > The comparator's name is attached to the database when it is created, and is checked on every subsequent database open. If the name changes, the `open` call will fail. Therefore, change the name if new key format and comparison function are incompatible with existing database, and it is ok to discard the contents of the existing database. +## Built-In Comparators + +ObjectiveRocks features some built-in comparators, which can be used like this: + +```objective-c +RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) { + options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorNumberAscending]; +}]; +``` + +* `RocksDBComparatorBytewiseAscending:` orders the keys lexicographically in ascending order. + * This is the default behavior if none is specified. +* `RocksDBComparatorBytewiseDescending` orders the keys lexicographically ins descending order. +* `RocksDBComparatorStringCompareAscending` orders NSString keys in ascending order via the `compare` selector. +* `RocksDBComparatorStringCompareDescending` orders NSString keys in descending order via the `compare` selector. +* `RocksDBComparatorNumberAscending` orders NSNumber keys in ascending order via the `compare` selector. +* `RocksDBComparatorNumberDescending` orders NSNumber keys in descending order via the `compare` selector. + ## Merge Operator -A Merge operators is an atomic Read-Modify-Write operation in RocksDB.For a detailed description visit the [Merge Operator](https://github.com/facebook/rocksdb/wiki/Merge-Operator) wiki page in the RocksDB project. +A Merge operators is an atomic Read-Modify-Write operation in RocksDB. For a detailed description visit the [Merge Operator](https://github.com/facebook/rocksdb/wiki/Merge-Operator) wiki page in the RocksDB project. + +> Analogous to the `comparator` a database created using one `merge operator` cannot be opened using another. + +### Associative Merge Operator + +You can use this Merge Operator when you have associative data: + +* Your merge operands are formatted the same as your Put values, AND +* It is okay to combine multiple operands into one (as long as they are in the same order) For example we can use a merge operator to append entries to an existing array, instead of reading it completely, updating it and writing it back: ```objective-c RocksDBMergeOperator *arrayAppend = [RocksDBMergeOperator operatorWithName:@"ArrayAppend" andBlock:^id (id key, id existingValue, id mergeValue) { - NSMutableArray *result = [NSMutableArray array]; - if (existingValue != nil) { - result = existingValue; + if (existingValue == nil) { + return mergeValue; + } else { + [existingValue addObjectsFromArray:mergeValue]; + return existingValue; } - [result addObjectsFromArray:mergeValue]; - return result; }]; +RocksDB *db = [[RocksDB alloc] initWithPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) { + options.mergeOperator = arrayAppend; + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSJSONSerializable; +}]; + +NSMutableArray *letters = [NSMutableArray arrayWithObjects:@"A", @"B", nil]; +[db setObject:letters forKey:@"Key"]; +[db mergeObject:@[@"C", @"D"] forKey:@"Key"]; +[db mergeObject:@[@"E"] forKey:@"Key"]; + +NSMutableArray *merged = [db objectForKey:@"Key"]; +// merged = @[@"A", @"B", @"C", @"D", @"E"]; +``` + +### Generic Merge Operator + +If either of the two associativity constraints do not hold, then use the Generic Merge Operator. + +The Generic Merge Operator has two methods, PartialMerge, FullMerge: + +* PartialMerge: used to combine two-merge operands (if possible). If the client-specified operator can logically handle "combining" two merge-operands into a single operand, the semantics for doing so should be provided in this method, which should then return a non-nil object. If `nil` is returned, then it means that the two merge-operands couldn't be combined into one. + +* FullMerge: this function is given an *existingValue* and a list of operands that have been stacked. The client-specified MergeOperator should then apply the operands one-by-one and return the resulting object. If `nil` is returned, then this indicates a failure, i.e. corrupted data, errors ...etc. + +ObjectiveRocks has a new `mergeOperation` method for use with a generic Merge Operator. This contrived example shows how a generic merge operator can be used with client-defined merge operations: + +```objective-c +RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator" + partialMergeBlock:^id(id key, NSString *leftOperand, NSString *rightOperand) { + NSString *left = [leftOperand componentsSeparatedByString:@":"][0]; + NSString *right = [rightOperand componentsSeparatedByString:@":"][0]; + if ([left isEqualToString:right]) { + return rightOperand; + } + return nil; + } fullMergeBlock:^id(id key, id *existing, NSArray *operands) { + for (NSString *operand in operands) { + NSArray *components = [operand componentsSeparatedByString:@":"]; + NSString *action = components[1]; + if ([action isEqualToString:@"DELETE"]) { + [existing removeObjectForKey:components[0]]; + } else { + existing[comp[0]] = components[2]; + } + } + return existing; + } +]; + RocksDB *db = [[RocksDB alloc] initWithPath:_path andDBOptions:^(RocksDBOptions *options) { options.createIfMissing = YES; - options.mergeOperator = arrayAppend; + options.mergeOperator = mergeOp; + + options.keyType = RocksDBTypeNSString; + options.valueType = RocksDBTypeNSJSONSerializable; }]; + +NSDictionary *object = @{@"Key 1" : @"Value 1", + @"Key 2" : @"Value 2", + @"Key 3" : @"Value 3"}; + +[db setObject:object forKey:@"Dict Key"]; + +[db mergeOperation:@"Key 1:UPDATE:Value X" forKey:@"Dict Key"]; +[db mergeOperation:@"Key 4:INSERT:Value 4" forKey:@"Dict Key"]; +[db mergeOperation:@"Key 2:DELETE" forKey:@"Dict Key"]; +[db mergeOperation:@"Key 1:UPDATE:Value 1 New" forKey:@"Dict Key"]; + +id result = [db objectForKey:@"Dict Key"]; +/** +result = @{@"Key 1" : @"Value 1 New", + @"Key 3" : @"Value 3", + @"Key 4" : @"Value 4"}; +*/ ``` -> Analogous to the `comparator` a database created using one `merge operator` cannot be opened using another. # Configuration diff --git a/rocksdb b/rocksdb index 9cda7cb..a801c1f 160000 --- a/rocksdb +++ b/rocksdb @@ -1 +1 @@ -Subproject commit 9cda7cb77b0c7208a63579c7e79252f23db92f67 +Subproject commit a801c1fb099167cf48a714483163061062e3dcb7