├── .gitignore ├── .gitmodules ├── Classes ├── Common.h ├── LDBSnapshot.h ├── LDBSnapshot.mm ├── LDBWriteBatch.h ├── LDBWriteBatch.mm ├── LevelDB.h └── LevelDB.mm ├── Configs └── Objective-LevelDB.xcconfig ├── LICENSE ├── Libraries └── leveldb │ ├── include │ └── leveldb │ │ ├── c.h │ │ ├── cache.h │ │ ├── comparator.h │ │ ├── db.h │ │ ├── dumpfile.h │ │ ├── env.h │ │ ├── filter_policy.h │ │ ├── iterator.h │ │ ├── options.h │ │ ├── slice.h │ │ ├── status.h │ │ ├── table.h │ │ ├── table_builder.h │ │ └── write_batch.h │ └── libleveldb.a ├── Objective-LevelDB+Uber.podspec ├── Objective-LevelDB.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Objective-LevelDB.xcworkspace └── contents.xcworkspacedata ├── Readme.md ├── Tests ├── BaseTestClass.h ├── BaseTestClass.m ├── Dummy.cpp ├── Dummy.h ├── MainTests.m ├── OS X Tests │ ├── OS X Tests-Info.plist │ ├── OS X Tests-Prefix.pch │ └── en.lproj │ │ └── InfoPlist.strings ├── Objective-LevelDB OS X Tests.xcodeproj │ └── project.pbxproj ├── Objective-LevelDB iOS Tests.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Podfile ├── SnapshotsTests.m ├── WritebatchTests.m └── iOS Tests │ ├── en.lproj │ └── InfoPlist.strings │ ├── iOS Tests-Info.plist │ └── iOS Tests-Prefix.pch └── setup-test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | Tests/Pods 4 | Tests/*.lock 5 | 6 | # Xcode 7 | build/* 8 | */build/* 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | .idea/ 22 | *.hmap 23 | *.xccheckout 24 | 25 | #CocoaPods 26 | Pods 27 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Vendor/google-leveldb"] 2 | path = Vendor/google-leveldb 3 | url = https://github.com/google/leveldb.git 4 | -------------------------------------------------------------------------------- /Classes/Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Header.h 3 | // Pods 4 | // 5 | // Created by Mathieu D'Amours on 5/8/13. 6 | // 7 | // 8 | 9 | #pragma once 10 | 11 | 12 | #define AssertKeyType(_key_)\ 13 | NSParameterAssert([_key_ isKindOfClass:[NSString class]] || [_key_ isKindOfClass:[NSData class]]) 14 | 15 | #define SliceFromString(_string_) leveldb::Slice((char *)[_string_ UTF8String], [_string_ lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) 16 | #define StringFromSlice(_slice_) [[[NSString alloc] initWithBytes:_slice_.data() length:_slice_.size() encoding:NSUTF8StringEncoding] autorelease] 17 | 18 | #define SliceFromData(_data_) leveldb::Slice((char *)[_data_ bytes], [_data_ length]) 19 | #define DataFromSlice(_slice_) [NSData dataWithBytes:_slice_.data() length:_slice_.size()] 20 | 21 | #define DecodeFromSlice(_slice_, _key_, _d) _d(_key_, DataFromSlice(_slice_)) 22 | #define EncodeToSlice(_object_, _key_, _e) SliceFromData(_e(_key_, _object_)) 23 | 24 | #define KeyFromStringOrData(_key_) ([_key_ isKindOfClass:[NSString class]]) ? SliceFromString(_key_) \ 25 | : SliceFromData(_key_) 26 | 27 | #define GenericKeyFromSlice(_slice_) (LevelDBKey) { .data = _slice_.data(), .length = _slice_.size() } 28 | #define GenericKeyFromNSDataOrString(_obj_) ([_obj_ isKindOfClass:[NSString class]]) ? \ 29 | (LevelDBKey) { \ 30 | .data = [_obj_ cStringUsingEncoding:NSUTF8StringEncoding], \ 31 | .length = [_obj_ lengthOfBytesUsingEncoding:NSUTF8StringEncoding] \ 32 | } \ 33 | : (LevelDBKey) { \ 34 | .data = [_obj_ bytes], \ 35 | .length = [_obj_ length] \ 36 | } 37 | -------------------------------------------------------------------------------- /Classes/LDBSnapshot.h: -------------------------------------------------------------------------------- 1 | // 2 | // LDBSnapshot.h 3 | // 4 | // Copyright 2013 Storm Labs. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import 9 | #import "LevelDB.h" 10 | 11 | @class LevelDB; 12 | 13 | @interface LDBSnapshot : NSObject 14 | 15 | @property (nonatomic, readonly, assign) LevelDB * db; 16 | 17 | /** 18 | Return the value associated with a key 19 | 20 | @param key The key to retrieve from the database 21 | */ 22 | - (id) objectForKey:(id)key; 23 | 24 | /** 25 | Same as `[self objectForKey:]` 26 | */ 27 | - (id) objectForKeyedSubscript:(id)key; 28 | 29 | /** 30 | Same as `[self objectForKey:]` 31 | */ 32 | - (id) valueForKey:(NSString *)key; 33 | 34 | /** 35 | Return an array containing the values associated with the provided list of keys. 36 | 37 | For keys that can't be found in the database, the `marker` value is used in place. 38 | 39 | @warning marker should not be `nil` 40 | 41 | @param keys The list of keys to fetch from the database 42 | @param marker The value to associate to missing keys 43 | */ 44 | - (id) objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker; 45 | 46 | /** 47 | Return a boolean value indicating whether or not the key exists in the database 48 | 49 | @param key The key to check for existence 50 | */ 51 | - (BOOL) objectExistsForKey:(id)key; 52 | 53 | 54 | /** 55 | Return an array containing all the keys of the database 56 | 57 | @warning This shouldn't be used with very large databases, since every key will be stored in memory 58 | */ 59 | - (NSArray *)allKeys; 60 | 61 | /** 62 | Return an array of key for which the value match the given predicate 63 | 64 | @param predicate A `NSPredicate` instance tested against the database's values to retrieve the corresponding keys 65 | */ 66 | - (NSArray *)keysByFilteringWithPredicate:(NSPredicate *)predicate; 67 | 68 | /** 69 | Return a dictionary with all key-value pairs, where values match the given predicate 70 | 71 | @param predicate A `NSPredicate` instance tested against the database's values to retrieve the corresponding key-value pairs 72 | */ 73 | - (NSDictionary *)dictionaryByFilteringWithPredicate:(NSPredicate *)predicate; 74 | 75 | /** 76 | Enumerate over the keys in the database, in order. 77 | 78 | Same as `[self enumerateKeysBackward:FALSE startingAtKey:nil filteredByPredicate:nil andPrefix:nil usingBlock:block]` 79 | 80 | @param block The enumeration block used when iterating over all the keys. 81 | */ 82 | - (void) enumerateKeysUsingBlock:(LevelDBKeyBlock)block; 83 | 84 | /** 85 | Enumerate over the keys in the database, in direct or backward order, with some options to control the keys iterated over 86 | 87 | @param backward A boolean value indicating whether the enumeration happens in direct or backward order 88 | @param key (optional) The key at which to start iteration. If the key isn't present in the database, the enumeration starts at the key immediately greater than the provided one. The key can be a `NSData` or `NSString` 89 | @param predicate A `NSPredicate` instance tested against the values. The iteration block will only be called for keys associated to values matching the predicate. If `nil`, this is ignored. 90 | @param prefix A `NSString` or `NSData` prefix used to filter the keys. If provided, only the keys prefixed with this value will be iterated over. 91 | @param block The enumeration block used when iterating over all the keys. It takes two arguments: the first is a pointer to a `LevelDBKey` struct. You can convert this to a `NSString` or `NSData` instance, using `NSDataFromLevelDBKey(LevelDBKey *key)` and `NSStringFromLevelDBKey(LevelDBKey *key)` respectively. The second arguments to the block is a `BOOL *` that can be used to stop enumeration at any time (e.g. `*stop = TRUE;`). 92 | */ 93 | - (void) enumerateKeysBackward:(BOOL)backward 94 | startingAtKey:(id)key 95 | filteredByPredicate:(NSPredicate *)predicate 96 | andPrefix:(id)prefix 97 | usingBlock:(LevelDBKeyBlock)block; 98 | 99 | /** 100 | Enumerate over the key value pairs in the database, in order. 101 | 102 | Same as `[self enumerateKeysAndObjectsBackward:FALSE startingAtKey:nil filteredByPredicate:nil andPrefix:nil usingBlock:block]` 103 | 104 | @param block The enumeration block used when iterating over all the key value pairs. 105 | */ 106 | - (void) enumerateKeysAndObjectsUsingBlock:(LevelDBKeyValueBlock)block; 107 | 108 | /** 109 | Enumerate over the keys in the database, in direct or backward order, with some options to control the keys iterated over 110 | 111 | @param backward A boolean value indicating whether the enumeration happens in direct or backward order 112 | @param key (optional) The key at which to start iteration. If the key isn't present in the database, the enumeration starts at the key immediately greater than the provided one. The key can be a `NSData` or `NSString` 113 | @param predicate A `NSPredicate` instance tested against the values. The iteration block will only be called for keys associated to values matching the predicate. If `nil`, this is ignored. 114 | @param prefix A `NSString` or `NSData` prefix used to filter the keys. If provided, only the keys prefixed with this value will be iterated over. 115 | @param block The enumeration block used when iterating over all the keys. It takes three arguments: the first is a pointer to a `LevelDBKey` struct. You can convert this to a `NSString` or `NSData` instance, using `NSDataFromLevelDBKey(LevelDBKey *key)` and `NSStringFromLevelDBKey(LevelDBKey *key)` respectively. The second argument is the value associated with the key. The third arguments to the block is a `BOOL *` that can be used to stop enumeration at any time (e.g. `*stop = TRUE;`). 116 | */ 117 | - (void) enumerateKeysAndObjectsBackward:(BOOL)backward 118 | lazily:(BOOL)lazily 119 | startingAtKey:(id)key 120 | filteredByPredicate:(NSPredicate *)predicate 121 | andPrefix:(id)prefix 122 | usingBlock:(id)block; 123 | 124 | /** 125 | Close the snapshot. 126 | 127 | @warning The instance cannot be used to perform any query after it has been closed. 128 | */ 129 | - (void) close; 130 | @end 131 | -------------------------------------------------------------------------------- /Classes/LDBSnapshot.mm: -------------------------------------------------------------------------------- 1 | // 2 | // LDBSnapshot.mm 3 | // 4 | // Copyright 2013 Storm Labs. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import "LDBSnapshot.h" 9 | #import 10 | 11 | @interface LevelDB () 12 | 13 | - (leveldb::DB *)db; 14 | 15 | - (void) enumerateKeysBackward:(BOOL)backward 16 | startingAtKey:(id)key 17 | filteredByPredicate:(NSPredicate *)predicate 18 | andPrefix:(id)prefix 19 | withSnapshot:(LDBSnapshot *)snapshot 20 | usingBlock:(LevelDBKeyBlock)block; 21 | 22 | - (void) enumerateKeysAndObjectsBackward:(BOOL)backward 23 | lazily:(BOOL)lazily 24 | startingAtKey:(id)key 25 | filteredByPredicate:(NSPredicate *)predicate 26 | andPrefix:(id)prefix 27 | withSnapshot:(LDBSnapshot *)snapshot 28 | usingBlock:(id)block; 29 | 30 | - (id) objectForKey:(id)key 31 | withSnapshot:(LDBSnapshot *)snapshot; 32 | 33 | - (BOOL) objectExistsForKey:(id)key 34 | withSnapshot:(LDBSnapshot *)snapshot; 35 | 36 | @end 37 | 38 | @interface LDBSnapshot () { 39 | const leveldb::Snapshot * _snapshot; 40 | } 41 | 42 | @property (readonly, getter = getSnapshot) const leveldb::Snapshot * snapshot; 43 | - (const leveldb::Snapshot *) getSnapshot; 44 | 45 | @end 46 | 47 | @implementation LDBSnapshot 48 | 49 | + (LDBSnapshot *) snapshotFromDB:(LevelDB *)database { 50 | LDBSnapshot *snapshot = [[[LDBSnapshot alloc] init] autorelease]; 51 | snapshot->_snapshot = [database db]->GetSnapshot(); 52 | snapshot->_db = database; 53 | return snapshot; 54 | } 55 | 56 | - (const leveldb::Snapshot *) getSnapshot { 57 | return _snapshot; 58 | } 59 | 60 | - (id) objectForKey:(id)key { 61 | return [_db objectForKey:key withSnapshot:self]; 62 | } 63 | - (id)objectForKeyedSubscript:(id)key { 64 | return [_db objectForKey:key withSnapshot:self]; 65 | } 66 | - (BOOL) objectExistsForKey:(id)key { 67 | return [_db objectExistsForKey:key withSnapshot:self]; 68 | } 69 | - (id) objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker { 70 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:keys.count]; 71 | [keys enumerateObjectsUsingBlock:^(id objId, NSUInteger idx, BOOL *stop) { 72 | id object = [self objectForKey:objId]; 73 | if (object == nil) object = marker; 74 | result[idx] = object; 75 | }]; 76 | return [NSArray arrayWithArray:result]; 77 | } 78 | - (id) valueForKey:(NSString *)key { 79 | if ([key characterAtIndex:0] == '@') { 80 | return [super valueForKey:[key stringByReplacingCharactersInRange:(NSRange){0, 1} 81 | withString:@""]]; 82 | } else 83 | return [_db objectForKey:key withSnapshot:self]; 84 | } 85 | 86 | - (NSArray *)allKeys { 87 | NSMutableArray *keys = [[[NSMutableArray alloc] init] autorelease]; 88 | [self enumerateKeysUsingBlock:^(LevelDBKey *key, BOOL *stop) { 89 | [keys addObject:NSDataFromLevelDBKey(key)]; 90 | }]; 91 | return [NSArray arrayWithArray:keys]; 92 | } 93 | - (NSArray *)keysByFilteringWithPredicate:(NSPredicate *)predicate { 94 | NSMutableArray *keys = [[[NSMutableArray alloc] init] autorelease]; 95 | [self enumerateKeysBackward:NO 96 | startingAtKey:nil 97 | filteredByPredicate:predicate 98 | andPrefix:nil 99 | usingBlock:^(LevelDBKey *key, BOOL *stop) { 100 | [keys addObject:NSDataFromLevelDBKey(key)]; 101 | }]; 102 | 103 | return [NSArray arrayWithArray:keys]; 104 | } 105 | 106 | - (NSDictionary *)dictionaryByFilteringWithPredicate:(NSPredicate *)predicate { 107 | NSMutableDictionary *results = [NSMutableDictionary dictionary]; 108 | [self enumerateKeysAndObjectsBackward:NO 109 | lazily:NO 110 | startingAtKey:nil 111 | filteredByPredicate:predicate 112 | andPrefix:nil 113 | usingBlock:^(LevelDBKey *key, id obj, BOOL *stop) { 114 | [results setObject:obj forKey:NSDataFromLevelDBKey(key)]; 115 | }]; 116 | 117 | return [NSDictionary dictionaryWithDictionary:results]; 118 | } 119 | 120 | - (void)enumerateKeysUsingBlock:(LevelDBKeyBlock)block { 121 | [_db enumerateKeysBackward:NO 122 | startingAtKey:nil 123 | filteredByPredicate:nil 124 | andPrefix:nil 125 | withSnapshot:self 126 | usingBlock:block]; 127 | } 128 | - (void)enumerateKeysBackward:(BOOL)backward 129 | startingAtKey:(id)key 130 | filteredByPredicate:(NSPredicate *)predicate 131 | andPrefix:(id)prefix 132 | usingBlock:(LevelDBKeyBlock)block { 133 | [_db enumerateKeysBackward:backward 134 | startingAtKey:key 135 | filteredByPredicate:predicate 136 | andPrefix:prefix 137 | withSnapshot:self 138 | usingBlock:block]; 139 | } 140 | 141 | - (void)enumerateKeysAndObjectsUsingBlock:(LevelDBKeyValueBlock)block { 142 | [_db enumerateKeysAndObjectsBackward:NO 143 | lazily:NO 144 | startingAtKey:nil 145 | filteredByPredicate:nil 146 | andPrefix:nil 147 | withSnapshot:self 148 | usingBlock:block]; 149 | } 150 | - (void)enumerateKeysAndObjectsBackward:(BOOL)backward 151 | lazily:(BOOL)lazily 152 | startingAtKey:(id)key 153 | filteredByPredicate:(NSPredicate *)predicate 154 | andPrefix:(id)prefix 155 | usingBlock:(id)block { 156 | [_db enumerateKeysAndObjectsBackward:backward 157 | lazily:lazily 158 | startingAtKey:key 159 | filteredByPredicate:predicate 160 | andPrefix:prefix 161 | withSnapshot:self 162 | usingBlock:block]; 163 | } 164 | 165 | - (void) close { 166 | if (_snapshot && _db && ![_db closed]) { 167 | [_db db]->ReleaseSnapshot(_snapshot); 168 | _snapshot = nil; 169 | } 170 | } 171 | - (void) dealloc { 172 | [self close]; 173 | [super dealloc]; 174 | } 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /Classes/LDBWriteBatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // WriteBatch.h 3 | // 4 | // Copyright 2013 Storm Labs. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import 9 | 10 | #import "LevelDB.h" 11 | 12 | @interface LDBWritebatch : NSObject 13 | 14 | @property (nonatomic, assign) id db; 15 | 16 | /** 17 | Remove a key (and its associated value) from the database 18 | 19 | @param key The key to remove from the database 20 | */ 21 | - (void) removeObjectForKey:(id)key; 22 | 23 | /** 24 | Remove a set of keys (and their associated values) from the database 25 | 26 | @param keyArray An array of keys to remove from the database 27 | */ 28 | - (void) removeObjectsForKeys:(NSArray *)keyArray; 29 | 30 | /** 31 | Remove all objects from the database 32 | */ 33 | - (void) removeAllObjects; 34 | 35 | /** 36 | Set the raw data associated with a key in the database 37 | 38 | The instance's encoder block will *not* be used to produce a NSData instance from the provided value. 39 | 40 | @param value The raw data value to put in the database 41 | @param key The key at which the value can be found 42 | */ 43 | - (void) setData:(NSData *)data forKey:(id)key; 44 | 45 | /** 46 | Set the value associated with a key in the database 47 | 48 | The instance's encoder block will be used to produce a NSData instance from the provided value. 49 | 50 | @param value The value to put in the database 51 | @param key The key at which the value can be found 52 | */ 53 | - (void) setObject:(id)value forKey:(id)key; 54 | 55 | /** 56 | Same as `[self setObject:forKey:]` 57 | */ 58 | - (void) setValue:(id)value forKey:(NSString *)key; 59 | 60 | /** 61 | Take all key-value pairs in the provided dictionary and insert them in the database 62 | 63 | @param dictionary A dictionary from which key-value pairs will be inserted 64 | */ 65 | - (void) addEntriesFromDictionary:(NSDictionary *)dictionary; 66 | 67 | /** 68 | Apply the write batch to the underlying database 69 | */ 70 | - (void) apply; 71 | 72 | @end -------------------------------------------------------------------------------- /Classes/LDBWriteBatch.mm: -------------------------------------------------------------------------------- 1 | // 2 | // WriteBatch.mm 3 | // 4 | // Copyright 2013 Storm Labs. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | #import "LDBWriteBatch.h" 12 | #include "Common.h" 13 | 14 | @interface LDBWritebatch () { 15 | leveldb::WriteBatch _writeBatch; 16 | id _db; 17 | } 18 | 19 | @property (readonly) leveldb::WriteBatch writeBatch; 20 | 21 | @end 22 | 23 | @implementation LDBWritebatch { 24 | dispatch_queue_t _serial_queue; 25 | } 26 | 27 | @synthesize writeBatch = _writeBatch; 28 | @synthesize db = _db; 29 | 30 | + (instancetype) writeBatchFromDB:(id)db { 31 | id wb = [[[self alloc] init] autorelease]; 32 | ((LDBWritebatch *)wb)->_db = [db retain]; 33 | return wb; 34 | } 35 | 36 | - (instancetype) init { 37 | self = [super init]; 38 | if (self) { 39 | _serial_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 40 | } 41 | return self; 42 | } 43 | - (void)dealloc { 44 | if (_serial_queue) { 45 | dispatch_release(_serial_queue); 46 | _serial_queue = nil; 47 | } 48 | if (_db) { 49 | [_db release]; 50 | _db = nil; 51 | } 52 | [super dealloc]; 53 | } 54 | 55 | - (void) removeObjectForKey:(id)key { 56 | AssertKeyType(key); 57 | leveldb::Slice k = KeyFromStringOrData(key); 58 | dispatch_sync(_serial_queue, ^{ 59 | _writeBatch.Delete(k); 60 | }); 61 | } 62 | - (void) removeObjectsForKeys:(NSArray *)keyArray { 63 | [keyArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 64 | [self removeObjectForKey:obj]; 65 | }]; 66 | } 67 | - (void) removeAllObjects { 68 | [_db enumerateKeysUsingBlock:^(LevelDBKey *key, BOOL *stop) { 69 | [self removeObjectForKey:NSDataFromLevelDBKey(key)]; 70 | }]; 71 | } 72 | 73 | - (void) setData:(NSData *)data forKey:(id)key { 74 | AssertKeyType(key); 75 | dispatch_sync(_serial_queue, ^{ 76 | leveldb::Slice lkey = KeyFromStringOrData(key); 77 | _writeBatch.Put(lkey, SliceFromData(data)); 78 | }); 79 | } 80 | - (void) setObject:(id)value forKey:(id)key { 81 | AssertKeyType(key); 82 | dispatch_sync(_serial_queue, ^{ 83 | leveldb::Slice k = KeyFromStringOrData(key); 84 | LevelDBKey lkey = GenericKeyFromSlice(k); 85 | 86 | NSData *data = ((LevelDB *)_db).encoder(&lkey, value); 87 | leveldb::Slice v = SliceFromData(data); 88 | 89 | _writeBatch.Put(k, v); 90 | }); 91 | } 92 | - (void) setValue:(id)value forKey:(NSString *)key { 93 | [self setObject:value forKey:key]; 94 | } 95 | - (void) addEntriesFromDictionary:(NSDictionary *)dictionary { 96 | [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 97 | [self setObject:obj forKey:key]; 98 | }]; 99 | } 100 | 101 | - (void) apply { 102 | [_db applyWritebatch:self]; 103 | } 104 | 105 | @end -------------------------------------------------------------------------------- /Classes/LevelDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // LevelDB.h 3 | // 4 | // Copyright 2011 Pave Labs. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import 9 | 10 | @class LDBSnapshot; 11 | @class LDBWritebatch; 12 | 13 | typedef struct LevelDBOptions { 14 | BOOL createIfMissing ; 15 | BOOL createIntermediateDirectories; 16 | BOOL errorIfExists ; 17 | BOOL paranoidCheck ; 18 | BOOL compression ; 19 | int filterPolicy ; 20 | size_t cacheSize; 21 | } LevelDBOptions; 22 | 23 | typedef struct { 24 | const char * data; 25 | NSUInteger length; 26 | } LevelDBKey; 27 | 28 | typedef NSData * (^LevelDBEncoderBlock) (LevelDBKey * key, id object); 29 | typedef id (^LevelDBDecoderBlock) (LevelDBKey * key, id data); 30 | 31 | typedef void (^LevelDBKeyBlock) (LevelDBKey * key, BOOL *stop); 32 | typedef void (^LevelDBKeyValueBlock)(LevelDBKey * key, id value, BOOL *stop); 33 | 34 | typedef id (^LevelDBValueGetterBlock) (void); 35 | typedef void (^LevelDBLazyKeyValueBlock) (LevelDBKey * key, LevelDBValueGetterBlock lazyValue, BOOL *stop); 36 | 37 | FOUNDATION_EXPORT NSString * const kLevelDBChangeType; 38 | FOUNDATION_EXPORT NSString * const kLevelDBChangeTypePut; 39 | FOUNDATION_EXPORT NSString * const kLevelDBChangeTypeDelete; 40 | FOUNDATION_EXPORT NSString * const kLevelDBChangeValue; 41 | FOUNDATION_EXPORT NSString * const kLevelDBChangeKey; 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | NSString * NSStringFromLevelDBKey(LevelDBKey * key); 48 | NSData * NSDataFromLevelDBKey (LevelDBKey * key); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | @interface LevelDB : NSObject 55 | 56 | ///------------------------------------------------------------------------ 57 | /// @name A LevelDB object, used to query to the database instance on disk 58 | ///------------------------------------------------------------------------ 59 | 60 | /** 61 | The path of the database on disk 62 | */ 63 | @property (nonatomic, retain) NSString *path; 64 | 65 | /** 66 | The name of the database. 67 | */ 68 | @property (nonatomic, retain) NSString *name; 69 | 70 | /** 71 | A boolean value indicating whether write operations should be synchronous (flush to disk before returning). 72 | */ 73 | @property (nonatomic) BOOL safe; 74 | 75 | /** 76 | A boolean value indicating whether read operations should try to use the configured cache (defaults to true). 77 | */ 78 | @property (nonatomic) BOOL useCache; 79 | 80 | /** 81 | A boolean readonly value indicating whether the database is closed or not. 82 | */ 83 | @property (readonly) BOOL closed; 84 | 85 | /** 86 | The data encoding block. 87 | */ 88 | @property (nonatomic, copy) LevelDBEncoderBlock encoder; 89 | 90 | /** 91 | The data decoding block. 92 | */ 93 | @property (nonatomic, copy) LevelDBDecoderBlock decoder; 94 | 95 | /** 96 | A class method that returns a LevelDBOptions struct, which can be modified to finetune leveldb 97 | */ 98 | + (LevelDBOptions) makeOptions; 99 | 100 | /** 101 | A class method that returns an autoreleased instance of LevelDB with the given name, inside the Library folder 102 | 103 | @param name The database's filename 104 | */ 105 | + (id) databaseInLibraryWithName:(NSString *)name; 106 | 107 | /** 108 | A class method that returns an autoreleased instance of LevelDB with the given name and options, inside the Library folder 109 | 110 | @param name The database's filename 111 | @param opts A LevelDBOptions struct with options for fine tuning leveldb 112 | */ 113 | + (id) databaseInLibraryWithName:(NSString *)name andOptions:(LevelDBOptions)opts; 114 | 115 | /** 116 | Initialize a leveldb instance 117 | 118 | @param path The parent directory of the database file on disk 119 | @param name the filename of the database file on disk 120 | */ 121 | - (id) initWithPath:(NSString *)path andName:(NSString *)name; 122 | 123 | /** 124 | Initialize a leveldb instance 125 | 126 | @param path The parent directory of the database file on disk 127 | @param name the filename of the database file on disk 128 | @param opts A LevelDBOptions struct with options for fine tuning leveldb 129 | */ 130 | - (id) initWithPath:(NSString *)path name:(NSString *)name andOptions:(LevelDBOptions)opts; 131 | 132 | 133 | /** 134 | Delete the database file on disk 135 | */ 136 | - (void) deleteDatabaseFromDisk; 137 | 138 | /** 139 | Close the database. 140 | 141 | @warning The instance cannot be used to perform any query after it has been closed. 142 | */ 143 | - (void) close; 144 | 145 | #pragma mark - Setters 146 | 147 | /** 148 | Set the value associated with a key in the database 149 | 150 | The instance's encoder block will be used to produce a NSData instance from the provided value. 151 | 152 | @param value The value to put in the database 153 | @param key The key at which the value can be found 154 | */ 155 | - (void) setObject:(id)value forKey:(id)key; 156 | 157 | /** 158 | Same as `[self setObject:forKey:]` 159 | */ 160 | - (void) setObject:(id)value forKeyedSubscript:(id)key; 161 | 162 | /** 163 | Same as `[self setObject:forKey:]` 164 | */ 165 | - (void) setValue:(id)value forKey:(NSString *)key ; 166 | 167 | /** 168 | Take all key-value pairs in the provided dictionary and insert them in the database 169 | 170 | @param dictionary A dictionary from which key-value pairs will be inserted 171 | */ 172 | - (void) addEntriesFromDictionary:(NSDictionary *)dictionary; 173 | 174 | #pragma mark - Write batches 175 | 176 | /** 177 | Return an retained LDBWritebatch instance for this database 178 | */ 179 | - (LDBWritebatch *) newWritebatch; 180 | 181 | /** 182 | Apply the operations from a writebatch into the current database 183 | */ 184 | - (void) applyWritebatch:(LDBWritebatch *)writeBatch; 185 | 186 | #pragma mark - Getters 187 | 188 | /** 189 | Return the value associated with a key 190 | 191 | @param key The key to retrieve from the database 192 | */ 193 | - (id) objectForKey:(id)key; 194 | 195 | /** 196 | Same as `[self objectForKey:]` 197 | */ 198 | - (id) objectForKeyedSubscript:(id)key; 199 | 200 | /** 201 | Same as `[self objectForKey:]` 202 | */ 203 | - (id) valueForKey:(NSString *)key; 204 | 205 | /** 206 | Return an array containing the values associated with the provided list of keys. 207 | 208 | For keys that can't be found in the database, the `marker` value is used in place. 209 | 210 | @warning marker should not be `nil` 211 | 212 | @param keys The list of keys to fetch from the database 213 | @param marker The value to associate to missing keys 214 | */ 215 | - (id) objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker; 216 | 217 | /** 218 | Return a boolean value indicating whether or not the key exists in the database 219 | 220 | @param key The key to check for existence 221 | */ 222 | - (BOOL) objectExistsForKey:(id)key; 223 | 224 | #pragma mark - Removers 225 | 226 | /** 227 | Remove a key (and its associated value) from the database 228 | 229 | @param key The key to remove from the database 230 | */ 231 | - (void) removeObjectForKey:(id)key; 232 | 233 | /** 234 | Remove a set of keys (and their associated values) from the database 235 | 236 | @param keyArray An array of keys to remove from the database 237 | */ 238 | - (void) removeObjectsForKeys:(NSArray *)keyArray; 239 | 240 | /** 241 | Remove all objects from the database 242 | */ 243 | - (void) removeAllObjects; 244 | 245 | /** 246 | Remove all objects prefixed with a given value (`NSString` or `NSData`) 247 | 248 | @param prefix The key prefix used to remove all matching keys (of type `NSString` or `NSData`) 249 | */ 250 | - (void) removeAllObjectsWithPrefix:(id)prefix; 251 | 252 | #pragma mark - Selection 253 | 254 | /** 255 | Return an array containing all the keys of the database 256 | 257 | @warning This shouldn't be used with very large databases, since every key will be stored in memory 258 | */ 259 | - (NSArray *) allKeys; 260 | 261 | /** 262 | Return an array of key for which the value match the given predicate 263 | 264 | @param predicate A `NSPredicate` instance tested against the database's values to retrieve the corresponding keys 265 | */ 266 | - (NSArray *) keysByFilteringWithPredicate:(NSPredicate *)predicate; 267 | 268 | /** 269 | Return a dictionary with all key-value pairs, where values match the given predicate 270 | 271 | @param predicate A `NSPredicate` instance tested against the database's values to retrieve the corresponding key-value pairs 272 | */ 273 | - (NSDictionary *) dictionaryByFilteringWithPredicate:(NSPredicate *)predicate; 274 | 275 | /** 276 | Return an retained LDBSnapshot instance for this database 277 | 278 | LDBSnapshots are a way to "freeze" the state of the database. Write operation applied to the database after the 279 | snapshot was taken do not affect the snapshot. Most *read* methods available in the LevelDB class are also 280 | available in the LDBSnapshot class. 281 | */ 282 | - (LDBSnapshot *) newSnapshot; 283 | 284 | #pragma mark - Enumeration 285 | 286 | /** 287 | Enumerate over the keys in the database, in order. 288 | 289 | Same as `[self enumerateKeysBackward:FALSE startingAtKey:nil filteredByPredicate:nil andPrefix:nil usingBlock:block]` 290 | 291 | @param block The enumeration block used when iterating over all the keys. 292 | */ 293 | - (void) enumerateKeysUsingBlock:(LevelDBKeyBlock)block; 294 | 295 | /** 296 | Enumerate over the keys in the database, in direct or backward order, with some options to control the keys iterated over 297 | 298 | @param backward A boolean value indicating whether the enumeration happens in direct or backward order 299 | @param key (optional) The key at which to start iteration. If the key isn't present in the database, the enumeration starts at the key immediately greater than the provided one. The key can be a `NSData` or `NSString` 300 | @param predicate A `NSPredicate` instance tested against the values. The iteration block will only be called for keys associated to values matching the predicate. If `nil`, this is ignored. 301 | @param prefix A `NSString` or `NSData` prefix used to filter the keys. If provided, only the keys prefixed with this value will be iterated over. 302 | @param block The enumeration block used when iterating over all the keys. It takes two arguments: the first is a pointer to a `LevelDBKey` struct. You can convert this to a `NSString` or `NSData` instance, using `NSDataFromLevelDBKey(LevelDBKey *key)` and `NSStringFromLevelDBKey(LevelDBKey *key)` respectively. The second arguments to the block is a `BOOL *` that can be used to stop enumeration at any time (e.g. `*stop = TRUE;`). 303 | */ 304 | - (void) enumerateKeysBackward:(BOOL)backward 305 | startingAtKey:(id)key 306 | filteredByPredicate:(NSPredicate *)predicate 307 | andPrefix:(id)prefix 308 | usingBlock:(LevelDBKeyBlock)block; 309 | 310 | 311 | /** 312 | Enumerate over the key value pairs in the database, in order. 313 | 314 | Same as `[self enumerateKeysAndObjectsBackward:FALSE startingAtKey:nil filteredByPredicate:nil andPrefix:nil usingBlock:block]` 315 | 316 | @param block The enumeration block used when iterating over all the key value pairs. 317 | */ 318 | - (void) enumerateKeysAndObjectsUsingBlock:(LevelDBKeyValueBlock)block; 319 | 320 | /** 321 | Enumerate over the keys in the database, in direct or backward order, with some options to control the keys iterated over 322 | 323 | @param backward A boolean value indicating whether the enumeration happens in direct or backward order 324 | @param key (optional) The key at which to start iteration. If the key isn't present in the database, the enumeration starts at the key immediately greater than the provided one. The key can be a `NSData` or `NSString` 325 | @param predicate A `NSPredicate` instance tested against the values. The iteration block will only be called for keys associated to values matching the predicate. If `nil`, this is ignored. 326 | @param prefix A `NSString` or `NSData` prefix used to filter the keys. If provided, only the keys prefixed with this value will be iterated over. 327 | @param block The enumeration block used when iterating over all the keys. It takes three arguments: the first is a pointer to a `LevelDBKey` struct. You can convert this to a `NSString` or `NSData` instance, using `NSDataFromLevelDBKey(LevelDBKey *key)` and `NSStringFromLevelDBKey(LevelDBKey *key)` respectively. The second argument is the value associated with the key. The third arguments to the block is a `BOOL *` that can be used to stop enumeration at any time (e.g. `*stop = TRUE;`). 328 | */ 329 | - (void) enumerateKeysAndObjectsBackward:(BOOL)backward 330 | lazily:(BOOL)lazily 331 | startingAtKey:(id)key 332 | filteredByPredicate:(NSPredicate *)predicate 333 | andPrefix:(id)prefix 334 | usingBlock:(id)block; 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /Classes/LevelDB.mm: -------------------------------------------------------------------------------- 1 | // 2 | // LevelDB.m 3 | // 4 | // Copyright 2011 Pave Labs. All rights reserved. 5 | // See LICENCE for details. 6 | // 7 | 8 | #import "LevelDB.h" 9 | #import "LDBSnapshot.h" 10 | #import "LDBWriteBatch.h" 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | 18 | #include "Common.h" 19 | 20 | #define MaybeAddSnapshotToOptions(_from_, _to_, _snap_) \ 21 | leveldb::ReadOptions __to_;\ 22 | leveldb::ReadOptions * _to_ = &__to_;\ 23 | if (_snap_ != nil) { \ 24 | _to_->fill_cache = _from_.fill_cache; \ 25 | _to_->snapshot = [_snap_ getSnapshot]; \ 26 | } else \ 27 | _to_ = &_from_; 28 | 29 | #define SeekToFirstOrKey(iter, key, _backward_) \ 30 | (key != nil) ? iter->Seek(KeyFromStringOrData(key)) : \ 31 | _backward_ ? iter->SeekToLast() : iter->SeekToFirst() 32 | 33 | #define MoveCursor(_iter_, _backward_) \ 34 | _backward_ ? iter->Prev() : iter->Next() 35 | 36 | #define EnsureNSData(_obj_) \ 37 | ([_obj_ isKindOfClass:[NSData class]]) ? _obj_ : \ 38 | ([_obj_ isKindOfClass:[NSString class]]) ? [NSData dataWithBytes:[_obj_ cStringUsingEncoding:NSUTF8StringEncoding] \ 39 | length:[_obj_ lengthOfBytesUsingEncoding:NSUTF8StringEncoding]] : nil 40 | 41 | #define AssertDBExists(_db_) \ 42 | NSAssert(_db_ != NULL, @"Database reference is not existent (it has probably been closed)"); 43 | 44 | namespace { 45 | class BatchIterator : public leveldb::WriteBatch::Handler { 46 | public: 47 | void (^putCallback)(const leveldb::Slice& key, const leveldb::Slice& value); 48 | void (^deleteCallback)(const leveldb::Slice& key); 49 | 50 | virtual void Put(const leveldb::Slice& key, const leveldb::Slice& value) { 51 | putCallback(key, value); 52 | } 53 | virtual void Delete(const leveldb::Slice& key) { 54 | deleteCallback(key); 55 | } 56 | }; 57 | } 58 | 59 | NSString * NSStringFromLevelDBKey(LevelDBKey * key) { 60 | return [[[NSString alloc] initWithBytes:key->data 61 | length:key->length 62 | encoding:NSUTF8StringEncoding] autorelease]; 63 | } 64 | NSData * NSDataFromLevelDBKey(LevelDBKey * key) { 65 | return [NSData dataWithBytes:key->data length:key->length]; 66 | } 67 | 68 | NSString * getLibraryPath() { 69 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); 70 | return [paths objectAtIndex:0]; 71 | } 72 | 73 | NSString * const kLevelDBChangeType = @"changeType"; 74 | NSString * const kLevelDBChangeTypePut = @"put"; 75 | NSString * const kLevelDBChangeTypeDelete = @"del"; 76 | NSString * const kLevelDBChangeValue = @"value"; 77 | NSString * const kLevelDBChangeKey = @"key"; 78 | 79 | LevelDBOptions MakeLevelDBOptions() { 80 | return (LevelDBOptions) {true, true, false, false, true, 0, 0}; 81 | } 82 | 83 | @interface LDBSnapshot () 84 | + (id) snapshotFromDB:(LevelDB *)database; 85 | - (const leveldb::Snapshot *) getSnapshot; 86 | @end 87 | 88 | @interface LDBWritebatch () 89 | + (instancetype) writeBatchFromDB:(id)db; 90 | - (leveldb::WriteBatch) writeBatch; 91 | @end 92 | 93 | @interface LevelDB () { 94 | leveldb::DB * db; 95 | leveldb::ReadOptions readOptions; 96 | leveldb::WriteOptions writeOptions; 97 | const leveldb::Cache * cache; 98 | const leveldb::FilterPolicy * filterPolicy; 99 | } 100 | 101 | @property (nonatomic, readonly) leveldb::DB * db; 102 | 103 | @end 104 | 105 | @implementation LevelDB 106 | 107 | @synthesize db = db; 108 | @synthesize path = _path; 109 | 110 | + (LevelDBOptions) makeOptions { 111 | return MakeLevelDBOptions(); 112 | } 113 | - (id) initWithPath:(NSString *)path andName:(NSString *)name { 114 | LevelDBOptions opts = MakeLevelDBOptions(); 115 | return [self initWithPath:path name:name andOptions:opts]; 116 | } 117 | - (id) initWithPath:(NSString *)path name:(NSString *)name andOptions:(LevelDBOptions)opts { 118 | self = [super init]; 119 | if (self) { 120 | _name = name; 121 | _path = path; 122 | 123 | leveldb::Options options; 124 | 125 | options.create_if_missing = opts.createIfMissing; 126 | options.paranoid_checks = opts.paranoidCheck; 127 | options.error_if_exists = opts.errorIfExists; 128 | 129 | if (!opts.compression) 130 | options.compression = leveldb::kNoCompression; 131 | 132 | if (opts.cacheSize > 0) { 133 | options.block_cache = leveldb::NewLRUCache(opts.cacheSize); 134 | cache = options.block_cache; 135 | } else 136 | readOptions.fill_cache = false; 137 | 138 | if (opts.createIntermediateDirectories) { 139 | NSString *dirpath = [path stringByDeletingLastPathComponent]; 140 | NSFileManager *fm = [NSFileManager defaultManager]; 141 | NSError *crError; 142 | 143 | BOOL success = [fm createDirectoryAtPath:dirpath 144 | withIntermediateDirectories:true 145 | attributes:nil 146 | error:&crError]; 147 | if (!success) { 148 | NSLog(@"Problem creating parent directory: %@", crError); 149 | } 150 | } 151 | 152 | if (opts.filterPolicy > 0) { 153 | filterPolicy = leveldb::NewBloomFilterPolicy(opts.filterPolicy);; 154 | options.filter_policy = filterPolicy; 155 | } 156 | leveldb::Status status = leveldb::DB::Open(options, [_path UTF8String], &db); 157 | 158 | readOptions.fill_cache = true; 159 | writeOptions.sync = false; 160 | 161 | if(!status.ok()) { 162 | NSLog(@"Problem creating LevelDB database: %s", status.ToString().c_str()); 163 | } 164 | 165 | self.encoder = ^ NSData *(LevelDBKey *key, id object) { 166 | #ifdef DEBUG 167 | static dispatch_once_t onceToken; 168 | dispatch_once(&onceToken, ^{ 169 | NSLog(@"No encoder block was set for this database [%@]", name); 170 | NSLog(@"Using a convenience encoder/decoder pair using NSKeyedArchiver."); 171 | }); 172 | #endif 173 | return [NSKeyedArchiver archivedDataWithRootObject:object]; 174 | }; 175 | self.decoder = ^ id (LevelDBKey *key, NSData *data) { 176 | return [NSKeyedUnarchiver unarchiveObjectWithData:data]; 177 | }; 178 | } 179 | 180 | return self; 181 | } 182 | 183 | + (id) databaseInLibraryWithName:(NSString *)name { 184 | LevelDBOptions opts = MakeLevelDBOptions(); 185 | return [self databaseInLibraryWithName:name andOptions:opts]; 186 | } 187 | + (id) databaseInLibraryWithName:(NSString *)name 188 | andOptions:(LevelDBOptions)opts { 189 | NSString *path = [getLibraryPath() stringByAppendingPathComponent:name]; 190 | LevelDB *ldb = [[[self alloc] initWithPath:path name:name andOptions:opts] autorelease]; 191 | return ldb; 192 | } 193 | 194 | - (void) setSafe:(BOOL)safe { 195 | writeOptions.sync = safe; 196 | } 197 | - (BOOL) safe { 198 | return writeOptions.sync; 199 | } 200 | - (void) setUseCache:(BOOL)useCache { 201 | readOptions.fill_cache = useCache; 202 | } 203 | - (BOOL) useCache { 204 | return readOptions.fill_cache; 205 | } 206 | 207 | #pragma mark - Setters 208 | 209 | - (void) setObject:(id)value forKey:(id)key { 210 | AssertDBExists(db); 211 | AssertKeyType(key); 212 | NSParameterAssert(value != nil); 213 | 214 | leveldb::Slice k = KeyFromStringOrData(key); 215 | LevelDBKey lkey = GenericKeyFromSlice(k); 216 | 217 | NSData *data = _encoder(&lkey, value); 218 | leveldb::Slice v = SliceFromData(data); 219 | 220 | leveldb::Status status = db->Put(writeOptions, k, v); 221 | 222 | if(!status.ok()) { 223 | NSLog(@"Problem storing key/value pair in database: %s", status.ToString().c_str()); 224 | } 225 | } 226 | - (void) setValue:(id)value forKey:(NSString *)key { 227 | [self setObject:value forKey:key]; 228 | } 229 | - (void) setObject:(id)value forKeyedSubscript:(id)key { 230 | [self setObject:value forKey:key]; 231 | } 232 | - (void) addEntriesFromDictionary:(NSDictionary *)dictionary { 233 | [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 234 | [self setObject:obj forKey:key]; 235 | }]; 236 | } 237 | 238 | #pragma mark - Write batches 239 | 240 | - (LDBWritebatch *)newWritebatch { 241 | return [[LDBWritebatch writeBatchFromDB:self] retain]; 242 | } 243 | 244 | - (void) applyWritebatch:(LDBWritebatch *)writeBatch { 245 | leveldb::WriteBatch wb = [writeBatch writeBatch]; 246 | leveldb::Status status = db->Write(writeOptions, &wb); 247 | if(!status.ok()) { 248 | NSLog(@"Problem applying the write batch in database: %s", status.ToString().c_str()); 249 | } 250 | } 251 | 252 | #pragma mark - Getters 253 | 254 | - (id) objectForKey:(id)key { 255 | return [self objectForKey:key withSnapshot:nil]; 256 | } 257 | - (id) objectForKey:(id)key 258 | withSnapshot:(LDBSnapshot *)snapshot { 259 | 260 | AssertDBExists(db); 261 | AssertKeyType(key); 262 | std::string v_string; 263 | MaybeAddSnapshotToOptions(readOptions, readOptionsPtr, snapshot); 264 | leveldb::Slice k = KeyFromStringOrData(key); 265 | leveldb::Status status = db->Get(*readOptionsPtr, k, &v_string); 266 | 267 | if(!status.ok()) { 268 | if(!status.IsNotFound()) 269 | NSLog(@"Problem retrieving value for key '%@' from database: %s", key, status.ToString().c_str()); 270 | return nil; 271 | } 272 | 273 | LevelDBKey lkey = GenericKeyFromSlice(k); 274 | return DecodeFromSlice(v_string, &lkey, _decoder); 275 | } 276 | - (id) objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker { 277 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:keys.count]; 278 | [keys enumerateObjectsUsingBlock:^(id objId, NSUInteger idx, BOOL *stop) { 279 | id object = [self objectForKey:objId]; 280 | if (object == nil) object = marker; 281 | result[idx] = object; 282 | }]; 283 | return [NSArray arrayWithArray:result]; 284 | } 285 | - (id) valueForKey:(NSString *)key { 286 | if ([key characterAtIndex:0] == '@') { 287 | return [super valueForKey:[key stringByReplacingCharactersInRange:(NSRange){0, 1} 288 | withString:@""]]; 289 | } else 290 | return [self objectForKey:key]; 291 | } 292 | - (id) objectForKeyedSubscript:(id)key { 293 | return [self objectForKey:key withSnapshot:nil]; 294 | } 295 | 296 | - (BOOL) objectExistsForKey:(id)key { 297 | return [self objectExistsForKey:key withSnapshot:nil]; 298 | } 299 | - (BOOL) objectExistsForKey:(id)key 300 | withSnapshot:(LDBSnapshot *)snapshot { 301 | 302 | AssertDBExists(db); 303 | AssertKeyType(key); 304 | std::string v_string; 305 | MaybeAddSnapshotToOptions(readOptions, readOptionsPtr, snapshot); 306 | leveldb::Slice k = KeyFromStringOrData(key); 307 | leveldb::Status status = db->Get(*readOptionsPtr, k, &v_string); 308 | 309 | if (!status.ok()) { 310 | if (status.IsNotFound()) 311 | return false; 312 | else { 313 | NSLog(@"Problem retrieving value for key '%@' from database: %s", key, status.ToString().c_str()); 314 | return NULL; 315 | } 316 | } else 317 | return true; 318 | } 319 | 320 | #pragma mark - Removers 321 | 322 | - (void) removeObjectForKey:(id)key { 323 | AssertDBExists(db); 324 | AssertKeyType(key); 325 | 326 | leveldb::Slice k = KeyFromStringOrData(key); 327 | leveldb::Status status = db->Delete(writeOptions, k); 328 | 329 | if(!status.ok()) { 330 | NSLog(@"Problem deleting key/value pair in database: %s", status.ToString().c_str()); 331 | } 332 | } 333 | - (void) removeObjectsForKeys:(NSArray *)keyArray { 334 | [keyArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 335 | [self removeObjectForKey:obj]; 336 | }]; 337 | } 338 | 339 | - (void) removeAllObjects { 340 | [self removeAllObjectsWithPrefix:nil]; 341 | } 342 | - (void) removeAllObjectsWithPrefix:(id)prefix { 343 | AssertDBExists(db); 344 | 345 | leveldb::Iterator * iter = db->NewIterator(readOptions); 346 | leveldb::Slice lkey; 347 | 348 | const void *prefixPtr; 349 | size_t prefixLen; 350 | prefix = EnsureNSData(prefix); 351 | if (prefix) { 352 | prefixPtr = [(NSData *)prefix bytes]; 353 | prefixLen = (size_t)[(NSData *)prefix length]; 354 | } 355 | 356 | for (SeekToFirstOrKey(iter, (id)prefix, NO) 357 | ; iter->Valid() 358 | ; MoveCursor(iter, NO)) { 359 | 360 | lkey = iter->key(); 361 | if (prefix && memcmp(lkey.data(), prefixPtr, MIN(prefixLen, lkey.size())) != 0) 362 | break; 363 | 364 | db->Delete(writeOptions, lkey); 365 | } 366 | delete iter; 367 | } 368 | 369 | #pragma mark - Selection 370 | 371 | - (NSArray *)allKeys { 372 | NSMutableArray *keys = [[[NSMutableArray alloc] init] autorelease]; 373 | [self enumerateKeysUsingBlock:^(LevelDBKey *key, BOOL *stop) { 374 | [keys addObject:NSDataFromLevelDBKey(key)]; 375 | }]; 376 | return [NSArray arrayWithArray:keys]; 377 | } 378 | - (NSArray *)keysByFilteringWithPredicate:(NSPredicate *)predicate { 379 | NSMutableArray *keys = [[[NSMutableArray alloc] init] autorelease]; 380 | [self enumerateKeysAndObjectsBackward:NO lazily:NO 381 | startingAtKey:nil 382 | filteredByPredicate:predicate 383 | andPrefix:nil 384 | withSnapshot:nil 385 | usingBlock:^(LevelDBKey *key, id obj, BOOL *stop) { 386 | [keys addObject:NSDataFromLevelDBKey(key)]; 387 | }]; 388 | return [NSArray arrayWithArray:keys]; 389 | } 390 | 391 | - (NSDictionary *)dictionaryByFilteringWithPredicate:(NSPredicate *)predicate { 392 | NSMutableDictionary *results = [NSMutableDictionary dictionary]; 393 | [self enumerateKeysAndObjectsBackward:NO lazily:NO 394 | startingAtKey:nil 395 | filteredByPredicate:predicate 396 | andPrefix:nil 397 | withSnapshot:nil 398 | usingBlock:^(LevelDBKey *key, id obj, BOOL *stop) { 399 | [results setObject:obj forKey:NSDataFromLevelDBKey(key)]; 400 | }]; 401 | 402 | return [NSDictionary dictionaryWithDictionary:results]; 403 | } 404 | 405 | - (LDBSnapshot *) newSnapshot { 406 | return [[LDBSnapshot snapshotFromDB:self] retain]; 407 | } 408 | 409 | #pragma mark - Enumeration 410 | 411 | - (void) _startIterator:(leveldb::Iterator*)iter 412 | backward:(BOOL)backward 413 | prefix:(id)prefix 414 | start:(id)key { 415 | 416 | const void *prefixPtr; 417 | size_t prefixLen; 418 | leveldb::Slice lkey, startingKey; 419 | 420 | prefix = EnsureNSData(prefix); 421 | if (prefix) { 422 | prefixPtr = [(NSData *)prefix bytes]; 423 | prefixLen = (size_t)[(NSData *)prefix length]; 424 | startingKey = leveldb::Slice((char *)prefixPtr, prefixLen); 425 | 426 | if (key) { 427 | leveldb::Slice skey = KeyFromStringOrData(key); 428 | if (skey.size() > prefixLen && memcmp(skey.data(), prefixPtr, prefixLen) == 0) { 429 | startingKey = skey; 430 | } 431 | } 432 | 433 | /* 434 | * If a prefix is provided and the iteration is backwards 435 | * we need to start on the next key (maybe discarding the first iteration) 436 | */ 437 | if (backward) { 438 | signed long long i = startingKey.size() - 1; 439 | void * startingKeyPtr = malloc(startingKey.size()); 440 | unsigned char *keyChar; 441 | memcpy(startingKeyPtr, startingKey.data(), startingKey.size()); 442 | while (1) { 443 | if (i < 0) { 444 | iter->SeekToLast(); 445 | break; 446 | } 447 | keyChar = (unsigned char *)startingKeyPtr + i; 448 | if (*keyChar < 255) { 449 | *keyChar = *keyChar + 1; 450 | iter->Seek(leveldb::Slice((char *)startingKeyPtr, prefixLen)); 451 | if (!iter->Valid()) { 452 | iter->SeekToLast(); 453 | } 454 | break; 455 | } 456 | i--; 457 | }; 458 | free(startingKeyPtr); 459 | if (!iter->Valid()) 460 | return; 461 | 462 | lkey = iter->key(); 463 | if (prefix && memcmp(lkey.data(), prefixPtr, prefixLen) != 0) { 464 | iter->Prev(); 465 | } 466 | } else { 467 | // Otherwise, we start at the provided prefix 468 | iter->Seek(startingKey); 469 | } 470 | } else if (key) { 471 | iter->Seek(KeyFromStringOrData(key)); 472 | } else if (backward) { 473 | iter->SeekToLast(); 474 | } else { 475 | iter->SeekToFirst(); 476 | } 477 | } 478 | 479 | - (void) enumerateKeysUsingBlock:(LevelDBKeyBlock)block { 480 | 481 | [self enumerateKeysBackward:FALSE 482 | startingAtKey:nil 483 | filteredByPredicate:nil 484 | andPrefix:nil 485 | withSnapshot:nil 486 | usingBlock:block]; 487 | } 488 | 489 | - (void)enumerateKeysBackward:(BOOL)backward 490 | startingAtKey:(id)key 491 | filteredByPredicate:(NSPredicate *)predicate 492 | andPrefix:(id)prefix 493 | usingBlock:(LevelDBKeyBlock)block { 494 | 495 | [self enumerateKeysBackward:backward 496 | startingAtKey:key 497 | filteredByPredicate:predicate 498 | andPrefix:prefix 499 | withSnapshot:nil 500 | usingBlock:block]; 501 | } 502 | 503 | - (void) enumerateKeysBackward:(BOOL)backward 504 | startingAtKey:(id)key 505 | filteredByPredicate:(NSPredicate *)predicate 506 | andPrefix:(id)prefix 507 | withSnapshot:(LDBSnapshot *)snapshot 508 | usingBlock:(LevelDBKeyBlock)block { 509 | 510 | AssertDBExists(db); 511 | MaybeAddSnapshotToOptions(readOptions, readOptionsPtr, snapshot); 512 | leveldb::Iterator* iter = db->NewIterator(*readOptionsPtr); 513 | leveldb::Slice lkey; 514 | BOOL stop = false; 515 | 516 | NSData *prefixData = EnsureNSData(prefix); 517 | 518 | LevelDBKeyValueBlock iterate = (predicate != nil) 519 | ? ^(LevelDBKey *lk, id value, BOOL *stop) { 520 | if ([predicate evaluateWithObject:value]) 521 | block(lk, stop); 522 | } 523 | : ^(LevelDBKey *lk, id value, BOOL *stop) { 524 | block(lk, stop); 525 | }; 526 | 527 | for ([self _startIterator:iter backward:backward prefix:prefix start:key] 528 | ; iter->Valid() 529 | ; MoveCursor(iter, backward)) { 530 | 531 | lkey = iter->key(); 532 | if (prefix && memcmp(lkey.data(), [prefixData bytes], MIN((size_t)[prefixData length], lkey.size())) != 0) 533 | break; 534 | 535 | LevelDBKey lk = GenericKeyFromSlice(lkey); 536 | id v = (predicate == nil) ? nil : DecodeFromSlice(iter->value(), &lk, _decoder); 537 | iterate(&lk, v, &stop); 538 | if (stop) break; 539 | } 540 | 541 | delete iter; 542 | } 543 | 544 | - (void) enumerateKeysAndObjectsUsingBlock:(LevelDBKeyValueBlock)block { 545 | [self enumerateKeysAndObjectsBackward:FALSE 546 | lazily:FALSE 547 | startingAtKey:nil 548 | filteredByPredicate:nil 549 | andPrefix:nil 550 | withSnapshot:nil 551 | usingBlock:block]; 552 | } 553 | 554 | - (void)enumerateKeysAndObjectsBackward:(BOOL)backward 555 | lazily:(BOOL)lazily 556 | startingAtKey:(id)key 557 | filteredByPredicate:(NSPredicate *)predicate 558 | andPrefix:(id)prefix 559 | usingBlock:(id)block { 560 | 561 | [self enumerateKeysAndObjectsBackward:backward 562 | lazily:lazily 563 | startingAtKey:key 564 | filteredByPredicate:predicate 565 | andPrefix:prefix 566 | withSnapshot:nil 567 | usingBlock:block]; 568 | } 569 | 570 | - (void) enumerateKeysAndObjectsBackward:(BOOL)backward 571 | lazily:(BOOL)lazily 572 | startingAtKey:(id)key 573 | filteredByPredicate:(NSPredicate *)predicate 574 | andPrefix:(id)prefix 575 | withSnapshot:(LDBSnapshot *)snapshot 576 | usingBlock:(id)block{ 577 | 578 | AssertDBExists(db); 579 | MaybeAddSnapshotToOptions(readOptions, readOptionsPtr, snapshot); 580 | leveldb::Iterator* iter = db->NewIterator(*readOptionsPtr); 581 | leveldb::Slice lkey; 582 | BOOL stop = false; 583 | 584 | LevelDBLazyKeyValueBlock iterate = (predicate != nil) 585 | 586 | // If there is a predicate: 587 | ? ^ (LevelDBKey *lk, LevelDBValueGetterBlock valueGetter, BOOL *stop) { 588 | // We need to get the value, whether the `lazily` flag was set or not 589 | id value = valueGetter(); 590 | 591 | // If the predicate yields positive, we call the block 592 | if ([predicate evaluateWithObject:value]) { 593 | if (lazily) 594 | ((LevelDBLazyKeyValueBlock)block)(lk, valueGetter, stop); 595 | else 596 | ((LevelDBKeyValueBlock)block)(lk, value, stop); 597 | } 598 | } 599 | 600 | // Otherwise, we call the block 601 | : ^ (LevelDBKey *lk, LevelDBValueGetterBlock valueGetter, BOOL *stop) { 602 | if (lazily) 603 | ((LevelDBLazyKeyValueBlock)block)(lk, valueGetter, stop); 604 | else 605 | ((LevelDBKeyValueBlock)block)(lk, valueGetter(), stop); 606 | }; 607 | 608 | NSData *prefixData = EnsureNSData(prefix); 609 | 610 | LevelDBValueGetterBlock getter; 611 | for ([self _startIterator:iter backward:backward prefix:prefix start:key] 612 | ; iter->Valid() 613 | ; MoveCursor(iter, backward)) { 614 | 615 | lkey = iter->key(); 616 | // If there is prefix provided, and the prefix and key don't match, we break out of iteration 617 | if (prefix && memcmp(lkey.data(), [prefixData bytes], MIN((size_t)[prefixData length], lkey.size())) != 0) 618 | break; 619 | 620 | __block LevelDBKey lk = GenericKeyFromSlice(lkey); 621 | __block id v = nil; 622 | 623 | getter = ^ id { 624 | if (v) return v; 625 | v = DecodeFromSlice(iter->value(), &lk, _decoder); 626 | return v; 627 | }; 628 | 629 | iterate(&lk, getter, &stop); 630 | if (stop) break; 631 | } 632 | 633 | delete iter; 634 | } 635 | 636 | #pragma mark - Bookkeeping 637 | 638 | - (void) deleteDatabaseFromDisk { 639 | [self close]; 640 | NSFileManager *fileManager = [NSFileManager defaultManager]; 641 | NSError *error; 642 | [fileManager removeItemAtPath:_path error:&error]; 643 | } 644 | 645 | - (void) close { 646 | @synchronized(self) { 647 | if (db) { 648 | delete db; 649 | 650 | if (cache) 651 | delete cache; 652 | 653 | if (filterPolicy) 654 | delete filterPolicy; 655 | 656 | db = NULL; 657 | } 658 | } 659 | } 660 | - (BOOL) closed { 661 | return db == NULL; 662 | } 663 | - (void) dealloc { 664 | [self close]; 665 | [super dealloc]; 666 | } 667 | 668 | @end 669 | -------------------------------------------------------------------------------- /Configs/Objective-LevelDB.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Objective-LevelDB.xcconfig 3 | // Objective-LevelDB 4 | // 5 | 6 | CLANG_ENABLE_OBJC_ARC = NO 7 | HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/Libraries/leveldb/include" 8 | LIBRARY_SEARCH_PATHS = $(inherited) "${SRCROOT}/Libraries/leveldb/" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Pave Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/c.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | Use of this source code is governed by a BSD-style license that can be 3 | found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | C bindings for leveldb. May be useful as a stable ABI that can be 6 | used by programs that keep leveldb in a shared library, or for 7 | a JNI api. 8 | 9 | Does not support: 10 | . getters for the option types 11 | . custom comparators that implement key shortening 12 | . custom iter, db, env, cache implementations using just the C bindings 13 | 14 | Some conventions: 15 | 16 | (1) We expose just opaque struct pointers and functions to clients. 17 | This allows us to change internal representations without having to 18 | recompile clients. 19 | 20 | (2) For simplicity, there is no equivalent to the Slice type. Instead, 21 | the caller has to pass the pointer and length as separate 22 | arguments. 23 | 24 | (3) Errors are represented by a null-terminated c string. NULL 25 | means no error. All operations that can raise an error are passed 26 | a "char** errptr" as the last argument. One of the following must 27 | be true on entry: 28 | *errptr == NULL 29 | *errptr points to a malloc()ed null-terminated error message 30 | (On Windows, *errptr must have been malloc()-ed by this library.) 31 | On success, a leveldb routine leaves *errptr unchanged. 32 | On failure, leveldb frees the old value of *errptr and 33 | set *errptr to a malloc()ed error message. 34 | 35 | (4) Bools have the type unsigned char (0 == false; rest == true) 36 | 37 | (5) All of the pointer arguments must be non-NULL. 38 | */ 39 | 40 | #ifndef STORAGE_LEVELDB_INCLUDE_C_H_ 41 | #define STORAGE_LEVELDB_INCLUDE_C_H_ 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | /* Exported types */ 52 | 53 | typedef struct leveldb_t leveldb_t; 54 | typedef struct leveldb_cache_t leveldb_cache_t; 55 | typedef struct leveldb_comparator_t leveldb_comparator_t; 56 | typedef struct leveldb_env_t leveldb_env_t; 57 | typedef struct leveldb_filelock_t leveldb_filelock_t; 58 | typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; 59 | typedef struct leveldb_iterator_t leveldb_iterator_t; 60 | typedef struct leveldb_logger_t leveldb_logger_t; 61 | typedef struct leveldb_options_t leveldb_options_t; 62 | typedef struct leveldb_randomfile_t leveldb_randomfile_t; 63 | typedef struct leveldb_readoptions_t leveldb_readoptions_t; 64 | typedef struct leveldb_seqfile_t leveldb_seqfile_t; 65 | typedef struct leveldb_snapshot_t leveldb_snapshot_t; 66 | typedef struct leveldb_writablefile_t leveldb_writablefile_t; 67 | typedef struct leveldb_writebatch_t leveldb_writebatch_t; 68 | typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; 69 | 70 | /* DB operations */ 71 | 72 | extern leveldb_t* leveldb_open( 73 | const leveldb_options_t* options, 74 | const char* name, 75 | char** errptr); 76 | 77 | extern void leveldb_close(leveldb_t* db); 78 | 79 | extern void leveldb_put( 80 | leveldb_t* db, 81 | const leveldb_writeoptions_t* options, 82 | const char* key, size_t keylen, 83 | const char* val, size_t vallen, 84 | char** errptr); 85 | 86 | extern void leveldb_delete( 87 | leveldb_t* db, 88 | const leveldb_writeoptions_t* options, 89 | const char* key, size_t keylen, 90 | char** errptr); 91 | 92 | extern void leveldb_write( 93 | leveldb_t* db, 94 | const leveldb_writeoptions_t* options, 95 | leveldb_writebatch_t* batch, 96 | char** errptr); 97 | 98 | /* Returns NULL if not found. A malloc()ed array otherwise. 99 | Stores the length of the array in *vallen. */ 100 | extern char* leveldb_get( 101 | leveldb_t* db, 102 | const leveldb_readoptions_t* options, 103 | const char* key, size_t keylen, 104 | size_t* vallen, 105 | char** errptr); 106 | 107 | extern leveldb_iterator_t* leveldb_create_iterator( 108 | leveldb_t* db, 109 | const leveldb_readoptions_t* options); 110 | 111 | extern const leveldb_snapshot_t* leveldb_create_snapshot( 112 | leveldb_t* db); 113 | 114 | extern void leveldb_release_snapshot( 115 | leveldb_t* db, 116 | const leveldb_snapshot_t* snapshot); 117 | 118 | /* Returns NULL if property name is unknown. 119 | Else returns a pointer to a malloc()-ed null-terminated value. */ 120 | extern char* leveldb_property_value( 121 | leveldb_t* db, 122 | const char* propname); 123 | 124 | extern void leveldb_approximate_sizes( 125 | leveldb_t* db, 126 | int num_ranges, 127 | const char* const* range_start_key, const size_t* range_start_key_len, 128 | const char* const* range_limit_key, const size_t* range_limit_key_len, 129 | uint64_t* sizes); 130 | 131 | extern void leveldb_compact_range( 132 | leveldb_t* db, 133 | const char* start_key, size_t start_key_len, 134 | const char* limit_key, size_t limit_key_len); 135 | 136 | /* Management operations */ 137 | 138 | extern void leveldb_destroy_db( 139 | const leveldb_options_t* options, 140 | const char* name, 141 | char** errptr); 142 | 143 | extern void leveldb_repair_db( 144 | const leveldb_options_t* options, 145 | const char* name, 146 | char** errptr); 147 | 148 | /* Iterator */ 149 | 150 | extern void leveldb_iter_destroy(leveldb_iterator_t*); 151 | extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); 152 | extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); 153 | extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); 154 | extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); 155 | extern void leveldb_iter_next(leveldb_iterator_t*); 156 | extern void leveldb_iter_prev(leveldb_iterator_t*); 157 | extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); 158 | extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); 159 | extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); 160 | 161 | /* Write batch */ 162 | 163 | extern leveldb_writebatch_t* leveldb_writebatch_create(); 164 | extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); 165 | extern void leveldb_writebatch_clear(leveldb_writebatch_t*); 166 | extern void leveldb_writebatch_put( 167 | leveldb_writebatch_t*, 168 | const char* key, size_t klen, 169 | const char* val, size_t vlen); 170 | extern void leveldb_writebatch_delete( 171 | leveldb_writebatch_t*, 172 | const char* key, size_t klen); 173 | extern void leveldb_writebatch_iterate( 174 | leveldb_writebatch_t*, 175 | void* state, 176 | void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), 177 | void (*deleted)(void*, const char* k, size_t klen)); 178 | 179 | /* Options */ 180 | 181 | extern leveldb_options_t* leveldb_options_create(); 182 | extern void leveldb_options_destroy(leveldb_options_t*); 183 | extern void leveldb_options_set_comparator( 184 | leveldb_options_t*, 185 | leveldb_comparator_t*); 186 | extern void leveldb_options_set_filter_policy( 187 | leveldb_options_t*, 188 | leveldb_filterpolicy_t*); 189 | extern void leveldb_options_set_create_if_missing( 190 | leveldb_options_t*, unsigned char); 191 | extern void leveldb_options_set_error_if_exists( 192 | leveldb_options_t*, unsigned char); 193 | extern void leveldb_options_set_paranoid_checks( 194 | leveldb_options_t*, unsigned char); 195 | extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); 196 | extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); 197 | extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); 198 | extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); 199 | extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); 200 | extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); 201 | extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); 202 | 203 | enum { 204 | leveldb_no_compression = 0, 205 | leveldb_snappy_compression = 1 206 | }; 207 | extern void leveldb_options_set_compression(leveldb_options_t*, int); 208 | 209 | /* Comparator */ 210 | 211 | extern leveldb_comparator_t* leveldb_comparator_create( 212 | void* state, 213 | void (*destructor)(void*), 214 | int (*compare)( 215 | void*, 216 | const char* a, size_t alen, 217 | const char* b, size_t blen), 218 | const char* (*name)(void*)); 219 | extern void leveldb_comparator_destroy(leveldb_comparator_t*); 220 | 221 | /* Filter policy */ 222 | 223 | extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( 224 | void* state, 225 | void (*destructor)(void*), 226 | char* (*create_filter)( 227 | void*, 228 | const char* const* key_array, const size_t* key_length_array, 229 | int num_keys, 230 | size_t* filter_length), 231 | unsigned char (*key_may_match)( 232 | void*, 233 | const char* key, size_t length, 234 | const char* filter, size_t filter_length), 235 | const char* (*name)(void*)); 236 | extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); 237 | 238 | extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( 239 | int bits_per_key); 240 | 241 | /* Read options */ 242 | 243 | extern leveldb_readoptions_t* leveldb_readoptions_create(); 244 | extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); 245 | extern void leveldb_readoptions_set_verify_checksums( 246 | leveldb_readoptions_t*, 247 | unsigned char); 248 | extern void leveldb_readoptions_set_fill_cache( 249 | leveldb_readoptions_t*, unsigned char); 250 | extern void leveldb_readoptions_set_snapshot( 251 | leveldb_readoptions_t*, 252 | const leveldb_snapshot_t*); 253 | 254 | /* Write options */ 255 | 256 | extern leveldb_writeoptions_t* leveldb_writeoptions_create(); 257 | extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); 258 | extern void leveldb_writeoptions_set_sync( 259 | leveldb_writeoptions_t*, unsigned char); 260 | 261 | /* Cache */ 262 | 263 | extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); 264 | extern void leveldb_cache_destroy(leveldb_cache_t* cache); 265 | 266 | /* Env */ 267 | 268 | extern leveldb_env_t* leveldb_create_default_env(); 269 | extern void leveldb_env_destroy(leveldb_env_t*); 270 | 271 | /* Utility */ 272 | 273 | /* Calls free(ptr). 274 | REQUIRES: ptr was malloc()-ed and returned by one of the routines 275 | in this file. Note that in certain cases (typically on Windows), you 276 | may need to call this routine instead of free(ptr) to dispose of 277 | malloc()-ed memory returned by this library. */ 278 | extern void leveldb_free(void* ptr); 279 | 280 | /* Return the major version number for this release. */ 281 | extern int leveldb_major_version(); 282 | 283 | /* Return the minor version number for this release. */ 284 | extern int leveldb_minor_version(); 285 | 286 | #ifdef __cplusplus 287 | } /* end extern "C" */ 288 | #endif 289 | 290 | #endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ 291 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/cache.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // A Cache is an interface that maps keys to values. It has internal 6 | // synchronization and may be safely accessed concurrently from 7 | // multiple threads. It may automatically evict entries to make room 8 | // for new entries. Values have a specified charge against the cache 9 | // capacity. For example, a cache where the values are variable 10 | // length strings, may use the length of the string as the charge for 11 | // the string. 12 | // 13 | // A builtin cache implementation with a least-recently-used eviction 14 | // policy is provided. Clients may use their own implementations if 15 | // they want something more sophisticated (like scan-resistance, a 16 | // custom eviction policy, variable cache sizing, etc.) 17 | 18 | #ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ 19 | #define STORAGE_LEVELDB_INCLUDE_CACHE_H_ 20 | 21 | #include 22 | #include "leveldb/slice.h" 23 | 24 | namespace leveldb { 25 | 26 | class Cache; 27 | 28 | // Create a new cache with a fixed size capacity. This implementation 29 | // of Cache uses a least-recently-used eviction policy. 30 | extern Cache* NewLRUCache(size_t capacity); 31 | 32 | class Cache { 33 | public: 34 | Cache() { } 35 | 36 | // Destroys all existing entries by calling the "deleter" 37 | // function that was passed to the constructor. 38 | virtual ~Cache(); 39 | 40 | // Opaque handle to an entry stored in the cache. 41 | struct Handle { }; 42 | 43 | // Insert a mapping from key->value into the cache and assign it 44 | // the specified charge against the total cache capacity. 45 | // 46 | // Returns a handle that corresponds to the mapping. The caller 47 | // must call this->Release(handle) when the returned mapping is no 48 | // longer needed. 49 | // 50 | // When the inserted entry is no longer needed, the key and 51 | // value will be passed to "deleter". 52 | virtual Handle* Insert(const Slice& key, void* value, size_t charge, 53 | void (*deleter)(const Slice& key, void* value)) = 0; 54 | 55 | // If the cache has no mapping for "key", returns NULL. 56 | // 57 | // Else return a handle that corresponds to the mapping. The caller 58 | // must call this->Release(handle) when the returned mapping is no 59 | // longer needed. 60 | virtual Handle* Lookup(const Slice& key) = 0; 61 | 62 | // Release a mapping returned by a previous Lookup(). 63 | // REQUIRES: handle must not have been released yet. 64 | // REQUIRES: handle must have been returned by a method on *this. 65 | virtual void Release(Handle* handle) = 0; 66 | 67 | // Return the value encapsulated in a handle returned by a 68 | // successful Lookup(). 69 | // REQUIRES: handle must not have been released yet. 70 | // REQUIRES: handle must have been returned by a method on *this. 71 | virtual void* Value(Handle* handle) = 0; 72 | 73 | // If the cache contains entry for key, erase it. Note that the 74 | // underlying entry will be kept around until all existing handles 75 | // to it have been released. 76 | virtual void Erase(const Slice& key) = 0; 77 | 78 | // Return a new numeric id. May be used by multiple clients who are 79 | // sharing the same cache to partition the key space. Typically the 80 | // client will allocate a new id at startup and prepend the id to 81 | // its cache keys. 82 | virtual uint64_t NewId() = 0; 83 | 84 | private: 85 | void LRU_Remove(Handle* e); 86 | void LRU_Append(Handle* e); 87 | void Unref(Handle* e); 88 | 89 | struct Rep; 90 | Rep* rep_; 91 | 92 | // No copying allowed 93 | Cache(const Cache&); 94 | void operator=(const Cache&); 95 | }; 96 | 97 | } // namespace leveldb 98 | 99 | #endif // STORAGE_LEVELDB_INCLUDE_CACHE_H_ 100 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/comparator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ 6 | #define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ 7 | 8 | #include 9 | 10 | namespace leveldb { 11 | 12 | class Slice; 13 | 14 | // A Comparator object provides a total order across slices that are 15 | // used as keys in an sstable or a database. A Comparator implementation 16 | // must be thread-safe since leveldb may invoke its methods concurrently 17 | // from multiple threads. 18 | class Comparator { 19 | public: 20 | virtual ~Comparator(); 21 | 22 | // Three-way comparison. Returns value: 23 | // < 0 iff "a" < "b", 24 | // == 0 iff "a" == "b", 25 | // > 0 iff "a" > "b" 26 | virtual int Compare(const Slice& a, const Slice& b) const = 0; 27 | 28 | // The name of the comparator. Used to check for comparator 29 | // mismatches (i.e., a DB created with one comparator is 30 | // accessed using a different comparator. 31 | // 32 | // The client of this package should switch to a new name whenever 33 | // the comparator implementation changes in a way that will cause 34 | // the relative ordering of any two keys to change. 35 | // 36 | // Names starting with "leveldb." are reserved and should not be used 37 | // by any clients of this package. 38 | virtual const char* Name() const = 0; 39 | 40 | // Advanced functions: these are used to reduce the space requirements 41 | // for internal data structures like index blocks. 42 | 43 | // If *start < limit, changes *start to a short string in [start,limit). 44 | // Simple comparator implementations may return with *start unchanged, 45 | // i.e., an implementation of this method that does nothing is correct. 46 | virtual void FindShortestSeparator( 47 | std::string* start, 48 | const Slice& limit) const = 0; 49 | 50 | // Changes *key to a short string >= *key. 51 | // Simple comparator implementations may return with *key unchanged, 52 | // i.e., an implementation of this method that does nothing is correct. 53 | virtual void FindShortSuccessor(std::string* key) const = 0; 54 | }; 55 | 56 | // Return a builtin comparator that uses lexicographic byte-wise 57 | // ordering. The result remains the property of this module and 58 | // must not be deleted. 59 | extern const Comparator* BytewiseComparator(); 60 | 61 | } // namespace leveldb 62 | 63 | #endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ 64 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/db.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ 6 | #define STORAGE_LEVELDB_INCLUDE_DB_H_ 7 | 8 | #include 9 | #include 10 | #include "leveldb/iterator.h" 11 | #include "leveldb/options.h" 12 | 13 | namespace leveldb { 14 | 15 | // Update Makefile if you change these 16 | static const int kMajorVersion = 1; 17 | static const int kMinorVersion = 18; 18 | 19 | struct Options; 20 | struct ReadOptions; 21 | struct WriteOptions; 22 | class WriteBatch; 23 | 24 | // Abstract handle to particular state of a DB. 25 | // A Snapshot is an immutable object and can therefore be safely 26 | // accessed from multiple threads without any external synchronization. 27 | class Snapshot { 28 | protected: 29 | virtual ~Snapshot(); 30 | }; 31 | 32 | // A range of keys 33 | struct Range { 34 | Slice start; // Included in the range 35 | Slice limit; // Not included in the range 36 | 37 | Range() { } 38 | Range(const Slice& s, const Slice& l) : start(s), limit(l) { } 39 | }; 40 | 41 | // A DB is a persistent ordered map from keys to values. 42 | // A DB is safe for concurrent access from multiple threads without 43 | // any external synchronization. 44 | class DB { 45 | public: 46 | // Open the database with the specified "name". 47 | // Stores a pointer to a heap-allocated database in *dbptr and returns 48 | // OK on success. 49 | // Stores NULL in *dbptr and returns a non-OK status on error. 50 | // Caller should delete *dbptr when it is no longer needed. 51 | static Status Open(const Options& options, 52 | const std::string& name, 53 | DB** dbptr); 54 | 55 | DB() { } 56 | virtual ~DB(); 57 | 58 | // Set the database entry for "key" to "value". Returns OK on success, 59 | // and a non-OK status on error. 60 | // Note: consider setting options.sync = true. 61 | virtual Status Put(const WriteOptions& options, 62 | const Slice& key, 63 | const Slice& value) = 0; 64 | 65 | // Remove the database entry (if any) for "key". Returns OK on 66 | // success, and a non-OK status on error. It is not an error if "key" 67 | // did not exist in the database. 68 | // Note: consider setting options.sync = true. 69 | virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; 70 | 71 | // Apply the specified updates to the database. 72 | // Returns OK on success, non-OK on failure. 73 | // Note: consider setting options.sync = true. 74 | virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; 75 | 76 | // If the database contains an entry for "key" store the 77 | // corresponding value in *value and return OK. 78 | // 79 | // If there is no entry for "key" leave *value unchanged and return 80 | // a status for which Status::IsNotFound() returns true. 81 | // 82 | // May return some other Status on an error. 83 | virtual Status Get(const ReadOptions& options, 84 | const Slice& key, std::string* value) = 0; 85 | 86 | // Return a heap-allocated iterator over the contents of the database. 87 | // The result of NewIterator() is initially invalid (caller must 88 | // call one of the Seek methods on the iterator before using it). 89 | // 90 | // Caller should delete the iterator when it is no longer needed. 91 | // The returned iterator should be deleted before this db is deleted. 92 | virtual Iterator* NewIterator(const ReadOptions& options) = 0; 93 | 94 | // Return a handle to the current DB state. Iterators created with 95 | // this handle will all observe a stable snapshot of the current DB 96 | // state. The caller must call ReleaseSnapshot(result) when the 97 | // snapshot is no longer needed. 98 | virtual const Snapshot* GetSnapshot() = 0; 99 | 100 | // Release a previously acquired snapshot. The caller must not 101 | // use "snapshot" after this call. 102 | virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; 103 | 104 | // DB implementations can export properties about their state 105 | // via this method. If "property" is a valid property understood by this 106 | // DB implementation, fills "*value" with its current value and returns 107 | // true. Otherwise returns false. 108 | // 109 | // 110 | // Valid property names include: 111 | // 112 | // "leveldb.num-files-at-level" - return the number of files at level , 113 | // where is an ASCII representation of a level number (e.g. "0"). 114 | // "leveldb.stats" - returns a multi-line string that describes statistics 115 | // about the internal operation of the DB. 116 | // "leveldb.sstables" - returns a multi-line string that describes all 117 | // of the sstables that make up the db contents. 118 | virtual bool GetProperty(const Slice& property, std::string* value) = 0; 119 | 120 | // For each i in [0,n-1], store in "sizes[i]", the approximate 121 | // file system space used by keys in "[range[i].start .. range[i].limit)". 122 | // 123 | // Note that the returned sizes measure file system space usage, so 124 | // if the user data compresses by a factor of ten, the returned 125 | // sizes will be one-tenth the size of the corresponding user data size. 126 | // 127 | // The results may not include the sizes of recently written data. 128 | virtual void GetApproximateSizes(const Range* range, int n, 129 | uint64_t* sizes) = 0; 130 | 131 | // Compact the underlying storage for the key range [*begin,*end]. 132 | // In particular, deleted and overwritten versions are discarded, 133 | // and the data is rearranged to reduce the cost of operations 134 | // needed to access the data. This operation should typically only 135 | // be invoked by users who understand the underlying implementation. 136 | // 137 | // begin==NULL is treated as a key before all keys in the database. 138 | // end==NULL is treated as a key after all keys in the database. 139 | // Therefore the following call will compact the entire database: 140 | // db->CompactRange(NULL, NULL); 141 | virtual void CompactRange(const Slice* begin, const Slice* end) = 0; 142 | 143 | private: 144 | // No copying allowed 145 | DB(const DB&); 146 | void operator=(const DB&); 147 | }; 148 | 149 | // Destroy the contents of the specified database. 150 | // Be very careful using this method. 151 | Status DestroyDB(const std::string& name, const Options& options); 152 | 153 | // If a DB cannot be opened, you may attempt to call this method to 154 | // resurrect as much of the contents of the database as possible. 155 | // Some data may be lost, so be careful when calling this function 156 | // on a database that contains important information. 157 | Status RepairDB(const std::string& dbname, const Options& options); 158 | 159 | } // namespace leveldb 160 | 161 | #endif // STORAGE_LEVELDB_INCLUDE_DB_H_ 162 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/dumpfile.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ 6 | #define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ 7 | 8 | #include 9 | #include "leveldb/env.h" 10 | #include "leveldb/status.h" 11 | 12 | namespace leveldb { 13 | 14 | // Dump the contents of the file named by fname in text format to 15 | // *dst. Makes a sequence of dst->Append() calls; each call is passed 16 | // the newline-terminated text corresponding to a single item found 17 | // in the file. 18 | // 19 | // Returns a non-OK result if fname does not name a leveldb storage 20 | // file, or if the file cannot be read. 21 | Status DumpFile(Env* env, const std::string& fname, WritableFile* dst); 22 | 23 | } // namespace leveldb 24 | 25 | #endif // STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ 26 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/env.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // An Env is an interface used by the leveldb implementation to access 6 | // operating system functionality like the filesystem etc. Callers 7 | // may wish to provide a custom Env object when opening a database to 8 | // get fine gain control; e.g., to rate limit file system operations. 9 | // 10 | // All Env implementations are safe for concurrent access from 11 | // multiple threads without any external synchronization. 12 | 13 | #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ 14 | #define STORAGE_LEVELDB_INCLUDE_ENV_H_ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "leveldb/status.h" 21 | 22 | namespace leveldb { 23 | 24 | class FileLock; 25 | class Logger; 26 | class RandomAccessFile; 27 | class SequentialFile; 28 | class Slice; 29 | class WritableFile; 30 | 31 | class Env { 32 | public: 33 | Env() { } 34 | virtual ~Env(); 35 | 36 | // Return a default environment suitable for the current operating 37 | // system. Sophisticated users may wish to provide their own Env 38 | // implementation instead of relying on this default environment. 39 | // 40 | // The result of Default() belongs to leveldb and must never be deleted. 41 | static Env* Default(); 42 | 43 | // Create a brand new sequentially-readable file with the specified name. 44 | // On success, stores a pointer to the new file in *result and returns OK. 45 | // On failure stores NULL in *result and returns non-OK. If the file does 46 | // not exist, returns a non-OK status. 47 | // 48 | // The returned file will only be accessed by one thread at a time. 49 | virtual Status NewSequentialFile(const std::string& fname, 50 | SequentialFile** result) = 0; 51 | 52 | // Create a brand new random access read-only file with the 53 | // specified name. On success, stores a pointer to the new file in 54 | // *result and returns OK. On failure stores NULL in *result and 55 | // returns non-OK. If the file does not exist, returns a non-OK 56 | // status. 57 | // 58 | // The returned file may be concurrently accessed by multiple threads. 59 | virtual Status NewRandomAccessFile(const std::string& fname, 60 | RandomAccessFile** result) = 0; 61 | 62 | // Create an object that writes to a new file with the specified 63 | // name. Deletes any existing file with the same name and creates a 64 | // new file. On success, stores a pointer to the new file in 65 | // *result and returns OK. On failure stores NULL in *result and 66 | // returns non-OK. 67 | // 68 | // The returned file will only be accessed by one thread at a time. 69 | virtual Status NewWritableFile(const std::string& fname, 70 | WritableFile** result) = 0; 71 | 72 | // Returns true iff the named file exists. 73 | virtual bool FileExists(const std::string& fname) = 0; 74 | 75 | // Store in *result the names of the children of the specified directory. 76 | // The names are relative to "dir". 77 | // Original contents of *results are dropped. 78 | virtual Status GetChildren(const std::string& dir, 79 | std::vector* result) = 0; 80 | 81 | // Delete the named file. 82 | virtual Status DeleteFile(const std::string& fname) = 0; 83 | 84 | // Create the specified directory. 85 | virtual Status CreateDir(const std::string& dirname) = 0; 86 | 87 | // Delete the specified directory. 88 | virtual Status DeleteDir(const std::string& dirname) = 0; 89 | 90 | // Store the size of fname in *file_size. 91 | virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; 92 | 93 | // Rename file src to target. 94 | virtual Status RenameFile(const std::string& src, 95 | const std::string& target) = 0; 96 | 97 | // Lock the specified file. Used to prevent concurrent access to 98 | // the same db by multiple processes. On failure, stores NULL in 99 | // *lock and returns non-OK. 100 | // 101 | // On success, stores a pointer to the object that represents the 102 | // acquired lock in *lock and returns OK. The caller should call 103 | // UnlockFile(*lock) to release the lock. If the process exits, 104 | // the lock will be automatically released. 105 | // 106 | // If somebody else already holds the lock, finishes immediately 107 | // with a failure. I.e., this call does not wait for existing locks 108 | // to go away. 109 | // 110 | // May create the named file if it does not already exist. 111 | virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; 112 | 113 | // Release the lock acquired by a previous successful call to LockFile. 114 | // REQUIRES: lock was returned by a successful LockFile() call 115 | // REQUIRES: lock has not already been unlocked. 116 | virtual Status UnlockFile(FileLock* lock) = 0; 117 | 118 | // Arrange to run "(*function)(arg)" once in a background thread. 119 | // 120 | // "function" may run in an unspecified thread. Multiple functions 121 | // added to the same Env may run concurrently in different threads. 122 | // I.e., the caller may not assume that background work items are 123 | // serialized. 124 | virtual void Schedule( 125 | void (*function)(void* arg), 126 | void* arg) = 0; 127 | 128 | // Start a new thread, invoking "function(arg)" within the new thread. 129 | // When "function(arg)" returns, the thread will be destroyed. 130 | virtual void StartThread(void (*function)(void* arg), void* arg) = 0; 131 | 132 | // *path is set to a temporary directory that can be used for testing. It may 133 | // or many not have just been created. The directory may or may not differ 134 | // between runs of the same process, but subsequent calls will return the 135 | // same directory. 136 | virtual Status GetTestDirectory(std::string* path) = 0; 137 | 138 | // Create and return a log file for storing informational messages. 139 | virtual Status NewLogger(const std::string& fname, Logger** result) = 0; 140 | 141 | // Returns the number of micro-seconds since some fixed point in time. Only 142 | // useful for computing deltas of time. 143 | virtual uint64_t NowMicros() = 0; 144 | 145 | // Sleep/delay the thread for the prescribed number of micro-seconds. 146 | virtual void SleepForMicroseconds(int micros) = 0; 147 | 148 | private: 149 | // No copying allowed 150 | Env(const Env&); 151 | void operator=(const Env&); 152 | }; 153 | 154 | // A file abstraction for reading sequentially through a file 155 | class SequentialFile { 156 | public: 157 | SequentialFile() { } 158 | virtual ~SequentialFile(); 159 | 160 | // Read up to "n" bytes from the file. "scratch[0..n-1]" may be 161 | // written by this routine. Sets "*result" to the data that was 162 | // read (including if fewer than "n" bytes were successfully read). 163 | // May set "*result" to point at data in "scratch[0..n-1]", so 164 | // "scratch[0..n-1]" must be live when "*result" is used. 165 | // If an error was encountered, returns a non-OK status. 166 | // 167 | // REQUIRES: External synchronization 168 | virtual Status Read(size_t n, Slice* result, char* scratch) = 0; 169 | 170 | // Skip "n" bytes from the file. This is guaranteed to be no 171 | // slower that reading the same data, but may be faster. 172 | // 173 | // If end of file is reached, skipping will stop at the end of the 174 | // file, and Skip will return OK. 175 | // 176 | // REQUIRES: External synchronization 177 | virtual Status Skip(uint64_t n) = 0; 178 | 179 | private: 180 | // No copying allowed 181 | SequentialFile(const SequentialFile&); 182 | void operator=(const SequentialFile&); 183 | }; 184 | 185 | // A file abstraction for randomly reading the contents of a file. 186 | class RandomAccessFile { 187 | public: 188 | RandomAccessFile() { } 189 | virtual ~RandomAccessFile(); 190 | 191 | // Read up to "n" bytes from the file starting at "offset". 192 | // "scratch[0..n-1]" may be written by this routine. Sets "*result" 193 | // to the data that was read (including if fewer than "n" bytes were 194 | // successfully read). May set "*result" to point at data in 195 | // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when 196 | // "*result" is used. If an error was encountered, returns a non-OK 197 | // status. 198 | // 199 | // Safe for concurrent use by multiple threads. 200 | virtual Status Read(uint64_t offset, size_t n, Slice* result, 201 | char* scratch) const = 0; 202 | 203 | private: 204 | // No copying allowed 205 | RandomAccessFile(const RandomAccessFile&); 206 | void operator=(const RandomAccessFile&); 207 | }; 208 | 209 | // A file abstraction for sequential writing. The implementation 210 | // must provide buffering since callers may append small fragments 211 | // at a time to the file. 212 | class WritableFile { 213 | public: 214 | WritableFile() { } 215 | virtual ~WritableFile(); 216 | 217 | virtual Status Append(const Slice& data) = 0; 218 | virtual Status Close() = 0; 219 | virtual Status Flush() = 0; 220 | virtual Status Sync() = 0; 221 | 222 | private: 223 | // No copying allowed 224 | WritableFile(const WritableFile&); 225 | void operator=(const WritableFile&); 226 | }; 227 | 228 | // An interface for writing log messages. 229 | class Logger { 230 | public: 231 | Logger() { } 232 | virtual ~Logger(); 233 | 234 | // Write an entry to the log file with the specified format. 235 | virtual void Logv(const char* format, va_list ap) = 0; 236 | 237 | private: 238 | // No copying allowed 239 | Logger(const Logger&); 240 | void operator=(const Logger&); 241 | }; 242 | 243 | 244 | // Identifies a locked file. 245 | class FileLock { 246 | public: 247 | FileLock() { } 248 | virtual ~FileLock(); 249 | private: 250 | // No copying allowed 251 | FileLock(const FileLock&); 252 | void operator=(const FileLock&); 253 | }; 254 | 255 | // Log the specified data to *info_log if info_log is non-NULL. 256 | extern void Log(Logger* info_log, const char* format, ...) 257 | # if defined(__GNUC__) || defined(__clang__) 258 | __attribute__((__format__ (__printf__, 2, 3))) 259 | # endif 260 | ; 261 | 262 | // A utility routine: write "data" to the named file. 263 | extern Status WriteStringToFile(Env* env, const Slice& data, 264 | const std::string& fname); 265 | 266 | // A utility routine: read contents of named file into *data 267 | extern Status ReadFileToString(Env* env, const std::string& fname, 268 | std::string* data); 269 | 270 | // An implementation of Env that forwards all calls to another Env. 271 | // May be useful to clients who wish to override just part of the 272 | // functionality of another Env. 273 | class EnvWrapper : public Env { 274 | public: 275 | // Initialize an EnvWrapper that delegates all calls to *t 276 | explicit EnvWrapper(Env* t) : target_(t) { } 277 | virtual ~EnvWrapper(); 278 | 279 | // Return the target to which this Env forwards all calls 280 | Env* target() const { return target_; } 281 | 282 | // The following text is boilerplate that forwards all methods to target() 283 | Status NewSequentialFile(const std::string& f, SequentialFile** r) { 284 | return target_->NewSequentialFile(f, r); 285 | } 286 | Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { 287 | return target_->NewRandomAccessFile(f, r); 288 | } 289 | Status NewWritableFile(const std::string& f, WritableFile** r) { 290 | return target_->NewWritableFile(f, r); 291 | } 292 | bool FileExists(const std::string& f) { return target_->FileExists(f); } 293 | Status GetChildren(const std::string& dir, std::vector* r) { 294 | return target_->GetChildren(dir, r); 295 | } 296 | Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } 297 | Status CreateDir(const std::string& d) { return target_->CreateDir(d); } 298 | Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } 299 | Status GetFileSize(const std::string& f, uint64_t* s) { 300 | return target_->GetFileSize(f, s); 301 | } 302 | Status RenameFile(const std::string& s, const std::string& t) { 303 | return target_->RenameFile(s, t); 304 | } 305 | Status LockFile(const std::string& f, FileLock** l) { 306 | return target_->LockFile(f, l); 307 | } 308 | Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } 309 | void Schedule(void (*f)(void*), void* a) { 310 | return target_->Schedule(f, a); 311 | } 312 | void StartThread(void (*f)(void*), void* a) { 313 | return target_->StartThread(f, a); 314 | } 315 | virtual Status GetTestDirectory(std::string* path) { 316 | return target_->GetTestDirectory(path); 317 | } 318 | virtual Status NewLogger(const std::string& fname, Logger** result) { 319 | return target_->NewLogger(fname, result); 320 | } 321 | uint64_t NowMicros() { 322 | return target_->NowMicros(); 323 | } 324 | void SleepForMicroseconds(int micros) { 325 | target_->SleepForMicroseconds(micros); 326 | } 327 | private: 328 | Env* target_; 329 | }; 330 | 331 | } // namespace leveldb 332 | 333 | #endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ 334 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/filter_policy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // A database can be configured with a custom FilterPolicy object. 6 | // This object is responsible for creating a small filter from a set 7 | // of keys. These filters are stored in leveldb and are consulted 8 | // automatically by leveldb to decide whether or not to read some 9 | // information from disk. In many cases, a filter can cut down the 10 | // number of disk seeks form a handful to a single disk seek per 11 | // DB::Get() call. 12 | // 13 | // Most people will want to use the builtin bloom filter support (see 14 | // NewBloomFilterPolicy() below). 15 | 16 | #ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ 17 | #define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ 18 | 19 | #include 20 | 21 | namespace leveldb { 22 | 23 | class Slice; 24 | 25 | class FilterPolicy { 26 | public: 27 | virtual ~FilterPolicy(); 28 | 29 | // Return the name of this policy. Note that if the filter encoding 30 | // changes in an incompatible way, the name returned by this method 31 | // must be changed. Otherwise, old incompatible filters may be 32 | // passed to methods of this type. 33 | virtual const char* Name() const = 0; 34 | 35 | // keys[0,n-1] contains a list of keys (potentially with duplicates) 36 | // that are ordered according to the user supplied comparator. 37 | // Append a filter that summarizes keys[0,n-1] to *dst. 38 | // 39 | // Warning: do not change the initial contents of *dst. Instead, 40 | // append the newly constructed filter to *dst. 41 | virtual void CreateFilter(const Slice* keys, int n, std::string* dst) 42 | const = 0; 43 | 44 | // "filter" contains the data appended by a preceding call to 45 | // CreateFilter() on this class. This method must return true if 46 | // the key was in the list of keys passed to CreateFilter(). 47 | // This method may return true or false if the key was not on the 48 | // list, but it should aim to return false with a high probability. 49 | virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; 50 | }; 51 | 52 | // Return a new filter policy that uses a bloom filter with approximately 53 | // the specified number of bits per key. A good value for bits_per_key 54 | // is 10, which yields a filter with ~ 1% false positive rate. 55 | // 56 | // Callers must delete the result after any database that is using the 57 | // result has been closed. 58 | // 59 | // Note: if you are using a custom comparator that ignores some parts 60 | // of the keys being compared, you must not use NewBloomFilterPolicy() 61 | // and must provide your own FilterPolicy that also ignores the 62 | // corresponding parts of the keys. For example, if the comparator 63 | // ignores trailing spaces, it would be incorrect to use a 64 | // FilterPolicy (like NewBloomFilterPolicy) that does not ignore 65 | // trailing spaces in keys. 66 | extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); 67 | 68 | } 69 | 70 | #endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ 71 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/iterator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // An iterator yields a sequence of key/value pairs from a source. 6 | // The following class defines the interface. Multiple implementations 7 | // are provided by this library. In particular, iterators are provided 8 | // to access the contents of a Table or a DB. 9 | // 10 | // Multiple threads can invoke const methods on an Iterator without 11 | // external synchronization, but if any of the threads may call a 12 | // non-const method, all threads accessing the same Iterator must use 13 | // external synchronization. 14 | 15 | #ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ 16 | #define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ 17 | 18 | #include "leveldb/slice.h" 19 | #include "leveldb/status.h" 20 | 21 | namespace leveldb { 22 | 23 | class Iterator { 24 | public: 25 | Iterator(); 26 | virtual ~Iterator(); 27 | 28 | // An iterator is either positioned at a key/value pair, or 29 | // not valid. This method returns true iff the iterator is valid. 30 | virtual bool Valid() const = 0; 31 | 32 | // Position at the first key in the source. The iterator is Valid() 33 | // after this call iff the source is not empty. 34 | virtual void SeekToFirst() = 0; 35 | 36 | // Position at the last key in the source. The iterator is 37 | // Valid() after this call iff the source is not empty. 38 | virtual void SeekToLast() = 0; 39 | 40 | // Position at the first key in the source that is at or past target. 41 | // The iterator is Valid() after this call iff the source contains 42 | // an entry that comes at or past target. 43 | virtual void Seek(const Slice& target) = 0; 44 | 45 | // Moves to the next entry in the source. After this call, Valid() is 46 | // true iff the iterator was not positioned at the last entry in the source. 47 | // REQUIRES: Valid() 48 | virtual void Next() = 0; 49 | 50 | // Moves to the previous entry in the source. After this call, Valid() is 51 | // true iff the iterator was not positioned at the first entry in source. 52 | // REQUIRES: Valid() 53 | virtual void Prev() = 0; 54 | 55 | // Return the key for the current entry. The underlying storage for 56 | // the returned slice is valid only until the next modification of 57 | // the iterator. 58 | // REQUIRES: Valid() 59 | virtual Slice key() const = 0; 60 | 61 | // Return the value for the current entry. The underlying storage for 62 | // the returned slice is valid only until the next modification of 63 | // the iterator. 64 | // REQUIRES: Valid() 65 | virtual Slice value() const = 0; 66 | 67 | // If an error has occurred, return it. Else return an ok status. 68 | virtual Status status() const = 0; 69 | 70 | // Clients are allowed to register function/arg1/arg2 triples that 71 | // will be invoked when this iterator is destroyed. 72 | // 73 | // Note that unlike all of the preceding methods, this method is 74 | // not abstract and therefore clients should not override it. 75 | typedef void (*CleanupFunction)(void* arg1, void* arg2); 76 | void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); 77 | 78 | private: 79 | struct Cleanup { 80 | CleanupFunction function; 81 | void* arg1; 82 | void* arg2; 83 | Cleanup* next; 84 | }; 85 | Cleanup cleanup_; 86 | 87 | // No copying allowed 88 | Iterator(const Iterator&); 89 | void operator=(const Iterator&); 90 | }; 91 | 92 | // Return an empty iterator (yields nothing). 93 | extern Iterator* NewEmptyIterator(); 94 | 95 | // Return an empty iterator with the specified status. 96 | extern Iterator* NewErrorIterator(const Status& status); 97 | 98 | } // namespace leveldb 99 | 100 | #endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ 101 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/options.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ 6 | #define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ 7 | 8 | #include 9 | 10 | namespace leveldb { 11 | 12 | class Cache; 13 | class Comparator; 14 | class Env; 15 | class FilterPolicy; 16 | class Logger; 17 | class Snapshot; 18 | 19 | // DB contents are stored in a set of blocks, each of which holds a 20 | // sequence of key,value pairs. Each block may be compressed before 21 | // being stored in a file. The following enum describes which 22 | // compression method (if any) is used to compress a block. 23 | enum CompressionType { 24 | // NOTE: do not change the values of existing entries, as these are 25 | // part of the persistent format on disk. 26 | kNoCompression = 0x0, 27 | kSnappyCompression = 0x1 28 | }; 29 | 30 | // Options to control the behavior of a database (passed to DB::Open) 31 | struct Options { 32 | // ------------------- 33 | // Parameters that affect behavior 34 | 35 | // Comparator used to define the order of keys in the table. 36 | // Default: a comparator that uses lexicographic byte-wise ordering 37 | // 38 | // REQUIRES: The client must ensure that the comparator supplied 39 | // here has the same name and orders keys *exactly* the same as the 40 | // comparator provided to previous open calls on the same DB. 41 | const Comparator* comparator; 42 | 43 | // If true, the database will be created if it is missing. 44 | // Default: false 45 | bool create_if_missing; 46 | 47 | // If true, an error is raised if the database already exists. 48 | // Default: false 49 | bool error_if_exists; 50 | 51 | // If true, the implementation will do aggressive checking of the 52 | // data it is processing and will stop early if it detects any 53 | // errors. This may have unforeseen ramifications: for example, a 54 | // corruption of one DB entry may cause a large number of entries to 55 | // become unreadable or for the entire DB to become unopenable. 56 | // Default: false 57 | bool paranoid_checks; 58 | 59 | // Use the specified object to interact with the environment, 60 | // e.g. to read/write files, schedule background work, etc. 61 | // Default: Env::Default() 62 | Env* env; 63 | 64 | // Any internal progress/error information generated by the db will 65 | // be written to info_log if it is non-NULL, or to a file stored 66 | // in the same directory as the DB contents if info_log is NULL. 67 | // Default: NULL 68 | Logger* info_log; 69 | 70 | // ------------------- 71 | // Parameters that affect performance 72 | 73 | // Amount of data to build up in memory (backed by an unsorted log 74 | // on disk) before converting to a sorted on-disk file. 75 | // 76 | // Larger values increase performance, especially during bulk loads. 77 | // Up to two write buffers may be held in memory at the same time, 78 | // so you may wish to adjust this parameter to control memory usage. 79 | // Also, a larger write buffer will result in a longer recovery time 80 | // the next time the database is opened. 81 | // 82 | // Default: 4MB 83 | size_t write_buffer_size; 84 | 85 | // Number of open files that can be used by the DB. You may need to 86 | // increase this if your database has a large working set (budget 87 | // one open file per 2MB of working set). 88 | // 89 | // Default: 1000 90 | int max_open_files; 91 | 92 | // Control over blocks (user data is stored in a set of blocks, and 93 | // a block is the unit of reading from disk). 94 | 95 | // If non-NULL, use the specified cache for blocks. 96 | // If NULL, leveldb will automatically create and use an 8MB internal cache. 97 | // Default: NULL 98 | Cache* block_cache; 99 | 100 | // Approximate size of user data packed per block. Note that the 101 | // block size specified here corresponds to uncompressed data. The 102 | // actual size of the unit read from disk may be smaller if 103 | // compression is enabled. This parameter can be changed dynamically. 104 | // 105 | // Default: 4K 106 | size_t block_size; 107 | 108 | // Number of keys between restart points for delta encoding of keys. 109 | // This parameter can be changed dynamically. Most clients should 110 | // leave this parameter alone. 111 | // 112 | // Default: 16 113 | int block_restart_interval; 114 | 115 | // Compress blocks using the specified compression algorithm. This 116 | // parameter can be changed dynamically. 117 | // 118 | // Default: kSnappyCompression, which gives lightweight but fast 119 | // compression. 120 | // 121 | // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: 122 | // ~200-500MB/s compression 123 | // ~400-800MB/s decompression 124 | // Note that these speeds are significantly faster than most 125 | // persistent storage speeds, and therefore it is typically never 126 | // worth switching to kNoCompression. Even if the input data is 127 | // incompressible, the kSnappyCompression implementation will 128 | // efficiently detect that and will switch to uncompressed mode. 129 | CompressionType compression; 130 | 131 | // If non-NULL, use the specified filter policy to reduce disk reads. 132 | // Many applications will benefit from passing the result of 133 | // NewBloomFilterPolicy() here. 134 | // 135 | // Default: NULL 136 | const FilterPolicy* filter_policy; 137 | 138 | // Create an Options object with default values for all fields. 139 | Options(); 140 | }; 141 | 142 | // Options that control read operations 143 | struct ReadOptions { 144 | // If true, all data read from underlying storage will be 145 | // verified against corresponding checksums. 146 | // Default: false 147 | bool verify_checksums; 148 | 149 | // Should the data read for this iteration be cached in memory? 150 | // Callers may wish to set this field to false for bulk scans. 151 | // Default: true 152 | bool fill_cache; 153 | 154 | // If "snapshot" is non-NULL, read as of the supplied snapshot 155 | // (which must belong to the DB that is being read and which must 156 | // not have been released). If "snapshot" is NULL, use an implicit 157 | // snapshot of the state at the beginning of this read operation. 158 | // Default: NULL 159 | const Snapshot* snapshot; 160 | 161 | ReadOptions() 162 | : verify_checksums(false), 163 | fill_cache(true), 164 | snapshot(NULL) { 165 | } 166 | }; 167 | 168 | // Options that control write operations 169 | struct WriteOptions { 170 | // If true, the write will be flushed from the operating system 171 | // buffer cache (by calling WritableFile::Sync()) before the write 172 | // is considered complete. If this flag is true, writes will be 173 | // slower. 174 | // 175 | // If this flag is false, and the machine crashes, some recent 176 | // writes may be lost. Note that if it is just the process that 177 | // crashes (i.e., the machine does not reboot), no writes will be 178 | // lost even if sync==false. 179 | // 180 | // In other words, a DB write with sync==false has similar 181 | // crash semantics as the "write()" system call. A DB write 182 | // with sync==true has similar crash semantics to a "write()" 183 | // system call followed by "fsync()". 184 | // 185 | // Default: false 186 | bool sync; 187 | 188 | WriteOptions() 189 | : sync(false) { 190 | } 191 | }; 192 | 193 | } // namespace leveldb 194 | 195 | #endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ 196 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/slice.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // Slice is a simple structure containing a pointer into some external 6 | // storage and a size. The user of a Slice must ensure that the slice 7 | // is not used after the corresponding external storage has been 8 | // deallocated. 9 | // 10 | // Multiple threads can invoke const methods on a Slice without 11 | // external synchronization, but if any of the threads may call a 12 | // non-const method, all threads accessing the same Slice must use 13 | // external synchronization. 14 | 15 | #ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ 16 | #define STORAGE_LEVELDB_INCLUDE_SLICE_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace leveldb { 24 | 25 | class Slice { 26 | public: 27 | // Create an empty slice. 28 | Slice() : data_(""), size_(0) { } 29 | 30 | // Create a slice that refers to d[0,n-1]. 31 | Slice(const char* d, size_t n) : data_(d), size_(n) { } 32 | 33 | // Create a slice that refers to the contents of "s" 34 | Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } 35 | 36 | // Create a slice that refers to s[0,strlen(s)-1] 37 | Slice(const char* s) : data_(s), size_(strlen(s)) { } 38 | 39 | // Return a pointer to the beginning of the referenced data 40 | const char* data() const { return data_; } 41 | 42 | // Return the length (in bytes) of the referenced data 43 | size_t size() const { return size_; } 44 | 45 | // Return true iff the length of the referenced data is zero 46 | bool empty() const { return size_ == 0; } 47 | 48 | // Return the ith byte in the referenced data. 49 | // REQUIRES: n < size() 50 | char operator[](size_t n) const { 51 | assert(n < size()); 52 | return data_[n]; 53 | } 54 | 55 | // Change this slice to refer to an empty array 56 | void clear() { data_ = ""; size_ = 0; } 57 | 58 | // Drop the first "n" bytes from this slice. 59 | void remove_prefix(size_t n) { 60 | assert(n <= size()); 61 | data_ += n; 62 | size_ -= n; 63 | } 64 | 65 | // Return a string that contains the copy of the referenced data. 66 | std::string ToString() const { return std::string(data_, size_); } 67 | 68 | // Three-way comparison. Returns value: 69 | // < 0 iff "*this" < "b", 70 | // == 0 iff "*this" == "b", 71 | // > 0 iff "*this" > "b" 72 | int compare(const Slice& b) const; 73 | 74 | // Return true iff "x" is a prefix of "*this" 75 | bool starts_with(const Slice& x) const { 76 | return ((size_ >= x.size_) && 77 | (memcmp(data_, x.data_, x.size_) == 0)); 78 | } 79 | 80 | private: 81 | const char* data_; 82 | size_t size_; 83 | 84 | // Intentionally copyable 85 | }; 86 | 87 | inline bool operator==(const Slice& x, const Slice& y) { 88 | return ((x.size() == y.size()) && 89 | (memcmp(x.data(), y.data(), x.size()) == 0)); 90 | } 91 | 92 | inline bool operator!=(const Slice& x, const Slice& y) { 93 | return !(x == y); 94 | } 95 | 96 | inline int Slice::compare(const Slice& b) const { 97 | const size_t min_len = (size_ < b.size_) ? size_ : b.size_; 98 | int r = memcmp(data_, b.data_, min_len); 99 | if (r == 0) { 100 | if (size_ < b.size_) r = -1; 101 | else if (size_ > b.size_) r = +1; 102 | } 103 | return r; 104 | } 105 | 106 | } // namespace leveldb 107 | 108 | 109 | #endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ 110 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/status.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // A Status encapsulates the result of an operation. It may indicate success, 6 | // or it may indicate an error with an associated error message. 7 | // 8 | // Multiple threads can invoke const methods on a Status without 9 | // external synchronization, but if any of the threads may call a 10 | // non-const method, all threads accessing the same Status must use 11 | // external synchronization. 12 | 13 | #ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ 14 | #define STORAGE_LEVELDB_INCLUDE_STATUS_H_ 15 | 16 | #include 17 | #include "leveldb/slice.h" 18 | 19 | namespace leveldb { 20 | 21 | class Status { 22 | public: 23 | // Create a success status. 24 | Status() : state_(NULL) { } 25 | ~Status() { delete[] state_; } 26 | 27 | // Copy the specified status. 28 | Status(const Status& s); 29 | void operator=(const Status& s); 30 | 31 | // Return a success status. 32 | static Status OK() { return Status(); } 33 | 34 | // Return error status of an appropriate type. 35 | static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { 36 | return Status(kNotFound, msg, msg2); 37 | } 38 | static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { 39 | return Status(kCorruption, msg, msg2); 40 | } 41 | static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { 42 | return Status(kNotSupported, msg, msg2); 43 | } 44 | static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { 45 | return Status(kInvalidArgument, msg, msg2); 46 | } 47 | static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { 48 | return Status(kIOError, msg, msg2); 49 | } 50 | 51 | // Returns true iff the status indicates success. 52 | bool ok() const { return (state_ == NULL); } 53 | 54 | // Returns true iff the status indicates a NotFound error. 55 | bool IsNotFound() const { return code() == kNotFound; } 56 | 57 | // Returns true iff the status indicates a Corruption error. 58 | bool IsCorruption() const { return code() == kCorruption; } 59 | 60 | // Returns true iff the status indicates an IOError. 61 | bool IsIOError() const { return code() == kIOError; } 62 | 63 | // Return a string representation of this status suitable for printing. 64 | // Returns the string "OK" for success. 65 | std::string ToString() const; 66 | 67 | private: 68 | // OK status has a NULL state_. Otherwise, state_ is a new[] array 69 | // of the following form: 70 | // state_[0..3] == length of message 71 | // state_[4] == code 72 | // state_[5..] == message 73 | const char* state_; 74 | 75 | enum Code { 76 | kOk = 0, 77 | kNotFound = 1, 78 | kCorruption = 2, 79 | kNotSupported = 3, 80 | kInvalidArgument = 4, 81 | kIOError = 5 82 | }; 83 | 84 | Code code() const { 85 | return (state_ == NULL) ? kOk : static_cast(state_[4]); 86 | } 87 | 88 | Status(Code code, const Slice& msg, const Slice& msg2); 89 | static const char* CopyState(const char* s); 90 | }; 91 | 92 | inline Status::Status(const Status& s) { 93 | state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); 94 | } 95 | inline void Status::operator=(const Status& s) { 96 | // The following condition catches both aliasing (when this == &s), 97 | // and the common case where both s and *this are ok. 98 | if (state_ != s.state_) { 99 | delete[] state_; 100 | state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); 101 | } 102 | } 103 | 104 | } // namespace leveldb 105 | 106 | #endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ 107 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/table.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ 6 | #define STORAGE_LEVELDB_INCLUDE_TABLE_H_ 7 | 8 | #include 9 | #include "leveldb/iterator.h" 10 | 11 | namespace leveldb { 12 | 13 | class Block; 14 | class BlockHandle; 15 | class Footer; 16 | struct Options; 17 | class RandomAccessFile; 18 | struct ReadOptions; 19 | class TableCache; 20 | 21 | // A Table is a sorted map from strings to strings. Tables are 22 | // immutable and persistent. A Table may be safely accessed from 23 | // multiple threads without external synchronization. 24 | class Table { 25 | public: 26 | // Attempt to open the table that is stored in bytes [0..file_size) 27 | // of "file", and read the metadata entries necessary to allow 28 | // retrieving data from the table. 29 | // 30 | // If successful, returns ok and sets "*table" to the newly opened 31 | // table. The client should delete "*table" when no longer needed. 32 | // If there was an error while initializing the table, sets "*table" 33 | // to NULL and returns a non-ok status. Does not take ownership of 34 | // "*source", but the client must ensure that "source" remains live 35 | // for the duration of the returned table's lifetime. 36 | // 37 | // *file must remain live while this Table is in use. 38 | static Status Open(const Options& options, 39 | RandomAccessFile* file, 40 | uint64_t file_size, 41 | Table** table); 42 | 43 | ~Table(); 44 | 45 | // Returns a new iterator over the table contents. 46 | // The result of NewIterator() is initially invalid (caller must 47 | // call one of the Seek methods on the iterator before using it). 48 | Iterator* NewIterator(const ReadOptions&) const; 49 | 50 | // Given a key, return an approximate byte offset in the file where 51 | // the data for that key begins (or would begin if the key were 52 | // present in the file). The returned value is in terms of file 53 | // bytes, and so includes effects like compression of the underlying data. 54 | // E.g., the approximate offset of the last key in the table will 55 | // be close to the file length. 56 | uint64_t ApproximateOffsetOf(const Slice& key) const; 57 | 58 | private: 59 | struct Rep; 60 | Rep* rep_; 61 | 62 | explicit Table(Rep* rep) { rep_ = rep; } 63 | static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); 64 | 65 | // Calls (*handle_result)(arg, ...) with the entry found after a call 66 | // to Seek(key). May not make such a call if filter policy says 67 | // that key is not present. 68 | friend class TableCache; 69 | Status InternalGet( 70 | const ReadOptions&, const Slice& key, 71 | void* arg, 72 | void (*handle_result)(void* arg, const Slice& k, const Slice& v)); 73 | 74 | 75 | void ReadMeta(const Footer& footer); 76 | void ReadFilter(const Slice& filter_handle_value); 77 | 78 | // No copying allowed 79 | Table(const Table&); 80 | void operator=(const Table&); 81 | }; 82 | 83 | } // namespace leveldb 84 | 85 | #endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ 86 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/table_builder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // TableBuilder provides the interface used to build a Table 6 | // (an immutable and sorted map from keys to values). 7 | // 8 | // Multiple threads can invoke const methods on a TableBuilder without 9 | // external synchronization, but if any of the threads may call a 10 | // non-const method, all threads accessing the same TableBuilder must use 11 | // external synchronization. 12 | 13 | #ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ 14 | #define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ 15 | 16 | #include 17 | #include "leveldb/options.h" 18 | #include "leveldb/status.h" 19 | 20 | namespace leveldb { 21 | 22 | class BlockBuilder; 23 | class BlockHandle; 24 | class WritableFile; 25 | 26 | class TableBuilder { 27 | public: 28 | // Create a builder that will store the contents of the table it is 29 | // building in *file. Does not close the file. It is up to the 30 | // caller to close the file after calling Finish(). 31 | TableBuilder(const Options& options, WritableFile* file); 32 | 33 | // REQUIRES: Either Finish() or Abandon() has been called. 34 | ~TableBuilder(); 35 | 36 | // Change the options used by this builder. Note: only some of the 37 | // option fields can be changed after construction. If a field is 38 | // not allowed to change dynamically and its value in the structure 39 | // passed to the constructor is different from its value in the 40 | // structure passed to this method, this method will return an error 41 | // without changing any fields. 42 | Status ChangeOptions(const Options& options); 43 | 44 | // Add key,value to the table being constructed. 45 | // REQUIRES: key is after any previously added key according to comparator. 46 | // REQUIRES: Finish(), Abandon() have not been called 47 | void Add(const Slice& key, const Slice& value); 48 | 49 | // Advanced operation: flush any buffered key/value pairs to file. 50 | // Can be used to ensure that two adjacent entries never live in 51 | // the same data block. Most clients should not need to use this method. 52 | // REQUIRES: Finish(), Abandon() have not been called 53 | void Flush(); 54 | 55 | // Return non-ok iff some error has been detected. 56 | Status status() const; 57 | 58 | // Finish building the table. Stops using the file passed to the 59 | // constructor after this function returns. 60 | // REQUIRES: Finish(), Abandon() have not been called 61 | Status Finish(); 62 | 63 | // Indicate that the contents of this builder should be abandoned. Stops 64 | // using the file passed to the constructor after this function returns. 65 | // If the caller is not going to call Finish(), it must call Abandon() 66 | // before destroying this builder. 67 | // REQUIRES: Finish(), Abandon() have not been called 68 | void Abandon(); 69 | 70 | // Number of calls to Add() so far. 71 | uint64_t NumEntries() const; 72 | 73 | // Size of the file generated so far. If invoked after a successful 74 | // Finish() call, returns the size of the final generated file. 75 | uint64_t FileSize() const; 76 | 77 | private: 78 | bool ok() const { return status().ok(); } 79 | void WriteBlock(BlockBuilder* block, BlockHandle* handle); 80 | void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); 81 | 82 | struct Rep; 83 | Rep* rep_; 84 | 85 | // No copying allowed 86 | TableBuilder(const TableBuilder&); 87 | void operator=(const TableBuilder&); 88 | }; 89 | 90 | } // namespace leveldb 91 | 92 | #endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ 93 | -------------------------------------------------------------------------------- /Libraries/leveldb/include/leveldb/write_batch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | // 5 | // WriteBatch holds a collection of updates to apply atomically to a DB. 6 | // 7 | // The updates are applied in the order in which they are added 8 | // to the WriteBatch. For example, the value of "key" will be "v3" 9 | // after the following batch is written: 10 | // 11 | // batch.Put("key", "v1"); 12 | // batch.Delete("key"); 13 | // batch.Put("key", "v2"); 14 | // batch.Put("key", "v3"); 15 | // 16 | // Multiple threads can invoke const methods on a WriteBatch without 17 | // external synchronization, but if any of the threads may call a 18 | // non-const method, all threads accessing the same WriteBatch must use 19 | // external synchronization. 20 | 21 | #ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ 22 | #define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ 23 | 24 | #include 25 | #include "leveldb/status.h" 26 | 27 | namespace leveldb { 28 | 29 | class Slice; 30 | 31 | class WriteBatch { 32 | public: 33 | WriteBatch(); 34 | ~WriteBatch(); 35 | 36 | // Store the mapping "key->value" in the database. 37 | void Put(const Slice& key, const Slice& value); 38 | 39 | // If the database contains a mapping for "key", erase it. Else do nothing. 40 | void Delete(const Slice& key); 41 | 42 | // Clear all updates buffered in this batch. 43 | void Clear(); 44 | 45 | // Support for iterating over the contents of a batch. 46 | class Handler { 47 | public: 48 | virtual ~Handler(); 49 | virtual void Put(const Slice& key, const Slice& value) = 0; 50 | virtual void Delete(const Slice& key) = 0; 51 | }; 52 | Status Iterate(Handler* handler) const; 53 | 54 | private: 55 | friend class WriteBatchInternal; 56 | 57 | std::string rep_; // See comment in write_batch.cc for the format of rep_ 58 | 59 | // Intentionally copyable 60 | }; 61 | 62 | } // namespace leveldb 63 | 64 | #endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ 65 | -------------------------------------------------------------------------------- /Libraries/leveldb/libleveldb.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-archive/Objective-LevelDB/707abcfee4eb972a60b76860e02373cbbbd5de46/Libraries/leveldb/libleveldb.a -------------------------------------------------------------------------------- /Objective-LevelDB+Uber.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Objective-LevelDB+Uber' 3 | s.version = '2.1.6' 4 | s.license = 'MIT' 5 | s.summary = 'A feature-complete wrapper for LevelDB in Objective-C.' 6 | s.description = 'This is a feature-complete wrapper for Google\'s LevelDB. LevelDB is a fast key-value store written by Google.' 7 | s.homepage = 'https://github.com/uber/Objective-LevelDB' 8 | s.authors = 'Michael Hoisie', 'Mathieu D\'Amours' 9 | 10 | s.ios.deployment_target = '5.0' 11 | s.osx.deployment_target = '10.7' 12 | 13 | s.source = { 14 | :git => 'https://github.com/uber/Objective-LevelDB.git', 15 | :tag => s.version.to_s 16 | } 17 | 18 | s.source_files = 'Classes/*.{h,m,mm}' 19 | s.requires_arc = false 20 | 21 | s.subspec "leveldb" do |sp| 22 | sp.source_files = 'Libraries/leveldb/**/*.h' 23 | sp.vendored_libraries = 'Libraries/leveldb/libleveldb.a' 24 | sp.header_dir = 'leveldb' 25 | sp.xcconfig = { 26 | 'CC' => 'clang', 27 | 'CXX' => 'clang++', 28 | 'OTHER_LDFLAGS' => '-lc++' 29 | } 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /Objective-LevelDB.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EA4AA1991AA940C100F06E51 /* LDBSnapshot.mm in Sources */ = {isa = PBXBuildFile; fileRef = EA4AA1651AA9401200F06E51 /* LDBSnapshot.mm */; }; 11 | EA4AA19A1AA940C100F06E51 /* LDBWriteBatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = EA4AA1671AA9401200F06E51 /* LDBWriteBatch.mm */; }; 12 | EA4AA19B1AA940C100F06E51 /* LevelDB.mm in Sources */ = {isa = PBXBuildFile; fileRef = EA4AA1691AA9401200F06E51 /* LevelDB.mm */; }; 13 | EA4AA19C1AA940D300F06E51 /* libleveldb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EA4AA17C1AA9401200F06E51 /* libleveldb.a */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | EA4AA17F1AA9405200F06E51 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = "include/$(PRODUCT_NAME)"; 21 | dstSubfolderSpec = 16; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 0; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | EA4AA1631AA9401200F06E51 /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; 30 | EA4AA1641AA9401200F06E51 /* LDBSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDBSnapshot.h; sourceTree = ""; }; 31 | EA4AA1651AA9401200F06E51 /* LDBSnapshot.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LDBSnapshot.mm; sourceTree = ""; }; 32 | EA4AA1661AA9401200F06E51 /* LDBWriteBatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDBWriteBatch.h; sourceTree = ""; }; 33 | EA4AA1671AA9401200F06E51 /* LDBWriteBatch.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LDBWriteBatch.mm; sourceTree = ""; }; 34 | EA4AA1681AA9401200F06E51 /* LevelDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LevelDB.h; sourceTree = ""; }; 35 | EA4AA1691AA9401200F06E51 /* LevelDB.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LevelDB.mm; sourceTree = ""; }; 36 | EA4AA16E1AA9401200F06E51 /* c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = c.h; sourceTree = ""; }; 37 | EA4AA16F1AA9401200F06E51 /* cache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cache.h; sourceTree = ""; }; 38 | EA4AA1701AA9401200F06E51 /* comparator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = comparator.h; sourceTree = ""; }; 39 | EA4AA1711AA9401200F06E51 /* db.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = db.h; sourceTree = ""; }; 40 | EA4AA1721AA9401200F06E51 /* dumpfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dumpfile.h; sourceTree = ""; }; 41 | EA4AA1731AA9401200F06E51 /* env.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = env.h; sourceTree = ""; }; 42 | EA4AA1741AA9401200F06E51 /* filter_policy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = filter_policy.h; sourceTree = ""; }; 43 | EA4AA1751AA9401200F06E51 /* iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = ""; }; 44 | EA4AA1761AA9401200F06E51 /* options.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = ""; }; 45 | EA4AA1771AA9401200F06E51 /* slice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = slice.h; sourceTree = ""; }; 46 | EA4AA1781AA9401200F06E51 /* status.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = status.h; sourceTree = ""; }; 47 | EA4AA1791AA9401200F06E51 /* table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; 48 | EA4AA17A1AA9401200F06E51 /* table_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = table_builder.h; sourceTree = ""; }; 49 | EA4AA17B1AA9401200F06E51 /* write_batch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = write_batch.h; sourceTree = ""; }; 50 | EA4AA17C1AA9401200F06E51 /* libleveldb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libleveldb.a; sourceTree = ""; }; 51 | EA4AA1811AA9405200F06E51 /* libObjective-LevelDB.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libObjective-LevelDB.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | EA4AA19F1AAA239C00F06E51 /* Objective-LevelDB.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Objective-LevelDB.xcconfig"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | EA4AA17E1AA9405200F06E51 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | EA4AA19C1AA940D300F06E51 /* libleveldb.a in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | EA4AA15B1AA93FCE00F06E51 = { 68 | isa = PBXGroup; 69 | children = ( 70 | EA4AA19E1AAA239C00F06E51 /* Configs */, 71 | EA4AA1621AA9401200F06E51 /* Classes */, 72 | EA4AA16A1AA9401200F06E51 /* Libraries */, 73 | EA4AA1821AA9405200F06E51 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | EA4AA1621AA9401200F06E51 /* Classes */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | EA4AA1631AA9401200F06E51 /* Common.h */, 81 | EA4AA1641AA9401200F06E51 /* LDBSnapshot.h */, 82 | EA4AA1651AA9401200F06E51 /* LDBSnapshot.mm */, 83 | EA4AA1661AA9401200F06E51 /* LDBWriteBatch.h */, 84 | EA4AA1671AA9401200F06E51 /* LDBWriteBatch.mm */, 85 | EA4AA1681AA9401200F06E51 /* LevelDB.h */, 86 | EA4AA1691AA9401200F06E51 /* LevelDB.mm */, 87 | ); 88 | path = Classes; 89 | sourceTree = ""; 90 | }; 91 | EA4AA16A1AA9401200F06E51 /* Libraries */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | EA4AA16B1AA9401200F06E51 /* leveldb */, 95 | ); 96 | path = Libraries; 97 | sourceTree = ""; 98 | }; 99 | EA4AA16B1AA9401200F06E51 /* leveldb */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | EA4AA16C1AA9401200F06E51 /* include */, 103 | EA4AA17C1AA9401200F06E51 /* libleveldb.a */, 104 | ); 105 | path = leveldb; 106 | sourceTree = ""; 107 | }; 108 | EA4AA16C1AA9401200F06E51 /* include */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | EA4AA16D1AA9401200F06E51 /* leveldb */, 112 | ); 113 | path = include; 114 | sourceTree = ""; 115 | }; 116 | EA4AA16D1AA9401200F06E51 /* leveldb */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | EA4AA16E1AA9401200F06E51 /* c.h */, 120 | EA4AA16F1AA9401200F06E51 /* cache.h */, 121 | EA4AA1701AA9401200F06E51 /* comparator.h */, 122 | EA4AA1711AA9401200F06E51 /* db.h */, 123 | EA4AA1721AA9401200F06E51 /* dumpfile.h */, 124 | EA4AA1731AA9401200F06E51 /* env.h */, 125 | EA4AA1741AA9401200F06E51 /* filter_policy.h */, 126 | EA4AA1751AA9401200F06E51 /* iterator.h */, 127 | EA4AA1761AA9401200F06E51 /* options.h */, 128 | EA4AA1771AA9401200F06E51 /* slice.h */, 129 | EA4AA1781AA9401200F06E51 /* status.h */, 130 | EA4AA1791AA9401200F06E51 /* table.h */, 131 | EA4AA17A1AA9401200F06E51 /* table_builder.h */, 132 | EA4AA17B1AA9401200F06E51 /* write_batch.h */, 133 | ); 134 | path = leveldb; 135 | sourceTree = ""; 136 | }; 137 | EA4AA1821AA9405200F06E51 /* Products */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | EA4AA1811AA9405200F06E51 /* libObjective-LevelDB.a */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | EA4AA19E1AAA239C00F06E51 /* Configs */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | EA4AA19F1AAA239C00F06E51 /* Objective-LevelDB.xcconfig */, 149 | ); 150 | path = Configs; 151 | sourceTree = ""; 152 | }; 153 | /* End PBXGroup section */ 154 | 155 | /* Begin PBXNativeTarget section */ 156 | EA4AA1801AA9405200F06E51 /* Objective-LevelDB */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = EA4AA1931AA9405200F06E51 /* Build configuration list for PBXNativeTarget "Objective-LevelDB" */; 159 | buildPhases = ( 160 | EA4AA17D1AA9405200F06E51 /* Sources */, 161 | EA4AA17E1AA9405200F06E51 /* Frameworks */, 162 | EA4AA17F1AA9405200F06E51 /* CopyFiles */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | ); 168 | name = "Objective-LevelDB"; 169 | productName = "Objective-LevelDB"; 170 | productReference = EA4AA1811AA9405200F06E51 /* libObjective-LevelDB.a */; 171 | productType = "com.apple.product-type.library.static"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | EA4AA15C1AA93FCE00F06E51 /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastUpgradeCheck = 0610; 180 | TargetAttributes = { 181 | EA4AA1801AA9405200F06E51 = { 182 | CreatedOnToolsVersion = 6.1.1; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = EA4AA15F1AA93FCE00F06E51 /* Build configuration list for PBXProject "Objective-LevelDB" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | ); 193 | mainGroup = EA4AA15B1AA93FCE00F06E51; 194 | productRefGroup = EA4AA1821AA9405200F06E51 /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | EA4AA1801AA9405200F06E51 /* Objective-LevelDB */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXSourcesBuildPhase section */ 204 | EA4AA17D1AA9405200F06E51 /* Sources */ = { 205 | isa = PBXSourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | EA4AA1991AA940C100F06E51 /* LDBSnapshot.mm in Sources */, 209 | EA4AA19A1AA940C100F06E51 /* LDBWriteBatch.mm in Sources */, 210 | EA4AA19B1AA940C100F06E51 /* LevelDB.mm in Sources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXSourcesBuildPhase section */ 215 | 216 | /* Begin XCBuildConfiguration section */ 217 | EA4AA1601AA93FCE00F06E51 /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 221 | }; 222 | name = Debug; 223 | }; 224 | EA4AA1611AA93FCE00F06E51 /* Release */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 228 | }; 229 | name = Release; 230 | }; 231 | EA4AA1941AA9405200F06E51 /* Debug */ = { 232 | isa = XCBuildConfiguration; 233 | baseConfigurationReference = EA4AA19F1AAA239C00F06E51 /* Objective-LevelDB.xcconfig */; 234 | buildSettings = { 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | COPY_PHASE_STRIP = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_DYNAMIC_NO_PIC = NO; 251 | GCC_OPTIMIZATION_LEVEL = 0; 252 | GCC_PREPROCESSOR_DEFINITIONS = ( 253 | "DEBUG=1", 254 | "$(inherited)", 255 | ); 256 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 259 | GCC_WARN_UNDECLARED_SELECTOR = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 261 | GCC_WARN_UNUSED_FUNCTION = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 264 | MTL_ENABLE_DEBUG_INFO = YES; 265 | ONLY_ACTIVE_ARCH = YES; 266 | OTHER_LDFLAGS = "-ObjC"; 267 | PRODUCT_NAME = "$(TARGET_NAME)"; 268 | SDKROOT = iphoneos; 269 | SKIP_INSTALL = YES; 270 | }; 271 | name = Debug; 272 | }; 273 | EA4AA1951AA9405200F06E51 /* Release */ = { 274 | isa = XCBuildConfiguration; 275 | baseConfigurationReference = EA4AA19F1AAA239C00F06E51 /* Objective-LevelDB.xcconfig */; 276 | buildSettings = { 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | COPY_PHASE_STRIP = YES; 290 | ENABLE_NS_ASSERTIONS = NO; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 294 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 295 | GCC_WARN_UNDECLARED_SELECTOR = YES; 296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 297 | GCC_WARN_UNUSED_FUNCTION = YES; 298 | GCC_WARN_UNUSED_VARIABLE = YES; 299 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 300 | MTL_ENABLE_DEBUG_INFO = NO; 301 | OTHER_LDFLAGS = "-ObjC"; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | SDKROOT = iphoneos; 304 | SKIP_INSTALL = YES; 305 | VALIDATE_PRODUCT = YES; 306 | }; 307 | name = Release; 308 | }; 309 | /* End XCBuildConfiguration section */ 310 | 311 | /* Begin XCConfigurationList section */ 312 | EA4AA15F1AA93FCE00F06E51 /* Build configuration list for PBXProject "Objective-LevelDB" */ = { 313 | isa = XCConfigurationList; 314 | buildConfigurations = ( 315 | EA4AA1601AA93FCE00F06E51 /* Debug */, 316 | EA4AA1611AA93FCE00F06E51 /* Release */, 317 | ); 318 | defaultConfigurationIsVisible = 0; 319 | defaultConfigurationName = Release; 320 | }; 321 | EA4AA1931AA9405200F06E51 /* Build configuration list for PBXNativeTarget "Objective-LevelDB" */ = { 322 | isa = XCConfigurationList; 323 | buildConfigurations = ( 324 | EA4AA1941AA9405200F06E51 /* Debug */, 325 | EA4AA1951AA9405200F06E51 /* Release */, 326 | ); 327 | defaultConfigurationIsVisible = 0; 328 | }; 329 | /* End XCConfigurationList section */ 330 | }; 331 | rootObject = EA4AA15C1AA93FCE00F06E51 /* Project object */; 332 | } 333 | -------------------------------------------------------------------------------- /Objective-LevelDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Objective-LevelDB.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | An Objective-C database library built over [Google's LevelDB](http://code.google.com/p/leveldb), a fast embedded key-value store written by Google. 4 | 5 | ## Installation 6 | 7 | By far, the easiest way to integrate this library in your project is by using [CocoaPods][1]. 8 | 9 | 1. Have [Cocoapods][1] installed, if you don't already 10 | 2. In your Podfile, add the line 11 | 12 | pod 'Objective-LevelDB' 13 | 14 | 3. Run `pod install` 15 | 4. Make something awesome. 16 | 17 | ## How to use 18 | 19 | #### Creating/Opening a database file on disk 20 | 21 | ```objective-c 22 | LevelDB *ldb = [LevelDB databaseInLibraryWithName:@"test.ldb"]; 23 | ``` 24 | 25 | ##### Setup Encoder/Decoder blocks 26 | 27 | By default, any object you store will be encoded and decoded using `NSKeyedArchiver`/`NSKeyedUnarchiver`. You can customize this by providing `encoder` and `decoder` blocks, like this: 28 | 29 | ```objective-c 30 | ldb.encoder = ^ NSData * (LevelDBKey *key, id object) { 31 | // return some data, given an object 32 | } 33 | ldb.decoder = ^ id (LevelDBKey *key, NSData * data) { 34 | // return an object, given some data 35 | } 36 | ``` 37 | 38 | ##### NSMutableDictionary-like API 39 | 40 | ```objective-c 41 | ldb[@"string_test"] = @"laval"; // same as: 42 | [ldb setObject:@"laval" forKey:@"string_test"]; 43 | 44 | NSLog(@"String Value: %@", ldb[@"string_test"]); // same as: 45 | NSLog(@"String Value: %@", [ldb objectForKey:@"string_test"]); 46 | 47 | [ldb setObject:@{@"key1" : @"val1", @"key2" : @"val2"} forKey:@"dict_test"]; 48 | NSLog(@"Dictionary Value: %@", [ldb objectForKey:@"dict_test"]); 49 | 50 | ``` 51 | All available methods can be found in its [header file](https://github.com/matehat/Objective-LevelDB/blob/master/Classes/LevelDB.h) (documented). 52 | 53 | ##### Enumeration 54 | 55 | ```objective-c 56 | [ldb enumerateKeysAndObjectsUsingBlock:^(LevelDBKey *key, id value, BOOL *stop) { 57 | // This step is necessary since the key could be a string or raw data (use NSDataFromLevelDBKey in that case) 58 | NSString *keyString = NSStringFromLevelDBKey(key); // Assumes UTF-8 encoding 59 | // Do something clever 60 | }]; 61 | 62 | // Enumerate with options 63 | [ldb enumerateKeysAndObjectsBackward:TRUE 64 | lazily:TRUE // Block below will have a block(void) instead of id argument for value 65 | startingAtKey:someKey // Start iteration there (NSString or NSData) 66 | filteredByPredicate:predicate // Only iterate over values matching NSPredicate 67 | andPrefix:prefix // Only iterate over keys prefixed with something 68 | usingBlock:^(LevelDBKey *key, void(^valueGetter)(void), BOOL *stop) { 69 | 70 | NSString *keyString = NSStringFromLevelDBKey(key); 71 | 72 | // If we had wanted the value directly instead of a valueGetter block, we would've set the 73 | // above 'lazily' argument to FALSE 74 | id value = valueGetter(); 75 | }] 76 | ``` 77 | More iteration methods are available, just have a look at the [header section](https://github.com/matehat/Objective-LevelDB/blob/master/Classes/LevelDB.h) 78 | 79 | ##### Snapshots, NSDictionary-like API (immutable) 80 | 81 | A snapshot is a readonly interface to the database, permanently reflecting the state of 82 | the database when it was created, even if the database changes afterwards. 83 | 84 | ```objective-c 85 | LDBSnapshot *snap = [ldb newSnapshot]; // You get ownership of this variable, so in non-ARC projects, 86 | // you'll need to release/autorelease it eventually 87 | [ldb removeObjectForKey:@"string_test"]; 88 | 89 | // The result of these calls will reflect the state of ldb when the snapshot was taken 90 | NSLog(@"String Value: %@", [snap objectForKey:@"string_test"]); 91 | NSLog(@"Dictionary Value: %@", [ldb objectForKey:@"dict_test"]); 92 | ``` 93 | 94 | All available methods can be found in its [header file](https://github.com/matehat/Objective-LevelDB/blob/master/Classes/LDBSnapshot.h) 95 | 96 | ##### Write batches, atomic sets of updates 97 | 98 | Write batches are a mutable proxy to a `LevelDB` database, accumulating updates 99 | without applying them, until you do using `-[LDBWritebatch apply]` 100 | 101 | ```objective-c 102 | LDBWritebatch *wb = [ldb newWritebatch]; 103 | [wb setObject:@{ @"foo" : @"bar" } forKey: @"another_test"]; 104 | [wb removeObjectForKey:@"dict_test"]; 105 | 106 | // Those changes aren't yet applied to ldb 107 | // To apply them in batch, 108 | [wb apply]; 109 | ``` 110 | 111 | All available methods can be found in its [header file](https://github.com/matehat/Objective-LevelDB/blob/master/Classes/LDBWriteBatch.h) 112 | 113 | ##### LevelDB options 114 | 115 | ```objective-c 116 | // The following values are the default 117 | LevelDBOptions options = [LevelDB makeOptions]; 118 | options.createIfMissing = true; 119 | options.errorIfExists = false; 120 | options.paranoidCheck = false; 121 | options.compression = true; 122 | options.filterPolicy = 0; // Size in bits per key, allocated for a bloom filter, used in testing presence of key 123 | options.cacheSize = 0; // Size in bytes, allocated for a LRU cache used for speeding up lookups 124 | 125 | // Then, you can provide it when initializing a db instance. 126 | LevelDB *ldb = [LevelDB databaseInLibraryWithName:@"test.ldb" andOptions:options]; 127 | ``` 128 | 129 | ##### Per-request options 130 | 131 | ```objective-c 132 | db.safe = true; // Make sure to data was actually written to disk before returning from write operations. 133 | [ldb setObject:@"laval" forKey:@"string_test"]; 134 | [ldb setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1", @"val2", @"key2", nil] forKey:@"dict_test"]; 135 | db.safe = false; // Switch back to default 136 | 137 | db.useCache = false; // Do not use DB cache when reading data (default to true); 138 | ``` 139 | 140 | ##### Concurrency 141 | 142 | As [Google's documentation states][2], updates and reads from a leveldb instance do not require external synchronization 143 | to be thread-safe. Write batches do, and we've taken care of it, by isolating every `LDBWritebatch` it inside a serial dispatch 144 | queue, and making every request dispatch *synchronously* to it. So use it from wherever you want, it'll just work. 145 | 146 | However, if you are using something like JSONKit for encoding data to JSON in the database, and you are clever enough to 147 | preallocate a `JSONDecoder` instance for all data decoding, beware that this particular object is *not* thread-safe, and you will 148 | need to take care of it manually. 149 | 150 | ### Testing 151 | 152 | If you want to run the tests, you will need XCode 5, as the test suite uses the new XCTest. 153 | 154 | Clone this repository and, once in it, 155 | 156 | ```bash 157 | ./setup-test.sh 158 | cd Tests && open Objective-LevelDB.xcworkspace 159 | ``` 160 | 161 | Currently, all tests were setup to work with the iOS test suite. 162 | 163 | ### License 164 | 165 | Distributed under the [MIT license](LICENSE) 166 | 167 | [1]: http://cocoapods.org 168 | [2]: http://leveldb.googlecode.com/svn/trunk/doc/index.html 169 | -------------------------------------------------------------------------------- /Tests/BaseTestClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTestClass.h 3 | // Objective-LevelDB Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/14/13. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern dispatch_queue_t lvldb_test_queue; 13 | 14 | @interface BaseTestClass : XCTestCase { 15 | LevelDB *db; 16 | } 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /Tests/BaseTestClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTestClass.m 3 | // Objective-LevelDB Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/14/13. 6 | // 7 | // 8 | 9 | #import "BaseTestClass.h" 10 | 11 | dispatch_queue_t lvldb_test_queue; 12 | 13 | @implementation BaseTestClass 14 | 15 | static int db_i = 0; 16 | 17 | + (void)setUp { 18 | lvldb_test_queue = dispatch_queue_create("Create DB", DISPATCH_QUEUE_SERIAL); 19 | } 20 | 21 | - (void)setUp { 22 | dispatch_sync(lvldb_test_queue, ^{ 23 | db = [LevelDB databaseInLibraryWithName:[NSString stringWithFormat:@"TestDB%d", db_i]]; 24 | db_i++; 25 | }); 26 | [db removeAllObjects]; 27 | 28 | db.encoder = ^ NSData * (LevelDBKey *key, id value) { 29 | return [NSJSONSerialization dataWithJSONObject:value options:0 error:nil]; 30 | }; 31 | db.decoder = ^ id (LevelDBKey *key, NSData *data) { 32 | return [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 33 | }; 34 | } 35 | 36 | - (void)tearDown { 37 | [db close]; 38 | [db deleteDatabaseFromDisk]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Tests/Dummy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Dummy.cpp 3 | // Objective-LevelDB iOS Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/15/13. 6 | // 7 | // 8 | 9 | #include "Dummy.h" 10 | -------------------------------------------------------------------------------- /Tests/Dummy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Dummy.h 3 | // Objective-LevelDB iOS Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/15/13. 6 | // 7 | // 8 | 9 | #ifndef __Objective_LevelDB_iOS_Tests__Dummy__ 10 | #define __Objective_LevelDB_iOS_Tests__Dummy__ 11 | 12 | #include 13 | 14 | #endif /* defined(__Objective_LevelDB_iOS_Tests__Dummy__) */ 15 | -------------------------------------------------------------------------------- /Tests/MainTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_Tests.m 3 | // iOS Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/13/13. 6 | // 7 | // 8 | 9 | #import "BaseTestClass.h" 10 | 11 | @interface MainTests : BaseTestClass 12 | 13 | @end 14 | 15 | static NSUInteger numberOfIterations = 2500; 16 | 17 | @implementation MainTests 18 | 19 | - (void)testDatabaseCreated { 20 | XCTAssertNotNil(db, @"Database should not be nil"); 21 | } 22 | 23 | - (void)testContentIntegrity { 24 | id key = @"dict1"; 25 | id value = @{@"foo": @"bar"}; 26 | [db setObject:value forKey:key]; 27 | XCTAssertEqualObjects([db objectForKey:key], value, @"Saving and retrieving should keep an dictionary intact"); 28 | 29 | [db removeObjectForKey:@"dict1"]; 30 | XCTAssertNil([db objectForKey:@"dict1"], @"A deleted key should return nil"); 31 | 32 | value = @[@"foo", @"bar"]; 33 | [db setObject:value forKey:key]; 34 | XCTAssertEqualObjects([db objectForKey:key], value, @"Saving and retrieving should keep an array intact"); 35 | 36 | [db removeObjectsForKeys:@[@"array1"]]; 37 | XCTAssertNil([db objectForKey:@"array1"], @"A key that was deleted in batch should return nil"); 38 | } 39 | 40 | - (void)testKeysManipulation { 41 | id value = @{@"foo": @"bar"}; 42 | 43 | [db setObject:value forKey:@"dict1"]; 44 | [db setObject:value forKey:@"dict2"]; 45 | [db setObject:value forKey:@"dict3"]; 46 | 47 | NSArray *keys = @[ @"dict1", @"dict2", @"dict3" ]; 48 | NSArray *keysFromDB = [db allKeys]; 49 | NSMutableArray *stringKeys = [NSMutableArray arrayWithCapacity:3]; 50 | [keysFromDB enumerateObjectsUsingBlock:^(NSData *obj, NSUInteger idx, BOOL *stop) { 51 | NSString *stringKey = [[NSString alloc] initWithBytes:obj.bytes length:obj.length encoding:NSUTF8StringEncoding]; 52 | [stringKeys addObject:stringKey]; 53 | }]; 54 | XCTAssertEqualObjects(stringKeys, keys, @"-[LevelDB allKeys] should return the list of keys used to insert data"); 55 | 56 | [db removeAllObjects]; 57 | XCTAssertEqual([db allKeys], @[], @"The list of keys should be empty after removing all objects from the database"); 58 | } 59 | 60 | - (void)testRemovingKeysWithPrefix { 61 | id value = @{@"foo": @"bar"}; 62 | [db setObject:value forKey:@"dict1"]; 63 | [db setObject:value forKey:@"dict2"]; 64 | [db setObject:value forKey:@"dict3"]; 65 | [db setObject:@[@1,@2,@3] forKey:@"array1"]; 66 | 67 | [db removeAllObjectsWithPrefix:@"dict"]; 68 | XCTAssertEqual([[db allKeys] count], (NSUInteger)1, 69 | @"There should be only 1 key remaining after removing all those prefixed with 'dict'"); 70 | } 71 | 72 | - (void)testDictionaryManipulations { 73 | NSDictionary *objects = @{ 74 | @"key1": @[@1, @2], 75 | @"key2": @{@"foo": @"bar"}, 76 | @"key3": @[@{}] 77 | }; 78 | [db addEntriesFromDictionary:objects]; 79 | NSArray *keys = @[@"key1", @"key2", @"key3"]; 80 | 81 | for (id key in keys) 82 | XCTAssertEqualObjects(db[key], objects[key], 83 | @"Objects should match between dictionary and db"); 84 | 85 | keys = @[@"key1", @"key2", @"key9"]; 86 | NSDictionary *extractedObjects = [NSDictionary dictionaryWithObjects:[db objectsForKeys:keys 87 | notFoundMarker:[NSNull null]] 88 | forKeys:keys]; 89 | for (id key in keys) { 90 | id val; 91 | XCTAssertEqualObjects(extractedObjects[key], 92 | (val = [objects objectForKey:key]) ? val : [NSNull null], 93 | @"Objects should match between dictionary and db, or return the noFoundMarker"); 94 | } 95 | } 96 | 97 | - (void)testPredicateFiltering { 98 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"price BETWEEN {25, 50}"]; 99 | NSMutableArray *resultKeys = [NSMutableArray array]; 100 | 101 | NSUInteger *keyDataPtr = malloc(sizeof(NSUInteger)); 102 | NSInteger price; 103 | 104 | NSComparator dataComparator = ^ NSComparisonResult (NSData *key1, NSData *key2) { 105 | int cmp = memcmp(key1.bytes, key2.bytes, MIN(key1.length, key2.length)); 106 | 107 | if (cmp == 0) 108 | return NSOrderedSame; 109 | else if (cmp > 0) 110 | return NSOrderedDescending; 111 | else 112 | return NSOrderedAscending; 113 | }; 114 | 115 | arc4random_stir(); 116 | for (int i=0; i= 25 && price <= 50) { 123 | [resultKeys addObject:keyData]; 124 | } 125 | [db setObject:@{@"price": @(price)} forKey:keyData]; 126 | } 127 | [resultKeys sortUsingComparator:dataComparator]; 128 | 129 | XCTAssertEqualObjects([db keysByFilteringWithPredicate:predicate], 130 | resultKeys, 131 | @"Filtering db keys with a predicate should return the same list as expected"); 132 | 133 | NSDictionary *allObjects = [db dictionaryByFilteringWithPredicate:predicate]; 134 | XCTAssertEqualObjects([[allObjects allKeys] sortedArrayUsingComparator:dataComparator], 135 | resultKeys, 136 | @"A dictionary obtained by filtering with a predicate should yield the expected list of keys"); 137 | 138 | __block int i = 0; 139 | [db enumerateKeysBackward:NO 140 | startingAtKey:nil 141 | filteredByPredicate:predicate 142 | andPrefix:nil 143 | usingBlock:^(LevelDBKey *key, BOOL *stop) { 144 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 145 | @"Enumerating by filtering with a predicate should yield the expected keys"); 146 | i++; 147 | }]; 148 | 149 | i = (int)resultKeys.count - 1; 150 | [db enumerateKeysBackward:YES 151 | startingAtKey:nil 152 | filteredByPredicate:predicate 153 | andPrefix:nil 154 | usingBlock:^(LevelDBKey *key, BOOL *stop) { 155 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 156 | @"Enumerating backwards by filtering with a predicate should yield the expected keys"); 157 | i--; 158 | }]; 159 | 160 | i = 0; 161 | [db enumerateKeysAndObjectsBackward:NO lazily:NO 162 | startingAtKey:nil 163 | filteredByPredicate:predicate 164 | andPrefix:nil 165 | usingBlock:^(LevelDBKey *key, id value, BOOL *stop) { 166 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 167 | @"Enumerating keys and objects by filtering with a predicate should yield the expected keys"); 168 | XCTAssertEqualObjects(value, allObjects[resultKeys[i]], 169 | @"Enumerating keys and objects by filtering with a predicate should yield the expected values"); 170 | i++; 171 | }]; 172 | 173 | i = (int)resultKeys.count - 1; 174 | [db enumerateKeysAndObjectsBackward:YES lazily:NO 175 | startingAtKey:nil 176 | filteredByPredicate:predicate 177 | andPrefix:nil 178 | usingBlock:^(LevelDBKey *key, id value, BOOL *stop) { 179 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 180 | @"Enumerating keys and objects by filtering with a predicate should yield the expected keys"); 181 | XCTAssertEqualObjects(value, allObjects[resultKeys[i]], 182 | @"Enumerating keys and objects by filtering with a predicate should yield the expected values"); 183 | i--; 184 | }]; 185 | } 186 | 187 | - (NSArray *)nPairs:(NSUInteger)n { 188 | NSMutableArray *pairs = [NSMutableArray array]; 189 | 190 | __block NSInteger r; 191 | __block NSString *key; 192 | __block NSArray *value; 193 | 194 | dispatch_apply(n, lvldb_test_queue, ^(size_t i) { 195 | do { 196 | r = arc4random_uniform(5000); 197 | key = [NSString stringWithFormat:@"%ld", (long)r]; 198 | } while ([db objectExistsForKey:key]); 199 | 200 | value = @[@(r), @(i)]; 201 | [pairs addObject:@[key, value]]; 202 | [db setObject:value forKey:key]; 203 | }); 204 | 205 | [pairs sortUsingComparator:^NSComparisonResult(NSArray *obj1, NSArray *obj2) { 206 | return [obj1[0] compare:obj2[0]]; 207 | }]; 208 | return pairs; 209 | } 210 | 211 | - (void)testForwardKeyEnumerations { 212 | __block NSInteger r; 213 | __block NSString *key; 214 | __block NSArray *value; 215 | 216 | NSArray *pairs = [self nPairs:numberOfIterations]; 217 | 218 | // Test that enumerating the whole set yields keys in the correct orders 219 | r = 0; 220 | [db enumerateKeysUsingBlock:^(LevelDBKey *lkey, BOOL *stop) { 221 | NSArray *pair = pairs[r]; 222 | key = pair[0]; 223 | value = pair[1]; 224 | 225 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 226 | @"Keys should be equal, given the ordering worked"); 227 | r++; 228 | }]; 229 | 230 | // Test that enumerating the set by starting at an offset yields keys in the correct orders 231 | r = 432; 232 | [db enumerateKeysBackward:NO 233 | startingAtKey:pairs[r][0] 234 | filteredByPredicate:nil 235 | andPrefix:nil 236 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 237 | NSArray *pair = pairs[r]; 238 | key = pair[0]; 239 | value = pair[1]; 240 | 241 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 242 | @"Keys should be equal, given the ordering worked"); 243 | r++; 244 | }]; 245 | } 246 | 247 | - (void)testBackwardKeyEnumerations { 248 | __block NSInteger r; 249 | __block NSString *key; 250 | __block NSArray *value; 251 | 252 | NSArray *pairs = [self nPairs:numberOfIterations]; 253 | 254 | // Test that enumerating the whole set backwards yields keys in the correct orders 255 | r = [pairs count] - 1; 256 | [db enumerateKeysBackward:YES 257 | startingAtKey:nil 258 | filteredByPredicate:nil 259 | andPrefix:nil 260 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 261 | NSArray *pair = pairs[r]; 262 | key = pair[0]; 263 | value = pair[1]; 264 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 265 | @"Keys should be equal, given the ordering worked"); 266 | r--; 267 | }]; 268 | 269 | // Test that enumerating the set backwards at an offset yields keys in the correct orders 270 | r = 567; 271 | [db enumerateKeysBackward:YES 272 | startingAtKey:pairs[r][0] 273 | filteredByPredicate:nil 274 | andPrefix:nil 275 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 276 | NSArray *pair = pairs[r]; 277 | key = pair[0]; 278 | value = pair[1]; 279 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 280 | @"Keys should be equal, given the ordering worked"); 281 | r--; 282 | }]; 283 | 284 | } 285 | 286 | - (void)testPrefixedEnumerations { 287 | id(^valueFor)(int) = ^ id (int i) { return @{ @"key": @(i) }; }; 288 | NSDictionary *pairs = @{ 289 | @"tess:0": valueFor(0), 290 | @"tesa:0": valueFor(0), 291 | @"test:1": valueFor(1), 292 | @"test:2": valueFor(2), 293 | @"test:3": valueFor(3), 294 | @"test:4": valueFor(4) 295 | }; 296 | 297 | __block int i = 4; 298 | [db addEntriesFromDictionary:pairs]; 299 | [db enumerateKeysBackward:YES 300 | startingAtKey:nil 301 | filteredByPredicate:nil 302 | andPrefix:@"test" 303 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 304 | NSString *key = [NSString stringWithFormat:@"test:%d", i]; 305 | XCTAssertEqualObjects(NSStringFromLevelDBKey(lkey), key, 306 | @"Keys should be restricted to the prefixed region"); 307 | i--; 308 | }]; 309 | XCTAssertEqual(i, 0, @""); 310 | 311 | 312 | [db removeAllObjects]; 313 | [db addEntriesFromDictionary:@{@"tess:0": valueFor(0), 314 | @"test:1": valueFor(1), 315 | @"test:2": valueFor(2), 316 | @"test:3": valueFor(3), 317 | @"test:4": valueFor(4), 318 | @"tesu:5": valueFor(5)}]; 319 | i = 4; 320 | [db enumerateKeysAndObjectsBackward:YES 321 | lazily:NO 322 | startingAtKey:nil 323 | filteredByPredicate:nil 324 | andPrefix:@"test" 325 | usingBlock:^(LevelDBKey *lkey, NSDictionary *value, BOOL *stop) { 326 | NSString *key = [NSString stringWithFormat:@"test:%d", i]; 327 | XCTAssertEqualObjects(NSStringFromLevelDBKey(lkey), key, 328 | @"Keys should be restricted to the prefixed region"); 329 | XCTAssertEqualObjects(value[@"key"], @(i), 330 | @"Values should be restricted to the prefixed region"); 331 | i--; 332 | }]; 333 | XCTAssertEqual(i, 0, @""); 334 | 335 | i = 1; 336 | [db addEntriesFromDictionary:pairs]; 337 | [db enumerateKeysBackward:NO 338 | startingAtKey:nil 339 | filteredByPredicate:nil 340 | andPrefix:@"test" 341 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 342 | NSString *key = [NSString stringWithFormat:@"test:%d", i]; 343 | XCTAssertEqualObjects(NSStringFromLevelDBKey(lkey), key, 344 | @"Keys should be restricted to the prefixed region"); 345 | i++; 346 | }]; 347 | XCTAssertEqual(i, 5, @""); 348 | 349 | i = 1; 350 | [db enumerateKeysAndObjectsBackward:NO 351 | lazily:NO 352 | startingAtKey:nil 353 | filteredByPredicate:nil 354 | andPrefix:@"test" 355 | usingBlock:^(LevelDBKey *lkey, NSDictionary *value, BOOL *stop) { 356 | NSString *key = [NSString stringWithFormat:@"test:%d", i]; 357 | XCTAssertEqualObjects(NSStringFromLevelDBKey(lkey), key, 358 | @"Keys should be restricted to the prefixed region"); 359 | XCTAssertEqualObjects(value[@"key"], @(i), 360 | @"Values should be restricted to the prefixed region"); 361 | i++; 362 | }]; 363 | XCTAssertEqual(i, 5, @""); 364 | } 365 | 366 | - (void)testForwardKeyAndValueEnumerations { 367 | __block NSInteger r; 368 | __block NSString *key; 369 | __block NSArray *value; 370 | 371 | NSArray *pairs = [self nPairs:numberOfIterations]; 372 | // Test that enumerating the whole set yields pairs in the correct orders 373 | r = 0; 374 | [db enumerateKeysAndObjectsUsingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 375 | NSArray *pair = pairs[r]; 376 | key = pair[0]; 377 | value = pair[1]; 378 | 379 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 380 | @"Keys should be equal, given the ordering worked"); 381 | XCTAssertEqualObjects(_value, value, 382 | @"Values should be equal, given the ordering worked"); 383 | r++; 384 | }]; 385 | 386 | // Test that enumerating the set by starting at an offset yields pairs in the correct orders 387 | r = 432; 388 | [db enumerateKeysAndObjectsBackward:NO lazily:NO 389 | startingAtKey:pairs[r][0] 390 | filteredByPredicate:nil 391 | andPrefix:nil 392 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 393 | NSArray *pair = pairs[r]; 394 | key = pair[0]; 395 | value = pair[1]; 396 | 397 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 398 | @"Keys should be equal, given the ordering worked"); 399 | XCTAssertEqualObjects(_value, value, 400 | @"Values should be equal, given the ordering worked"); 401 | r++; 402 | }]; 403 | } 404 | 405 | - (void)testBackwardKeyAndValueEnumerations { 406 | __block NSInteger r; 407 | __block NSString *key; 408 | __block NSArray *value; 409 | 410 | NSArray *pairs = [self nPairs:numberOfIterations]; 411 | // Test that enumerating the whole set backwards yields pairs in the correct orders 412 | r = [pairs count] - 1; 413 | [db enumerateKeysAndObjectsBackward:YES lazily:NO 414 | startingAtKey:nil 415 | filteredByPredicate:nil 416 | andPrefix:nil 417 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 418 | NSArray *pair = pairs[r]; 419 | key = pair[0]; 420 | value = pair[1]; 421 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 422 | @"Keys should be equal, given the ordering worked"); 423 | XCTAssertEqualObjects(_value, value, 424 | @"Values should be equal, given the ordering worked"); 425 | r--; 426 | }]; 427 | 428 | // Test that enumerating the set backwards at an offset yields pairs in the correct orders 429 | r = 567; 430 | [db enumerateKeysAndObjectsBackward:YES lazily:NO 431 | startingAtKey:pairs[r][0] 432 | filteredByPredicate:nil 433 | andPrefix:nil 434 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 435 | NSArray *pair = pairs[r]; 436 | key = pair[0]; 437 | value = pair[1]; 438 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 439 | @"Keys should be equal, given the ordering worked"); 440 | XCTAssertEqualObjects(_value, value, 441 | @"Values should be equal, given the ordering worked"); 442 | r--; 443 | }]; 444 | } 445 | 446 | - (void)testBackwardLazyKeyAndValueEnumerations { 447 | __block NSInteger r; 448 | __block NSString *key; 449 | __block NSArray *value; 450 | 451 | NSArray *pairs = [self nPairs:numberOfIterations]; 452 | // Test that enumerating the set backwards and lazily at an offset yields pairs in the correct orders 453 | r = 567; 454 | [db enumerateKeysAndObjectsBackward:YES lazily:YES 455 | startingAtKey:pairs[r][0] 456 | filteredByPredicate:nil 457 | andPrefix:nil 458 | usingBlock:^(LevelDBKey *lkey, LevelDBValueGetterBlock getter, BOOL *stop) { 459 | NSArray *pair = pairs[r]; 460 | key = pair[0]; 461 | value = pair[1]; 462 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 463 | @"Keys should be equal, given the ordering worked"); 464 | XCTAssertEqualObjects(getter(), value, 465 | @"Values should be equal, given the ordering worked"); 466 | r--; 467 | }]; 468 | 469 | [db removeAllObjects]; 470 | } 471 | 472 | @end 473 | -------------------------------------------------------------------------------- /Tests/OS X Tests/OS X Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/OS X Tests/OS X Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/OS X Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Tests/Objective-LevelDB OS X Tests.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B7F037D3138043C3BE1D1AA0 /* libPods-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B33EC4085AA4FB9AC528E97 /* libPods-osx.a */; }; 11 | D0358051183563BC00EDBF93 /* WritebatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9809A18356243000BD668 /* WritebatchTests.m */; }; 12 | D0358052183563C000EDBF93 /* BaseTestClass.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9809918356243000BD668 /* BaseTestClass.m */; }; 13 | D0D21E171836A2E200BD1F98 /* MainTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D21E151836A2E200BD1F98 /* MainTests.m */; }; 14 | D0D21E181836A2E200BD1F98 /* SnapshotsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D21E161836A2E200BD1F98 /* SnapshotsTests.m */; }; 15 | D0E980A518356273000BD668 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E980A418356273000BD668 /* XCTest.framework */; }; 16 | D0E980AB18356273000BD668 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0E980A918356273000BD668 /* InfoPlist.strings */; }; 17 | D0F44C77183673AC00AC2F1A /* Dummy.cpp in Resources */ = {isa = PBXBuildFile; fileRef = D0F44C75183673AC00AC2F1A /* Dummy.cpp */; }; 18 | D0F44C78183673AC00AC2F1A /* Dummy.h in Resources */ = {isa = PBXBuildFile; fileRef = D0F44C76183673AC00AC2F1A /* Dummy.h */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 3B33EC4085AA4FB9AC528E97 /* libPods-osx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-osx.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | C699A1AAADEC458E8DD5A410 /* Pods-osx.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-osx.xcconfig"; path = "Pods/Pods-osx.xcconfig"; sourceTree = ""; }; 24 | D0D21E151836A2E200BD1F98 /* MainTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainTests.m; sourceTree = ""; }; 25 | D0D21E161836A2E200BD1F98 /* SnapshotsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SnapshotsTests.m; sourceTree = ""; }; 26 | D0E9809818356243000BD668 /* BaseTestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTestClass.h; sourceTree = ""; }; 27 | D0E9809918356243000BD668 /* BaseTestClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseTestClass.m; sourceTree = ""; }; 28 | D0E9809A18356243000BD668 /* WritebatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WritebatchTests.m; sourceTree = ""; }; 29 | D0E980A118356273000BD668 /* OS X Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "OS X Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | D0E980A418356273000BD668 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 31 | D0E980A818356273000BD668 /* OS X Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "OS X Tests-Info.plist"; sourceTree = ""; }; 32 | D0E980AA18356273000BD668 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | D0E980AE18356273000BD668 /* OS X Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OS X Tests-Prefix.pch"; sourceTree = ""; }; 34 | D0F44C75183673AC00AC2F1A /* Dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dummy.cpp; sourceTree = ""; }; 35 | D0F44C76183673AC00AC2F1A /* Dummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dummy.h; sourceTree = ""; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | D0E9809E18356273000BD668 /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | D0E980A518356273000BD668 /* XCTest.framework in Frameworks */, 44 | B7F037D3138043C3BE1D1AA0 /* libPods-osx.a in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | D0E9809118356216000BD668 = { 52 | isa = PBXGroup; 53 | children = ( 54 | D0E9809818356243000BD668 /* BaseTestClass.h */, 55 | D0E9809918356243000BD668 /* BaseTestClass.m */, 56 | D0D21E151836A2E200BD1F98 /* MainTests.m */, 57 | D0D21E161836A2E200BD1F98 /* SnapshotsTests.m */, 58 | D0E9809A18356243000BD668 /* WritebatchTests.m */, 59 | D0F44C75183673AC00AC2F1A /* Dummy.cpp */, 60 | D0F44C76183673AC00AC2F1A /* Dummy.h */, 61 | D0E980A618356273000BD668 /* OS X Tests */, 62 | D0E980A318356273000BD668 /* Frameworks */, 63 | D0E980A218356273000BD668 /* Products */, 64 | C699A1AAADEC458E8DD5A410 /* Pods-osx.xcconfig */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | D0E980A218356273000BD668 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | D0E980A118356273000BD668 /* OS X Tests.xctest */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | D0E980A318356273000BD668 /* Frameworks */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | D0E980A418356273000BD668 /* XCTest.framework */, 80 | 3B33EC4085AA4FB9AC528E97 /* libPods-osx.a */, 81 | ); 82 | name = Frameworks; 83 | sourceTree = ""; 84 | }; 85 | D0E980A618356273000BD668 /* OS X Tests */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | D0E980A718356273000BD668 /* Supporting Files */, 89 | ); 90 | path = "OS X Tests"; 91 | sourceTree = ""; 92 | }; 93 | D0E980A718356273000BD668 /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | D0E980A818356273000BD668 /* OS X Tests-Info.plist */, 97 | D0E980A918356273000BD668 /* InfoPlist.strings */, 98 | D0E980AE18356273000BD668 /* OS X Tests-Prefix.pch */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | D0E980A018356273000BD668 /* OS X Tests */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = D0E980AF18356273000BD668 /* Build configuration list for PBXNativeTarget "OS X Tests" */; 109 | buildPhases = ( 110 | D0E9809D18356273000BD668 /* Sources */, 111 | D0E9809E18356273000BD668 /* Frameworks */, 112 | D0E9809F18356273000BD668 /* Resources */, 113 | 9C27E6898B6B4BFD8F0FED75 /* Copy Pods Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = "OS X Tests"; 120 | productName = "OS X Tests"; 121 | productReference = D0E980A118356273000BD668 /* OS X Tests.xctest */; 122 | productType = "com.apple.product-type.bundle.unit-test"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | D0E9809218356216000BD668 /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 0500; 131 | }; 132 | buildConfigurationList = D0E9809518356216000BD668 /* Build configuration list for PBXProject "Objective-LevelDB OS X Tests" */; 133 | compatibilityVersion = "Xcode 3.2"; 134 | developmentRegion = English; 135 | hasScannedForEncodings = 0; 136 | knownRegions = ( 137 | en, 138 | ); 139 | mainGroup = D0E9809118356216000BD668; 140 | productRefGroup = D0E980A218356273000BD668 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | D0E980A018356273000BD668 /* OS X Tests */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | D0E9809F18356273000BD668 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | D0F44C78183673AC00AC2F1A /* Dummy.h in Resources */, 155 | D0F44C77183673AC00AC2F1A /* Dummy.cpp in Resources */, 156 | D0E980AB18356273000BD668 /* InfoPlist.strings in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXShellScriptBuildPhase section */ 163 | 9C27E6898B6B4BFD8F0FED75 /* Copy Pods Resources */ = { 164 | isa = PBXShellScriptBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | ); 168 | inputPaths = ( 169 | ); 170 | name = "Copy Pods Resources"; 171 | outputPaths = ( 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | shellPath = /bin/sh; 175 | shellScript = "\"${SRCROOT}/Pods/Pods-osx-resources.sh\"\n"; 176 | showEnvVarsInLog = 0; 177 | }; 178 | /* End PBXShellScriptBuildPhase section */ 179 | 180 | /* Begin PBXSourcesBuildPhase section */ 181 | D0E9809D18356273000BD668 /* Sources */ = { 182 | isa = PBXSourcesBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | D0358051183563BC00EDBF93 /* WritebatchTests.m in Sources */, 186 | D0D21E181836A2E200BD1F98 /* SnapshotsTests.m in Sources */, 187 | D0D21E171836A2E200BD1F98 /* MainTests.m in Sources */, 188 | D0358052183563C000EDBF93 /* BaseTestClass.m in Sources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXSourcesBuildPhase section */ 193 | 194 | /* Begin PBXVariantGroup section */ 195 | D0E980A918356273000BD668 /* InfoPlist.strings */ = { 196 | isa = PBXVariantGroup; 197 | children = ( 198 | D0E980AA18356273000BD668 /* en */, 199 | ); 200 | name = InfoPlist.strings; 201 | sourceTree = ""; 202 | }; 203 | /* End PBXVariantGroup section */ 204 | 205 | /* Begin XCBuildConfiguration section */ 206 | D0E9809618356216000BD668 /* Debug */ = { 207 | isa = XCBuildConfiguration; 208 | buildSettings = { 209 | }; 210 | name = Debug; 211 | }; 212 | D0E9809718356216000BD668 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | }; 216 | name = Release; 217 | }; 218 | D0E980B018356273000BD668 /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | baseConfigurationReference = C699A1AAADEC458E8DD5A410 /* Pods-osx.xcconfig */; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; 224 | CLANG_CXX_LIBRARY = "compiler-default"; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | COPY_PHASE_STRIP = NO; 235 | FRAMEWORK_SEARCH_PATHS = ( 236 | "$(DEVELOPER_FRAMEWORKS_DIR)", 237 | "$(inherited)", 238 | ); 239 | GCC_C_LANGUAGE_STANDARD = gnu99; 240 | GCC_DYNAMIC_NO_PIC = NO; 241 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 244 | GCC_PREFIX_HEADER = "OS X Tests/OS X Tests-Prefix.pch"; 245 | GCC_PREPROCESSOR_DEFINITIONS = ( 246 | "DEBUG=1", 247 | "$(inherited)", 248 | ); 249 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 250 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 251 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 252 | GCC_WARN_UNDECLARED_SELECTOR = YES; 253 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 254 | GCC_WARN_UNUSED_FUNCTION = YES; 255 | GCC_WARN_UNUSED_VARIABLE = YES; 256 | INFOPLIST_FILE = "OS X Tests/OS X Tests-Info.plist"; 257 | MACOSX_DEPLOYMENT_TARGET = 10.9; 258 | ONLY_ACTIVE_ARCH = YES; 259 | PRODUCT_NAME = "$(TARGET_NAME)"; 260 | SDKROOT = macosx; 261 | WRAPPER_EXTENSION = xctest; 262 | }; 263 | name = Debug; 264 | }; 265 | D0E980B118356273000BD668 /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | baseConfigurationReference = C699A1AAADEC458E8DD5A410 /* Pods-osx.xcconfig */; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; 271 | CLANG_CXX_LIBRARY = "compiler-default"; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_WARN_BOOL_CONVERSION = YES; 274 | CLANG_WARN_CONSTANT_CONVERSION = YES; 275 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 276 | CLANG_WARN_EMPTY_BODY = YES; 277 | CLANG_WARN_ENUM_CONVERSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | COPY_PHASE_STRIP = YES; 282 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 283 | ENABLE_NS_ASSERTIONS = NO; 284 | FRAMEWORK_SEARCH_PATHS = ( 285 | "$(DEVELOPER_FRAMEWORKS_DIR)", 286 | "$(inherited)", 287 | ); 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 290 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 291 | GCC_PREFIX_HEADER = "OS X Tests/OS X Tests-Prefix.pch"; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | INFOPLIST_FILE = "OS X Tests/OS X Tests-Info.plist"; 299 | MACOSX_DEPLOYMENT_TARGET = 10.9; 300 | PRODUCT_NAME = "$(TARGET_NAME)"; 301 | SDKROOT = macosx; 302 | WRAPPER_EXTENSION = xctest; 303 | }; 304 | name = Release; 305 | }; 306 | /* End XCBuildConfiguration section */ 307 | 308 | /* Begin XCConfigurationList section */ 309 | D0E9809518356216000BD668 /* Build configuration list for PBXProject "Objective-LevelDB OS X Tests" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | D0E9809618356216000BD668 /* Debug */, 313 | D0E9809718356216000BD668 /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | D0E980AF18356273000BD668 /* Build configuration list for PBXNativeTarget "OS X Tests" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | D0E980B018356273000BD668 /* Debug */, 322 | D0E980B118356273000BD668 /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | /* End XCConfigurationList section */ 328 | }; 329 | rootObject = D0E9809218356216000BD668 /* Project object */; 330 | } 331 | -------------------------------------------------------------------------------- /Tests/Objective-LevelDB iOS Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Podfile: -------------------------------------------------------------------------------- 1 | workspace '../Objective-LevelDB' 2 | inhibit_all_warnings! 3 | 4 | def import_pods 5 | pod "Objective-LevelDB+Uber", :path => "../Objective-LevelDB+Uber.podspec" 6 | end 7 | 8 | if ENV["TEST_ENV"] == "OSX" 9 | xcodeproj 'Objective-LevelDB OS X Tests' 10 | target :osx do 11 | platform :osx, '10.9' 12 | link_with 'OS X Tests' 13 | import_pods 14 | end 15 | else 16 | xcodeproj 'Objective-LevelDB iOS Tests' 17 | target :ios do 18 | platform :ios, '7.0' 19 | link_with 'iOS Tests' 20 | import_pods 21 | end 22 | end -------------------------------------------------------------------------------- /Tests/SnapshotsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_SnapshotsTests.m 3 | // Objective-LevelDB Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/14/13. 6 | // 7 | // 8 | 9 | #import "BaseTestClass.h" 10 | #import 11 | 12 | static NSUInteger numberOfIterations = 2500; 13 | 14 | @interface SnapshotsTests : BaseTestClass 15 | 16 | @end 17 | 18 | @implementation SnapshotsTests { 19 | LDBSnapshot *snapshot; 20 | } 21 | 22 | - (void) testInvariability { 23 | snapshot = [db newSnapshot]; 24 | [db setObject:@{@"foo": @"bar"} forKey:@"key"]; 25 | XCTAssertNil([snapshot objectForKey:@"key"], 26 | @"Fetching a key inserted after snapshot was taken should yield nil"); 27 | 28 | snapshot = nil; 29 | } 30 | 31 | - (NSArray *)nPairs:(NSUInteger)n { 32 | NSMutableArray *pairs = [NSMutableArray array]; 33 | 34 | __block NSInteger r; 35 | __block NSString *key; 36 | __block NSArray *value; 37 | 38 | dispatch_apply(n, lvldb_test_queue, ^(size_t i) { 39 | do { 40 | r = arc4random_uniform(5000); 41 | key = [NSString stringWithFormat:@"%ld", (long)r]; 42 | } while ([db objectExistsForKey:key]); 43 | 44 | value = @[@(r), @(i)]; 45 | [pairs addObject:@[key, value]]; 46 | [db setObject:value forKey:key]; 47 | }); 48 | 49 | [pairs sortUsingComparator:^NSComparisonResult(NSArray *obj1, NSArray *obj2) { 50 | return [obj1[0] compare:obj2[0]]; 51 | }]; 52 | return pairs; 53 | } 54 | 55 | - (void)testContentIntegrity { 56 | id key = @"dict1"; 57 | id value = @{@"foo": @"bar"}; 58 | [db setObject:value forKey:key]; 59 | snapshot = [db newSnapshot]; 60 | XCTAssertEqualObjects([snapshot objectForKey:key], value, 61 | @"Saving and retrieving should keep an dictionary intact"); 62 | 63 | [db removeObjectForKey:key]; 64 | XCTAssertEqualObjects([snapshot objectForKey:key], value, 65 | @"Removing a key from the db should affect a snapshot right away"); 66 | 67 | snapshot = [db newSnapshot]; 68 | XCTAssertNil([snapshot objectForKey:@"dict1"], 69 | @"A new snapshot should have those changes"); 70 | 71 | value = @[@"foo", @"bar"]; 72 | [db setObject:value forKey:key]; 73 | XCTAssertNil([snapshot objectForKey:@"dict1"], 74 | @"Inserting a new value in the db should not affect a previous snapshot"); 75 | 76 | snapshot = [db newSnapshot]; 77 | XCTAssertEqualObjects([snapshot objectForKey:key], value, @"Saving and retrieving should keep an array intact"); 78 | 79 | [db removeObjectsForKeys:@[@"array1"]]; 80 | XCTAssertEqualObjects([snapshot objectForKey:key], value, 81 | @"Removing a key from the db should affect a snapshot right away"); 82 | 83 | snapshot = [db newSnapshot]; 84 | XCTAssertNil([snapshot objectForKey:@"array1"], @"A key that was deleted in batch should return nil"); 85 | } 86 | 87 | - (void)testKeysManipulation { 88 | id value = @{@"foo": @"bar"}; 89 | 90 | [db setObject:value forKey:@"dict1"]; 91 | [db setObject:value forKey:@"dict2"]; 92 | [db setObject:value forKey:@"dict3"]; 93 | 94 | snapshot = [db newSnapshot]; 95 | [db removeAllObjects]; 96 | 97 | NSArray *keys = @[ @"dict1", @"dict2", @"dict3" ]; 98 | NSArray *keysFromDB = [snapshot allKeys]; 99 | NSMutableArray *stringKeys = [NSMutableArray arrayWithCapacity:3]; 100 | [keysFromDB enumerateObjectsUsingBlock:^(NSData *obj, NSUInteger idx, BOOL *stop) { 101 | NSString *stringKey = [[NSString alloc] initWithBytes:obj.bytes length:obj.length encoding:NSUTF8StringEncoding]; 102 | [stringKeys addObject:stringKey]; 103 | }]; 104 | XCTAssertEqualObjects(stringKeys, keys, @"-[LevelDB allKeys] should return the list of keys used to insert data"); 105 | 106 | snapshot = [db newSnapshot]; 107 | XCTAssertEqual([snapshot allKeys], @[], 108 | @"The list of keys should be empty after removing all objects from the database"); 109 | } 110 | 111 | - (void)testDictionaryManipulations { 112 | NSDictionary *objects = @{ 113 | @"key1": @[@1, @2], 114 | @"key2": @{@"foo": @"bar"}, 115 | @"key3": @[@{}] 116 | }; 117 | [db addEntriesFromDictionary:objects]; 118 | NSArray *keys = @[@"key1", @"key2", @"key3"]; 119 | 120 | snapshot = [db newSnapshot]; 121 | [db removeAllObjects]; 122 | 123 | for (id key in keys) 124 | XCTAssertEqualObjects(snapshot[key], objects[key], 125 | @"Objects should match between dictionary and db"); 126 | 127 | keys = @[@"key1", @"key2", @"key9"]; 128 | NSDictionary *extractedObjects = [NSDictionary dictionaryWithObjects:[snapshot objectsForKeys:keys 129 | notFoundMarker:[NSNull null]] 130 | forKeys:keys]; 131 | for (id key in keys) { 132 | id val; 133 | XCTAssertEqualObjects(extractedObjects[key], 134 | (val = [objects objectForKey:key]) ? val : [NSNull null], 135 | @"Objects should match between dictionary and db, or return the noFoundMarker"); 136 | } 137 | } 138 | 139 | - (void)testPredicateFiltering { 140 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"price BETWEEN {25, 50}"]; 141 | NSMutableArray *resultKeys = [NSMutableArray array]; 142 | 143 | NSUInteger *keyDataPtr = malloc(sizeof(NSUInteger)); 144 | NSInteger price; 145 | 146 | NSComparator dataComparator = ^ NSComparisonResult (NSData *key1, NSData *key2) { 147 | int cmp = memcmp(key1.bytes, key2.bytes, MIN(key1.length, key2.length)); 148 | 149 | if (cmp == 0) 150 | return NSOrderedSame; 151 | else if (cmp > 0) 152 | return NSOrderedDescending; 153 | else 154 | return NSOrderedAscending; 155 | }; 156 | 157 | arc4random_stir(); 158 | for (int i=0; i= 25 && price <= 50) { 165 | [resultKeys addObject:keyData]; 166 | } 167 | [db setObject:@{@"price": @(price)} forKey:keyData]; 168 | } 169 | [resultKeys sortUsingComparator:dataComparator]; 170 | snapshot = [db newSnapshot]; 171 | [db removeAllObjects]; 172 | 173 | XCTAssertEqualObjects([snapshot keysByFilteringWithPredicate:predicate], 174 | resultKeys, 175 | @"Filtering db keys with a predicate should return the same list as expected"); 176 | 177 | NSDictionary *allObjects = [snapshot dictionaryByFilteringWithPredicate:predicate]; 178 | XCTAssertEqualObjects([[allObjects allKeys] sortedArrayUsingComparator:dataComparator], 179 | resultKeys, 180 | @"A dictionary obtained by filtering with a predicate should yield the expected list of keys"); 181 | 182 | __block int i = 0; 183 | [snapshot enumerateKeysBackward:NO 184 | startingAtKey:nil 185 | filteredByPredicate:predicate 186 | andPrefix:nil 187 | usingBlock:^(LevelDBKey *key, BOOL *stop) { 188 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 189 | @"Enumerating by filtering with a predicate should yield the expected keys"); 190 | i++; 191 | }]; 192 | 193 | i = (int)resultKeys.count - 1; 194 | [snapshot enumerateKeysBackward:YES 195 | startingAtKey:nil 196 | filteredByPredicate:predicate 197 | andPrefix:nil 198 | usingBlock:^(LevelDBKey *key, BOOL *stop) { 199 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 200 | @"Enumerating backwards by filtering with a predicate should yield the expected keys"); 201 | i--; 202 | }]; 203 | 204 | i = 0; 205 | [snapshot enumerateKeysAndObjectsBackward:NO lazily:NO 206 | startingAtKey:nil 207 | filteredByPredicate:predicate 208 | andPrefix:nil 209 | usingBlock:^(LevelDBKey *key, id value, BOOL *stop) { 210 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 211 | @"Enumerating keys and objects by filtering with a predicate should yield the expected keys"); 212 | XCTAssertEqualObjects(value, allObjects[resultKeys[i]], 213 | @"Enumerating keys and objects by filtering with a predicate should yield the expected values"); 214 | i++; 215 | }]; 216 | 217 | i = (int)resultKeys.count - 1; 218 | [snapshot enumerateKeysAndObjectsBackward:YES lazily:NO 219 | startingAtKey:nil 220 | filteredByPredicate:predicate 221 | andPrefix:nil 222 | usingBlock:^(LevelDBKey *key, id value, BOOL *stop) { 223 | XCTAssertEqualObjects(NSDataFromLevelDBKey(key), resultKeys[i], 224 | @"Enumerating keys and objects by filtering with a predicate should yield the expected keys"); 225 | XCTAssertEqualObjects(value, allObjects[resultKeys[i]], 226 | @"Enumerating keys and objects by filtering with a predicate should yield the expected values"); 227 | i--; 228 | }]; 229 | } 230 | 231 | - (void)testForwardKeyEnumerations { 232 | __block NSInteger r; 233 | __block NSString *key; 234 | __block NSArray *value; 235 | 236 | NSArray *pairs = [self nPairs:numberOfIterations]; 237 | 238 | snapshot = [db newSnapshot]; 239 | [db removeAllObjects]; 240 | 241 | // Test that enumerating the whole set yields keys in the correct orders 242 | r = 0; 243 | [snapshot enumerateKeysUsingBlock:^(LevelDBKey *lkey, BOOL *stop) { 244 | NSArray *pair = pairs[r]; 245 | key = pair[0]; 246 | value = pair[1]; 247 | 248 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 249 | @"Keys should be equal, given the ordering worked"); 250 | r++; 251 | }]; 252 | 253 | // Test that enumerating the set by starting at an offset yields keys in the correct orders 254 | r = 432; 255 | [snapshot enumerateKeysBackward:NO 256 | startingAtKey:pairs[r][0] 257 | filteredByPredicate:nil 258 | andPrefix:nil 259 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 260 | NSArray *pair = pairs[r]; 261 | key = pair[0]; 262 | value = pair[1]; 263 | 264 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 265 | @"Keys should be equal, given the ordering worked"); 266 | r++; 267 | }]; 268 | } 269 | 270 | - (void)testBackwardKeyEnumerations { 271 | __block NSInteger r; 272 | __block NSString *key; 273 | __block NSArray *value; 274 | 275 | NSArray *pairs = [self nPairs:numberOfIterations]; 276 | 277 | snapshot = [db newSnapshot]; 278 | [db removeAllObjects]; 279 | 280 | // Test that enumerating the whole set backwards yields keys in the correct orders 281 | r = [pairs count] - 1; 282 | [snapshot enumerateKeysBackward:YES 283 | startingAtKey:nil 284 | filteredByPredicate:nil 285 | andPrefix:nil 286 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 287 | NSArray *pair = pairs[r]; 288 | key = pair[0]; 289 | value = pair[1]; 290 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 291 | @"Keys should be equal, given the ordering worked"); 292 | r--; 293 | }]; 294 | 295 | // Test that enumerating the set backwards at an offset yields keys in the correct orders 296 | r = 567; 297 | [snapshot enumerateKeysBackward:YES 298 | startingAtKey:pairs[r][0] 299 | filteredByPredicate:nil 300 | andPrefix:nil 301 | usingBlock:^(LevelDBKey *lkey, BOOL *stop) { 302 | NSArray *pair = pairs[r]; 303 | key = pair[0]; 304 | value = pair[1]; 305 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 306 | @"Keys should be equal, given the ordering worked"); 307 | r--; 308 | }]; 309 | 310 | } 311 | 312 | - (void)testForwardKeyAndValueEnumerations { 313 | __block NSInteger r; 314 | __block NSString *key; 315 | __block NSArray *value; 316 | 317 | NSArray *pairs = [self nPairs:numberOfIterations]; 318 | // Test that enumerating the whole set yields pairs in the correct orders 319 | r = 0; 320 | 321 | snapshot = [db newSnapshot]; 322 | [db removeAllObjects]; 323 | 324 | [snapshot enumerateKeysAndObjectsUsingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 325 | NSArray *pair = pairs[r]; 326 | key = pair[0]; 327 | value = pair[1]; 328 | 329 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 330 | @"Keys should be equal, given the ordering worked"); 331 | XCTAssertEqualObjects(_value, value, 332 | @"Values should be equal, given the ordering worked"); 333 | r++; 334 | }]; 335 | 336 | // Test that enumerating the set by starting at an offset yields pairs in the correct orders 337 | r = 432; 338 | [snapshot enumerateKeysAndObjectsBackward:NO lazily:NO 339 | startingAtKey:pairs[r][0] 340 | filteredByPredicate:nil 341 | andPrefix:nil 342 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 343 | NSArray *pair = pairs[r]; 344 | key = pair[0]; 345 | value = pair[1]; 346 | 347 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 348 | @"Keys should be equal, given the ordering worked"); 349 | XCTAssertEqualObjects(_value, value, 350 | @"Values should be equal, given the ordering worked"); 351 | r++; 352 | }]; 353 | } 354 | 355 | - (void)testBackwardKeyAndValueEnumerations { 356 | __block NSInteger r; 357 | __block NSString *key; 358 | __block NSArray *value; 359 | 360 | NSArray *pairs = [self nPairs:numberOfIterations]; 361 | 362 | snapshot = [db newSnapshot]; 363 | [db removeAllObjects]; 364 | 365 | // Test that enumerating the whole set backwards yields pairs in the correct orders 366 | r = [pairs count] - 1; 367 | [snapshot enumerateKeysAndObjectsBackward:YES lazily:NO 368 | startingAtKey:nil 369 | filteredByPredicate:nil 370 | andPrefix:nil 371 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 372 | NSArray *pair = pairs[r]; 373 | key = pair[0]; 374 | value = pair[1]; 375 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 376 | @"Keys should be equal, given the ordering worked"); 377 | XCTAssertEqualObjects(_value, value, 378 | @"Values should be equal, given the ordering worked"); 379 | r--; 380 | }]; 381 | 382 | // Test that enumerating the set backwards at an offset yields pairs in the correct orders 383 | r = 567; 384 | [snapshot enumerateKeysAndObjectsBackward:YES lazily:NO 385 | startingAtKey:pairs[r][0] 386 | filteredByPredicate:nil 387 | andPrefix:nil 388 | usingBlock:^(LevelDBKey *lkey, id _value, BOOL *stop) { 389 | NSArray *pair = pairs[r]; 390 | key = pair[0]; 391 | value = pair[1]; 392 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 393 | @"Keys should be equal, given the ordering worked"); 394 | XCTAssertEqualObjects(_value, value, 395 | @"Values should be equal, given the ordering worked"); 396 | r--; 397 | }]; 398 | } 399 | 400 | - (void)testBackwardLazyKeyAndValueEnumerations { 401 | __block NSInteger r; 402 | __block NSString *key; 403 | __block NSArray *value; 404 | 405 | NSArray *pairs = [self nPairs:numberOfIterations]; 406 | // Test that enumerating the set backwards and lazily at an offset yields pairs in the correct orders 407 | r = 567; 408 | 409 | snapshot = [db newSnapshot]; 410 | [db removeAllObjects]; 411 | 412 | [snapshot enumerateKeysAndObjectsBackward:YES lazily:YES 413 | startingAtKey:pairs[r][0] 414 | filteredByPredicate:nil 415 | andPrefix:nil 416 | usingBlock:^(LevelDBKey *lkey, LevelDBValueGetterBlock getter, BOOL *stop) { 417 | NSArray *pair = pairs[r]; 418 | key = pair[0]; 419 | value = pair[1]; 420 | XCTAssertEqualObjects(key, NSStringFromLevelDBKey(lkey), 421 | @"Keys should be equal, given the ordering worked"); 422 | XCTAssertEqualObjects(getter(), value, 423 | @"Values should be equal, given the ordering worked"); 424 | r--; 425 | }]; 426 | 427 | [db removeAllObjects]; 428 | } 429 | 430 | @end -------------------------------------------------------------------------------- /Tests/WritebatchTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LDBWritebatch.m 3 | // Objective-LevelDB Tests 4 | // 5 | // Created by Mathieu D'Amours on 11/14/13. 6 | // 7 | // 8 | 9 | #import 10 | #import "BaseTestClass.h" 11 | 12 | @interface WritebatchTests : BaseTestClass 13 | 14 | @end 15 | 16 | @implementation WritebatchTests 17 | 18 | - (void) testDatabaseIntegrity { 19 | LDBWritebatch *wb = [db newWritebatch]; 20 | 21 | id key = @"dict1"; 22 | id value = @{@"foo": @"bar"}; 23 | [wb setObject:value forKey:key]; 24 | XCTAssertNil([db objectForKey:@"dict1"], 25 | @"An insertion operation on the writebatch alone should be reflected in the DB yet"); 26 | 27 | [wb apply]; 28 | XCTAssertEqualObjects([db objectForKey:key], value, 29 | @"Applying the writebatch should reflect its changes in the DB"); 30 | 31 | wb = [db newWritebatch]; 32 | [wb removeObjectForKey:@"dict1"]; 33 | XCTAssertEqualObjects([db objectForKey:key], value, 34 | @"A delete operation on a writebatch alone should not be reflected in the DB yet"); 35 | [wb apply]; 36 | XCTAssertNil([db objectForKey:@"dict1"], @"A deleted key, once the writebatch is applied, should return nil"); 37 | 38 | wb = nil; 39 | } 40 | 41 | - (void)testKeysManipulation { 42 | id value = @{@"foo": @"bar"}; 43 | 44 | LDBWritebatch *wb = [db newWritebatch]; 45 | 46 | [wb setObject:value forKey:@"dict1"]; 47 | [wb setObject:value forKey:@"dict2"]; 48 | [wb setObject:value forKey:@"dict3"]; 49 | 50 | XCTAssertEqual([db allKeys], @[], @"The list of keys should be empty before applying the writebatch"); 51 | [wb apply]; 52 | 53 | wb = [db newWritebatch]; 54 | [wb removeAllObjects]; 55 | 56 | NSArray *keys = @[ @"dict1", @"dict2", @"dict3" ]; 57 | NSArray *keysFromDB = [db allKeys]; 58 | NSMutableArray *stringKeys = [NSMutableArray arrayWithCapacity:3]; 59 | [keysFromDB enumerateObjectsUsingBlock:^(NSData *obj, NSUInteger idx, BOOL *stop) { 60 | NSString *stringKey = [[NSString alloc] initWithBytes:obj.bytes length:obj.length encoding:NSUTF8StringEncoding]; 61 | [stringKeys addObject:stringKey]; 62 | }]; 63 | XCTAssertEqualObjects(stringKeys, keys, @"-[LevelDB allKeys] should return the list of keys used to insert data"); 64 | 65 | [wb apply]; 66 | XCTAssertEqual([db allKeys], @[], @"The list of keys should be empty after removing all objects from the database"); 67 | } 68 | 69 | - (void)testDictionaryManipulations { 70 | NSDictionary *objects = @{ 71 | @"key1": @[@1, @2], 72 | @"key2": @{@"foo": @"bar"}, 73 | @"key3": @[@{}] 74 | }; 75 | 76 | LDBWritebatch *wb = [db newWritebatch]; 77 | [wb addEntriesFromDictionary:objects]; 78 | NSArray *keys = @[@"key1", @"key2", @"key3"]; 79 | 80 | XCTAssertEqual([db allKeys], @[], @"The list of keys should be empty before applying the writebatch"); 81 | [wb apply]; 82 | 83 | for (id key in keys) 84 | XCTAssertEqualObjects(db[key], objects[key], 85 | @"Objects should match between dictionary and db"); 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Tests/iOS Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Tests/iOS Tests/iOS Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/iOS Tests/iOS Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /setup-test.sh: -------------------------------------------------------------------------------- 1 | ROOT=$(pwd) 2 | 3 | cd Tests 4 | rm -fR Pods 5 | pod install && rm -fR Pods/Objective-LevelDB/Classes 6 | 7 | cd .. 8 | ln -nfs $ROOT $ROOT/Tests/Pods/Objective-LevelDB 9 | --------------------------------------------------------------------------------