aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source/Local/FSTLevelDB.mm
diff options
context:
space:
mode:
authorGravatar Gil <mcg@google.com>2017-10-03 08:55:22 -0700
committerGravatar GitHub <noreply@github.com>2017-10-03 08:55:22 -0700
commitbde743ed25166a0b320ae157bfb1d68064f531c9 (patch)
tree4dd7525d9df32fa5dbdb721d4b0d4f9b87f5e884 /Firestore/Source/Local/FSTLevelDB.mm
parentbf550507ffa8beee149383a5bf1e2363bccefbb4 (diff)
Release 4.3.0 (#327)
Initial release of Firestore at 0.8.0 Bump FirebaseCommunity to 0.1.3
Diffstat (limited to 'Firestore/Source/Local/FSTLevelDB.mm')
-rw-r--r--Firestore/Source/Local/FSTLevelDB.mm246
1 files changed, 246 insertions, 0 deletions
diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm
new file mode 100644
index 0000000..81e1064
--- /dev/null
+++ b/Firestore/Source/Local/FSTLevelDB.mm
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FSTLevelDB.h"
+
+#include <leveldb/db.h>
+
+#import "FIRFirestoreErrors.h"
+#import "FSTAssert.h"
+#import "FSTDatabaseID.h"
+#import "FSTDatabaseInfo.h"
+#import "FSTLevelDBMutationQueue.h"
+#import "FSTLevelDBQueryCache.h"
+#import "FSTLevelDBRemoteDocumentCache.h"
+#import "FSTLogger.h"
+#import "FSTSerializerBeta.h"
+#import "FSTWriteGroup.h"
+#import "FSTWriteGroupTracker.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+static NSString *const kReservedPathComponent = @"firestore";
+
+using leveldb::DB;
+using leveldb::Options;
+using leveldb::Status;
+using leveldb::WriteOptions;
+
+@interface FSTLevelDB ()
+
+@property(nonatomic, copy) NSString *directory;
+@property(nonatomic, strong) FSTWriteGroupTracker *writeGroupTracker;
+@property(nonatomic, assign, getter=isStarted) BOOL started;
+@property(nonatomic, strong, readonly) FSTLocalSerializer *serializer;
+
+@end
+
+@implementation FSTLevelDB
+
+- (instancetype)initWithDirectory:(NSString *)directory
+ serializer:(FSTLocalSerializer *)serializer {
+ if (self = [super init]) {
+ _directory = [directory copy];
+ _writeGroupTracker = [FSTWriteGroupTracker tracker];
+ _serializer = serializer;
+ }
+ return self;
+}
+
++ (NSString *)documentsDirectory {
+#if TARGET_OS_IPHONE
+ NSArray<NSString *> *directories =
+ NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ return [directories[0] stringByAppendingPathComponent:kReservedPathComponent];
+
+#elif TARGET_OS_MAC
+ NSString *dotPrefixed = [@"." stringByAppendingString:kReservedPathComponent];
+ return [NSHomeDirectory() stringByAppendingPathComponent:dotPrefixed];
+
+#else
+#error "local storage on tvOS"
+// TODO(mcg): Writing to NSDocumentsDirectory on tvOS will fail; we need to write to Caches
+// https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/
+
+#endif
+}
+
++ (NSString *)storageDirectoryForDatabaseInfo:(FSTDatabaseInfo *)databaseInfo
+ documentsDirectory:(NSString *)documentsDirectory {
+ // Use two different path formats:
+ //
+ // * persistenceKey / projectID . databaseID / name
+ // * persistenceKey / projectID / name
+ //
+ // projectIDs are DNS-compatible names and cannot contain dots so there's
+ // no danger of collisions.
+ NSString *directory = documentsDirectory;
+ directory = [directory stringByAppendingPathComponent:databaseInfo.persistenceKey];
+
+ NSString *segment = databaseInfo.databaseID.projectID;
+ if (![databaseInfo.databaseID isDefaultDatabase]) {
+ segment = [NSString stringWithFormat:@"%@.%@", segment, databaseInfo.databaseID.databaseID];
+ }
+ directory = [directory stringByAppendingPathComponent:segment];
+
+ // Reserve one additional path component to allow multiple physical databases
+ directory = [directory stringByAppendingPathComponent:@"main"];
+ return directory;
+}
+
+#pragma mark - Startup
+
+- (BOOL)start:(NSError **)error {
+ FSTAssert(!self.isStarted, @"FSTLevelDB double-started!");
+ self.started = YES;
+ NSString *directory = self.directory;
+ if (![self ensureDirectory:directory error:error]) {
+ return NO;
+ }
+
+ DB *database = [self createDBWithDirectory:directory error:error];
+ if (!database) {
+ return NO;
+ }
+
+ _ptr.reset(database);
+ return YES;
+}
+
+/** Creates the directory at @a directory and marks it as excluded from iCloud backup. */
+- (BOOL)ensureDirectory:(NSString *)directory error:(NSError **)error {
+ NSError *localError;
+ NSFileManager *files = [NSFileManager defaultManager];
+
+ BOOL success = [files createDirectoryAtPath:directory
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&localError];
+ if (!success) {
+ *error =
+ [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeInternal
+ userInfo:@{
+ NSLocalizedDescriptionKey : @"Failed to create persistence directory",
+ NSUnderlyingErrorKey : localError
+ }];
+ return NO;
+ }
+
+ NSURL *dirURL = [NSURL fileURLWithPath:directory];
+ success = [dirURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&localError];
+ if (!success) {
+ *error = [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeInternal
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ @"Failed mark persistence directory as excluded from backups",
+ NSUnderlyingErrorKey : localError
+ }];
+ return NO;
+ }
+
+ return YES;
+}
+
+/** Opens the database within the given directory. */
+- (nullable DB *)createDBWithDirectory:(NSString *)directory error:(NSError **)error {
+ Options options;
+ options.create_if_missing = true;
+
+ DB *database;
+ Status status = DB::Open(options, [directory UTF8String], &database);
+ if (!status.ok()) {
+ if (error) {
+ NSString *name = [directory lastPathComponent];
+ *error =
+ [FSTLevelDB errorWithStatus:status
+ description:@"Failed to create database %@ at path %@", name, directory];
+ }
+ return nullptr;
+ }
+
+ return database;
+}
+
+#pragma mark - Persistence Factory methods
+
+- (id<FSTMutationQueue>)mutationQueueForUser:(FSTUser *)user {
+ return [FSTLevelDBMutationQueue mutationQueueWithUser:user db:_ptr serializer:self.serializer];
+}
+
+- (id<FSTQueryCache>)queryCache {
+ return [[FSTLevelDBQueryCache alloc] initWithDB:_ptr serializer:self.serializer];
+}
+
+- (id<FSTRemoteDocumentCache>)remoteDocumentCache {
+ return [[FSTLevelDBRemoteDocumentCache alloc] initWithDB:_ptr serializer:self.serializer];
+}
+
+- (FSTWriteGroup *)startGroupWithAction:(NSString *)action {
+ return [self.writeGroupTracker startGroupWithAction:action];
+}
+
+- (void)commitGroup:(FSTWriteGroup *)group {
+ [self.writeGroupTracker endGroup:group];
+
+ NSString *description = [group description];
+ FSTLog(@"Committing %@", description);
+
+ Status status = [group writeToDB:_ptr];
+ if (!status.ok()) {
+ FSTFail(@"%@ failed with status: %s, description: %@", group.action, status.ToString().c_str(),
+ description);
+ }
+}
+
+- (void)shutdown {
+ FSTAssert(self.isStarted, @"FSTLevelDB shutdown without start!");
+ self.started = NO;
+ _ptr.reset();
+}
+
+#pragma mark - Error and Status
+
++ (nullable NSError *)errorWithStatus:(Status)status description:(NSString *)description, ... {
+ if (status.ok()) {
+ return nil;
+ }
+
+ va_list args;
+ va_start(args, description);
+
+ NSString *message = [[NSString alloc] initWithFormat:description arguments:args];
+ NSString *reason = [self descriptionOfStatus:status];
+ NSError *result = [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeInternal
+ userInfo:@{
+ NSLocalizedDescriptionKey : message,
+ NSLocalizedFailureReasonErrorKey : reason
+ }];
+
+ va_end(args);
+
+ return result;
+}
+
++ (NSString *)descriptionOfStatus:(Status)status {
+ return [NSString stringWithCString:status.ToString().c_str() encoding:NSUTF8StringEncoding];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END