diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.pbxproj b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.pbxproj new file mode 100644 index 0000000..da33c81 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.pbxproj @@ -0,0 +1,852 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + B9172DFC1F8E476F00B3D146 /* RepositoriesTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B9172DFB1F8E476F00B3D146 /* RepositoriesTableViewCell.m */; }; + B9172E091F8E9F9600B3D146 /* star.png in Resources */ = {isa = PBXBuildFile; fileRef = B9172E071F8E9F9200B3D146 /* star.png */; }; + B9172E0A1F8E9F9A00B3D146 /* fork.png in Resources */ = {isa = PBXBuildFile; fileRef = B9172E081F8E9F9200B3D146 /* fork.png */; }; + B9172E0C1F8EA2DF00B3D146 /* avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = B9172E0B1F8EA2D900B3D146 /* avatar.png */; }; + B9172E0E1F8EFD3100B3D146 /* Constantes.h in Sources */ = {isa = PBXBuildFile; fileRef = B9172E0D1F8EFD3100B3D146 /* Constantes.h */; }; + B9172E111F8F195B00B3D146 /* LoadingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B9172E101F8F195B00B3D146 /* LoadingTableViewCell.m */; }; + B9197EAC1F94DDBA0068E78B /* ServicePullRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B9197EAB1F94DDB90068E78B /* ServicePullRequest.m */; }; + B9197EAE1F9541190068E78B /* RequestBaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B9197EAD1F9541190068E78B /* RequestBaseTests.m */; }; + B9197EB31F9580CA0068E78B /* ActivityIndicatorViewBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B9197EB21F9580CA0068E78B /* ActivityIndicatorViewBase.m */; }; + B94BF3941F93969A00C39EE3 /* DatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF3931F93969A00C39EE3 /* DatabaseManager.m */; }; + B94BF3951F93969A00C39EE3 /* DatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF3931F93969A00C39EE3 /* DatabaseManager.m */; }; + B94BF3981F94031B00C39EE3 /* ServiceBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF3971F94031B00C39EE3 /* ServiceBase.m */; }; + B94BF3991F94031B00C39EE3 /* ServiceBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF3971F94031B00C39EE3 /* ServiceBase.m */; }; + B94BF39C1F94038200C39EE3 /* RequestBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF39B1F94038200C39EE3 /* RequestBase.m */; }; + B94BF39D1F94038200C39EE3 /* RequestBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF39B1F94038200C39EE3 /* RequestBase.m */; }; + B94BF3A01F9408E400C39EE3 /* ServiceRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF39F1F9408E400C39EE3 /* ServiceRepository.m */; }; + B94BF3A11F9408E400C39EE3 /* ServiceRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = B94BF39F1F9408E400C39EE3 /* ServiceRepository.m */; }; + B959FEBC1F8DBC7B001D8369 /* Repository.m in Sources */ = {isa = PBXBuildFile; fileRef = B959FEBB1F8DBC7B001D8369 /* Repository.m */; }; + B959FEBF1F8DBD4C001D8369 /* Owner.m in Sources */ = {isa = PBXBuildFile; fileRef = B959FEBE1F8DBD4C001D8369 /* Owner.m */; }; + B959FEC21F8DC17B001D8369 /* LibraryAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = B959FEC11F8DC17B001D8369 /* LibraryAPI.m */; }; + B990BAEC1F8CE90F00B32E43 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B990BAEB1F8CE90F00B32E43 /* AppDelegate.m */; }; + B990BAF21F8CE90F00B32E43 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B990BAF01F8CE90F00B32E43 /* Main.storyboard */; }; + B990BAF41F8CE90F00B32E43 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B990BAF31F8CE90F00B32E43 /* Assets.xcassets */; }; + B990BAF71F8CE90F00B32E43 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B990BAF51F8CE90F00B32E43 /* LaunchScreen.storyboard */; }; + B990BAFA1F8CE90F00B32E43 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B990BAF91F8CE90F00B32E43 /* main.m */; }; + B990BB0F1F8CE90F00B32E43 /* Desafio_Mobile_iOSUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B990BB0E1F8CE90F00B32E43 /* Desafio_Mobile_iOSUITests.m */; }; + B990BB1E1F8CEBDA00B32E43 /* PrincipalTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B990BB1D1F8CEBDA00B32E43 /* PrincipalTableViewController.m */; }; + B9AA7B581F9595C800B1B1EC /* ServiceRepositoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B9AA7B571F9595C800B1B1EC /* ServiceRepositoryTests.m */; }; + B9FF8A311F8F96DF00DB8BB2 /* PullRequestTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B9FF8A301F8F96DF00DB8BB2 /* PullRequestTableViewController.m */; }; + B9FF8A341F8F977300DB8BB2 /* PullRequestTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B9FF8A331F8F977300DB8BB2 /* PullRequestTableViewCell.m */; }; + B9FF8A371F8F9CBC00DB8BB2 /* PullRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B9FF8A361F8F9CBC00DB8BB2 /* PullRequest.m */; }; + DB4A608DDAA104ED6500B689 /* libPods-Desafio Mobile iOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00FDD90A52FC797F2C3A3835 /* libPods-Desafio Mobile iOSTests.a */; }; + E33320E54CB5A7F0E3D84218 /* libPods-Desafio Mobile iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6267806445F8B53D174B00BB /* libPods-Desafio Mobile iOS.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B990BB001F8CE90F00B32E43 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B990BADF1F8CE90F00B32E43 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B990BAE61F8CE90F00B32E43; + remoteInfo = "Desafio Mobile iOS"; + }; + B990BB0B1F8CE90F00B32E43 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B990BADF1F8CE90F00B32E43 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B990BAE61F8CE90F00B32E43; + remoteInfo = "Desafio Mobile iOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00FDD90A52FC797F2C3A3835 /* libPods-Desafio Mobile iOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Desafio Mobile iOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1E4C84044C6F0E8381693CEB /* Pods-Desafio Mobile iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Desafio Mobile iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Desafio Mobile iOS/Pods-Desafio Mobile iOS.release.xcconfig"; sourceTree = ""; }; + 25F8161FFD4046F004A93036 /* Pods-Desafio Mobile iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Desafio Mobile iOSTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Desafio Mobile iOSTests/Pods-Desafio Mobile iOSTests.release.xcconfig"; sourceTree = ""; }; + 563EF557B270144308318F46 /* Pods-Desafio Mobile iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Desafio Mobile iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Desafio Mobile iOS/Pods-Desafio Mobile iOS.debug.xcconfig"; sourceTree = ""; }; + 6267806445F8B53D174B00BB /* libPods-Desafio Mobile iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Desafio Mobile iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B9172DFA1F8E476F00B3D146 /* RepositoriesTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RepositoriesTableViewCell.h; sourceTree = ""; }; + B9172DFB1F8E476F00B3D146 /* RepositoriesTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RepositoriesTableViewCell.m; sourceTree = ""; }; + B9172E071F8E9F9200B3D146 /* star.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = star.png; sourceTree = ""; }; + B9172E081F8E9F9200B3D146 /* fork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fork.png; sourceTree = ""; }; + B9172E0B1F8EA2D900B3D146 /* avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = avatar.png; sourceTree = ""; }; + B9172E0D1F8EFD3100B3D146 /* Constantes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constantes.h; sourceTree = ""; }; + B9172E0F1F8F195B00B3D146 /* LoadingTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoadingTableViewCell.h; sourceTree = ""; }; + B9172E101F8F195B00B3D146 /* LoadingTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoadingTableViewCell.m; sourceTree = ""; }; + B9197EAA1F94DDB90068E78B /* ServicePullRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServicePullRequest.h; sourceTree = ""; }; + B9197EAB1F94DDB90068E78B /* ServicePullRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServicePullRequest.m; sourceTree = ""; }; + B9197EAD1F9541190068E78B /* RequestBaseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RequestBaseTests.m; sourceTree = ""; }; + B9197EB11F9580CA0068E78B /* ActivityIndicatorViewBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityIndicatorViewBase.h; sourceTree = ""; }; + B9197EB21F9580CA0068E78B /* ActivityIndicatorViewBase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ActivityIndicatorViewBase.m; sourceTree = ""; }; + B94BF3921F93969A00C39EE3 /* DatabaseManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = ""; }; + B94BF3931F93969A00C39EE3 /* DatabaseManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DatabaseManager.m; sourceTree = ""; }; + B94BF3961F94031B00C39EE3 /* ServiceBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServiceBase.h; sourceTree = ""; }; + B94BF3971F94031B00C39EE3 /* ServiceBase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServiceBase.m; sourceTree = ""; }; + B94BF39A1F94038200C39EE3 /* RequestBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RequestBase.h; sourceTree = ""; }; + B94BF39B1F94038200C39EE3 /* RequestBase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RequestBase.m; sourceTree = ""; }; + B94BF39E1F9408E400C39EE3 /* ServiceRepository.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServiceRepository.h; sourceTree = ""; }; + B94BF39F1F9408E400C39EE3 /* ServiceRepository.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServiceRepository.m; sourceTree = ""; }; + B959FEBA1F8DBC7B001D8369 /* Repository.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Repository.h; sourceTree = ""; }; + B959FEBB1F8DBC7B001D8369 /* Repository.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Repository.m; sourceTree = ""; }; + B959FEBD1F8DBD4C001D8369 /* Owner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Owner.h; sourceTree = ""; }; + B959FEBE1F8DBD4C001D8369 /* Owner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Owner.m; sourceTree = ""; }; + B959FEC01F8DC17B001D8369 /* LibraryAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LibraryAPI.h; sourceTree = ""; }; + B959FEC11F8DC17B001D8369 /* LibraryAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LibraryAPI.m; sourceTree = ""; }; + B990BAE71F8CE90F00B32E43 /* Desafio Mobile iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Desafio Mobile iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + B990BAEA1F8CE90F00B32E43 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + B990BAEB1F8CE90F00B32E43 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + B990BAF11F8CE90F00B32E43 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B990BAF31F8CE90F00B32E43 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B990BAF61F8CE90F00B32E43 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + B990BAF81F8CE90F00B32E43 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B990BAF91F8CE90F00B32E43 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B990BAFF1F8CE90F00B32E43 /* Desafio Mobile iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Desafio Mobile iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + B990BB051F8CE90F00B32E43 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B990BB0A1F8CE90F00B32E43 /* Desafio Mobile iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Desafio Mobile iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + B990BB0E1F8CE90F00B32E43 /* Desafio_Mobile_iOSUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Desafio_Mobile_iOSUITests.m; sourceTree = ""; }; + B990BB101F8CE90F00B32E43 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B990BB1C1F8CEBDA00B32E43 /* PrincipalTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrincipalTableViewController.h; sourceTree = ""; }; + B990BB1D1F8CEBDA00B32E43 /* PrincipalTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrincipalTableViewController.m; sourceTree = ""; }; + B9AA7B571F9595C800B1B1EC /* ServiceRepositoryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServiceRepositoryTests.m; sourceTree = ""; }; + B9FF8A2F1F8F96DF00DB8BB2 /* PullRequestTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PullRequestTableViewController.h; sourceTree = ""; }; + B9FF8A301F8F96DF00DB8BB2 /* PullRequestTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PullRequestTableViewController.m; sourceTree = ""; }; + B9FF8A321F8F977300DB8BB2 /* PullRequestTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PullRequestTableViewCell.h; sourceTree = ""; }; + B9FF8A331F8F977300DB8BB2 /* PullRequestTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PullRequestTableViewCell.m; sourceTree = ""; }; + B9FF8A351F8F9CBC00DB8BB2 /* PullRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PullRequest.h; sourceTree = ""; }; + B9FF8A361F8F9CBC00DB8BB2 /* PullRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PullRequest.m; sourceTree = ""; }; + EF9FC8BBBB3B189CE2F730CA /* Pods-Desafio Mobile iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Desafio Mobile iOSTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Desafio Mobile iOSTests/Pods-Desafio Mobile iOSTests.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B990BAE41F8CE90F00B32E43 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E33320E54CB5A7F0E3D84218 /* libPods-Desafio Mobile iOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BAFC1F8CE90F00B32E43 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DB4A608DDAA104ED6500B689 /* libPods-Desafio Mobile iOSTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BB071F8CE90F00B32E43 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A5614C021FA24CC411E482FA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6267806445F8B53D174B00BB /* libPods-Desafio Mobile iOS.a */, + 00FDD90A52FC797F2C3A3835 /* libPods-Desafio Mobile iOSTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + B6A8CDDFA9992DF1C9D8572E /* Pods */ = { + isa = PBXGroup; + children = ( + 563EF557B270144308318F46 /* Pods-Desafio Mobile iOS.debug.xcconfig */, + 1E4C84044C6F0E8381693CEB /* Pods-Desafio Mobile iOS.release.xcconfig */, + EF9FC8BBBB3B189CE2F730CA /* Pods-Desafio Mobile iOSTests.debug.xcconfig */, + 25F8161FFD4046F004A93036 /* Pods-Desafio Mobile iOSTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + B9172DFD1F8E478D00B3D146 /* Controller */ = { + isa = PBXGroup; + children = ( + B990BB1C1F8CEBDA00B32E43 /* PrincipalTableViewController.h */, + B990BB1D1F8CEBDA00B32E43 /* PrincipalTableViewController.m */, + B9FF8A2F1F8F96DF00DB8BB2 /* PullRequestTableViewController.h */, + B9FF8A301F8F96DF00DB8BB2 /* PullRequestTableViewController.m */, + ); + path = Controller; + sourceTree = ""; + }; + B9172E121F8F197000B3D146 /* View */ = { + isa = PBXGroup; + children = ( + B9172DFA1F8E476F00B3D146 /* RepositoriesTableViewCell.h */, + B9172DFB1F8E476F00B3D146 /* RepositoriesTableViewCell.m */, + B9FF8A321F8F977300DB8BB2 /* PullRequestTableViewCell.h */, + B9FF8A331F8F977300DB8BB2 /* PullRequestTableViewCell.m */, + B9172E0F1F8F195B00B3D146 /* LoadingTableViewCell.h */, + B9172E101F8F195B00B3D146 /* LoadingTableViewCell.m */, + B9197EB11F9580CA0068E78B /* ActivityIndicatorViewBase.h */, + B9197EB21F9580CA0068E78B /* ActivityIndicatorViewBase.m */, + ); + path = View; + sourceTree = ""; + }; + B94229361F924F0800A46760 /* Imagens */ = { + isa = PBXGroup; + children = ( + B9172E081F8E9F9200B3D146 /* fork.png */, + B9172E071F8E9F9200B3D146 /* star.png */, + B9172E0B1F8EA2D900B3D146 /* avatar.png */, + ); + path = Imagens; + sourceTree = ""; + }; + B990BADE1F8CE90F00B32E43 = { + isa = PBXGroup; + children = ( + B990BAE91F8CE90F00B32E43 /* Desafio Mobile iOS */, + B990BB021F8CE90F00B32E43 /* Desafio Mobile iOSTests */, + B990BB0D1F8CE90F00B32E43 /* Desafio Mobile iOSUITests */, + B990BAE81F8CE90F00B32E43 /* Products */, + B6A8CDDFA9992DF1C9D8572E /* Pods */, + A5614C021FA24CC411E482FA /* Frameworks */, + ); + sourceTree = ""; + }; + B990BAE81F8CE90F00B32E43 /* Products */ = { + isa = PBXGroup; + children = ( + B990BAE71F8CE90F00B32E43 /* Desafio Mobile iOS.app */, + B990BAFF1F8CE90F00B32E43 /* Desafio Mobile iOSTests.xctest */, + B990BB0A1F8CE90F00B32E43 /* Desafio Mobile iOSUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + B990BAE91F8CE90F00B32E43 /* Desafio Mobile iOS */ = { + isa = PBXGroup; + children = ( + B990BB281F8D032900B32E43 /* Models */, + B990BB1F1F8CFE1900B32E43 /* API */, + B990BAEA1F8CE90F00B32E43 /* AppDelegate.h */, + B990BAEB1F8CE90F00B32E43 /* AppDelegate.m */, + B9172E121F8F197000B3D146 /* View */, + B9172DFD1F8E478D00B3D146 /* Controller */, + B990BAF01F8CE90F00B32E43 /* Main.storyboard */, + B990BAF31F8CE90F00B32E43 /* Assets.xcassets */, + B990BAF51F8CE90F00B32E43 /* LaunchScreen.storyboard */, + B990BAF81F8CE90F00B32E43 /* Info.plist */, + B990BAF91F8CE90F00B32E43 /* main.m */, + B9172E0D1F8EFD3100B3D146 /* Constantes.h */, + B94229361F924F0800A46760 /* Imagens */, + ); + path = "Desafio Mobile iOS"; + sourceTree = ""; + }; + B990BB021F8CE90F00B32E43 /* Desafio Mobile iOSTests */ = { + isa = PBXGroup; + children = ( + B9AA7B571F9595C800B1B1EC /* ServiceRepositoryTests.m */, + B9197EAD1F9541190068E78B /* RequestBaseTests.m */, + B990BB051F8CE90F00B32E43 /* Info.plist */, + ); + path = "Desafio Mobile iOSTests"; + sourceTree = ""; + }; + B990BB0D1F8CE90F00B32E43 /* Desafio Mobile iOSUITests */ = { + isa = PBXGroup; + children = ( + B990BB0E1F8CE90F00B32E43 /* Desafio_Mobile_iOSUITests.m */, + B990BB101F8CE90F00B32E43 /* Info.plist */, + ); + path = "Desafio Mobile iOSUITests"; + sourceTree = ""; + }; + B990BB1F1F8CFE1900B32E43 /* API */ = { + isa = PBXGroup; + children = ( + B959FEC01F8DC17B001D8369 /* LibraryAPI.h */, + B959FEC11F8DC17B001D8369 /* LibraryAPI.m */, + B94BF3961F94031B00C39EE3 /* ServiceBase.h */, + B94BF3971F94031B00C39EE3 /* ServiceBase.m */, + B94BF39E1F9408E400C39EE3 /* ServiceRepository.h */, + B94BF39F1F9408E400C39EE3 /* ServiceRepository.m */, + B9197EAA1F94DDB90068E78B /* ServicePullRequest.h */, + B9197EAB1F94DDB90068E78B /* ServicePullRequest.m */, + B94BF39A1F94038200C39EE3 /* RequestBase.h */, + B94BF39B1F94038200C39EE3 /* RequestBase.m */, + B94BF3921F93969A00C39EE3 /* DatabaseManager.h */, + B94BF3931F93969A00C39EE3 /* DatabaseManager.m */, + ); + path = API; + sourceTree = ""; + }; + B990BB281F8D032900B32E43 /* Models */ = { + isa = PBXGroup; + children = ( + B959FEBA1F8DBC7B001D8369 /* Repository.h */, + B959FEBB1F8DBC7B001D8369 /* Repository.m */, + B959FEBD1F8DBD4C001D8369 /* Owner.h */, + B959FEBE1F8DBD4C001D8369 /* Owner.m */, + B9FF8A351F8F9CBC00DB8BB2 /* PullRequest.h */, + B9FF8A361F8F9CBC00DB8BB2 /* PullRequest.m */, + ); + path = Models; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B990BAE61F8CE90F00B32E43 /* Desafio Mobile iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = B990BB131F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOS" */; + buildPhases = ( + 9918EF0018E4695DF946D1EB /* [CP] Check Pods Manifest.lock */, + B990BAE31F8CE90F00B32E43 /* Sources */, + B990BAE41F8CE90F00B32E43 /* Frameworks */, + B990BAE51F8CE90F00B32E43 /* Resources */, + 4BCB578C993E0C6C7EADD669 /* [CP] Embed Pods Frameworks */, + EC116DE7DF046DDA128D3838 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Desafio Mobile iOS"; + productName = "Desafio Mobile iOS"; + productReference = B990BAE71F8CE90F00B32E43 /* Desafio Mobile iOS.app */; + productType = "com.apple.product-type.application"; + }; + B990BAFE1F8CE90F00B32E43 /* Desafio Mobile iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B990BB161F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOSTests" */; + buildPhases = ( + 915DBE68DF37D72DCEBDB69D /* [CP] Check Pods Manifest.lock */, + B990BAFB1F8CE90F00B32E43 /* Sources */, + B990BAFC1F8CE90F00B32E43 /* Frameworks */, + B990BAFD1F8CE90F00B32E43 /* Resources */, + 8A000D06E68E5BE792385421 /* [CP] Embed Pods Frameworks */, + CCB2CEADD9927C5624E1808A /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + B990BB011F8CE90F00B32E43 /* PBXTargetDependency */, + ); + name = "Desafio Mobile iOSTests"; + productName = "Desafio Mobile iOSTests"; + productReference = B990BAFF1F8CE90F00B32E43 /* Desafio Mobile iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + B990BB091F8CE90F00B32E43 /* Desafio Mobile iOSUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B990BB191F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOSUITests" */; + buildPhases = ( + B990BB061F8CE90F00B32E43 /* Sources */, + B990BB071F8CE90F00B32E43 /* Frameworks */, + B990BB081F8CE90F00B32E43 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B990BB0C1F8CE90F00B32E43 /* PBXTargetDependency */, + ); + name = "Desafio Mobile iOSUITests"; + productName = "Desafio Mobile iOSUITests"; + productReference = B990BB0A1F8CE90F00B32E43 /* Desafio Mobile iOSUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B990BADF1F8CE90F00B32E43 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "Adriano Rezena"; + TargetAttributes = { + B990BAE61F8CE90F00B32E43 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; + B990BAFE1F8CE90F00B32E43 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + TestTargetID = B990BAE61F8CE90F00B32E43; + }; + B990BB091F8CE90F00B32E43 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + TestTargetID = B990BAE61F8CE90F00B32E43; + }; + }; + }; + buildConfigurationList = B990BAE21F8CE90F00B32E43 /* Build configuration list for PBXProject "Desafio Mobile iOS" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B990BADE1F8CE90F00B32E43; + productRefGroup = B990BAE81F8CE90F00B32E43 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B990BAE61F8CE90F00B32E43 /* Desafio Mobile iOS */, + B990BAFE1F8CE90F00B32E43 /* Desafio Mobile iOSTests */, + B990BB091F8CE90F00B32E43 /* Desafio Mobile iOSUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B990BAE51F8CE90F00B32E43 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B9172E0C1F8EA2DF00B3D146 /* avatar.png in Resources */, + B9172E091F8E9F9600B3D146 /* star.png in Resources */, + B990BAF71F8CE90F00B32E43 /* LaunchScreen.storyboard in Resources */, + B990BAF41F8CE90F00B32E43 /* Assets.xcassets in Resources */, + B990BAF21F8CE90F00B32E43 /* Main.storyboard in Resources */, + B9172E0A1F8E9F9A00B3D146 /* fork.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BAFD1F8CE90F00B32E43 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BB081F8CE90F00B32E43 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4BCB578C993E0C6C7EADD669 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Desafio Mobile iOS/Pods-Desafio Mobile iOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8A000D06E68E5BE792385421 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Desafio Mobile iOSTests/Pods-Desafio Mobile iOSTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 915DBE68DF37D72DCEBDB69D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Desafio Mobile iOSTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9918EF0018E4695DF946D1EB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Desafio Mobile iOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CCB2CEADD9927C5624E1808A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Desafio Mobile iOSTests/Pods-Desafio Mobile iOSTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + EC116DE7DF046DDA128D3838 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Desafio Mobile iOS/Pods-Desafio Mobile iOS-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B990BAE31F8CE90F00B32E43 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B9172E111F8F195B00B3D146 /* LoadingTableViewCell.m in Sources */, + B959FEC21F8DC17B001D8369 /* LibraryAPI.m in Sources */, + B9197EAC1F94DDBA0068E78B /* ServicePullRequest.m in Sources */, + B9172E0E1F8EFD3100B3D146 /* Constantes.h in Sources */, + B959FEBF1F8DBD4C001D8369 /* Owner.m in Sources */, + B990BAFA1F8CE90F00B32E43 /* main.m in Sources */, + B990BB1E1F8CEBDA00B32E43 /* PrincipalTableViewController.m in Sources */, + B94BF3981F94031B00C39EE3 /* ServiceBase.m in Sources */, + B9197EB31F9580CA0068E78B /* ActivityIndicatorViewBase.m in Sources */, + B94BF39C1F94038200C39EE3 /* RequestBase.m in Sources */, + B9FF8A311F8F96DF00DB8BB2 /* PullRequestTableViewController.m in Sources */, + B9FF8A341F8F977300DB8BB2 /* PullRequestTableViewCell.m in Sources */, + B9172DFC1F8E476F00B3D146 /* RepositoriesTableViewCell.m in Sources */, + B990BAEC1F8CE90F00B32E43 /* AppDelegate.m in Sources */, + B9FF8A371F8F9CBC00DB8BB2 /* PullRequest.m in Sources */, + B959FEBC1F8DBC7B001D8369 /* Repository.m in Sources */, + B94BF3941F93969A00C39EE3 /* DatabaseManager.m in Sources */, + B94BF3A01F9408E400C39EE3 /* ServiceRepository.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BAFB1F8CE90F00B32E43 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B94BF3A11F9408E400C39EE3 /* ServiceRepository.m in Sources */, + B94BF39D1F94038200C39EE3 /* RequestBase.m in Sources */, + B94BF3951F93969A00C39EE3 /* DatabaseManager.m in Sources */, + B9197EAE1F9541190068E78B /* RequestBaseTests.m in Sources */, + B94BF3991F94031B00C39EE3 /* ServiceBase.m in Sources */, + B9AA7B581F9595C800B1B1EC /* ServiceRepositoryTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B990BB061F8CE90F00B32E43 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B990BB0F1F8CE90F00B32E43 /* Desafio_Mobile_iOSUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B990BB011F8CE90F00B32E43 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B990BAE61F8CE90F00B32E43 /* Desafio Mobile iOS */; + targetProxy = B990BB001F8CE90F00B32E43 /* PBXContainerItemProxy */; + }; + B990BB0C1F8CE90F00B32E43 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B990BAE61F8CE90F00B32E43 /* Desafio Mobile iOS */; + targetProxy = B990BB0B1F8CE90F00B32E43 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + B990BAF01F8CE90F00B32E43 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B990BAF11F8CE90F00B32E43 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + B990BAF51F8CE90F00B32E43 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B990BAF61F8CE90F00B32E43 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B990BB111F8CE90F00B32E43 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + B990BB121F8CE90F00B32E43 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B990BB141F8CE90F00B32E43 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 563EF557B270144308318F46 /* Pods-Desafio Mobile iOS.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B990BB151F8CE90F00B32E43 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1E4C84044C6F0E8381693CEB /* Pods-Desafio Mobile iOS.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + B990BB171F8CE90F00B32E43 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EF9FC8BBBB3B189CE2F730CA /* Pods-Desafio Mobile iOSTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Desafio Mobile iOS.app/Desafio Mobile iOS"; + }; + name = Debug; + }; + B990BB181F8CE90F00B32E43 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 25F8161FFD4046F004A93036 /* Pods-Desafio Mobile iOSTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Desafio Mobile iOS.app/Desafio Mobile iOS"; + }; + name = Release; + }; + B990BB1A1F8CE90F00B32E43 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOSUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Desafio Mobile iOS"; + }; + name = Debug; + }; + B990BB1B1F8CE90F00B32E43 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KB4L93EL6S; + INFOPLIST_FILE = "Desafio Mobile iOSUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "br.com.rezena.Desafio-Mobile-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Desafio Mobile iOS"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B990BAE21F8CE90F00B32E43 /* Build configuration list for PBXProject "Desafio Mobile iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B990BB111F8CE90F00B32E43 /* Debug */, + B990BB121F8CE90F00B32E43 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B990BB131F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B990BB141F8CE90F00B32E43 /* Debug */, + B990BB151F8CE90F00B32E43 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B990BB161F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B990BB171F8CE90F00B32E43 /* Debug */, + B990BB181F8CE90F00B32E43 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B990BB191F8CE90F00B32E43 /* Build configuration list for PBXNativeTarget "Desafio Mobile iOSUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B990BB1A1F8CE90F00B32E43 /* Debug */, + B990BB1B1F8CE90F00B32E43 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B990BADF1F8CE90F00B32E43 /* Project object */; +} diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..bd3e300 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..c25f2b2 Binary files /dev/null and b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/project.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Desafio Mobile iOS.xcscheme b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Desafio Mobile iOS.xcscheme new file mode 100644 index 0000000..c3c60f7 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Desafio Mobile iOS.xcscheme @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..328fbee --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + Desafio Mobile iOS.xcscheme + + orderHint + 3 + + + SuppressBuildableAutocreation + + B990BAE61F8CE90F00B32E43 + + primary + + + B990BAFE1F8CE90F00B32E43 + + primary + + + B990BB091F8CE90F00B32E43 + + primary + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/contents.xcworkspacedata b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..a12dc7b --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..b521185 Binary files /dev/null and b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..ff90d34 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS.xcworkspace/xcuserdata/adriano.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,1311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.h new file mode 100644 index 0000000..ca1dcc2 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.h @@ -0,0 +1,25 @@ +// +// DatabaseManager.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import +#import "Repository.h" +#import "PullRequest.h" + +@interface DatabaseManager : NSObject + ++(DatabaseManager *) sharedInstance; + +// Repository +-(void)createRepository: (NSDictionary *)jsonRepository jsonUser:(NSDictionary *)jsonUser; + +// Pull request +-(void)createPullRequest: (NSDictionary *)jsonPullRequest jsonUser:(NSDictionary *)jsonUser updateString:(NSString *)updateString; +-(void)deletePullRequestsFromRepository:(NSInteger) repositoryId withUpdateString:(NSString *)updateString; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.m new file mode 100644 index 0000000..2d55552 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/DatabaseManager.m @@ -0,0 +1,103 @@ +// +// DatabaseManager.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "DatabaseManager.h" + +@implementation DatabaseManager + ++(DatabaseManager *) sharedInstance { + static DatabaseManager *_sharedInstance = nil; + static dispatch_once_t oncePredicate; + + dispatch_once(&oncePredicate, ^{ + _sharedInstance = [[DatabaseManager alloc] init]; + }); + + return _sharedInstance; +} + + +-(void)createRepository: (NSDictionary *)jsonRepository jsonUser:(NSDictionary *)jsonUser { + RLMRealm *realm = [RLMRealm defaultRealm]; + + Repository *repository = [Repository new]; + repository.id = [jsonRepository[@"id"] integerValue]; + repository.name = jsonRepository[@"name"]; + repository.repDescription = jsonRepository[@"description"]; + repository.forks_count = [jsonRepository[@"forks_count"] integerValue]; + repository.stargazers_count = [jsonRepository[@"stargazers_count"] integerValue]; + repository.pulls_url = [jsonRepository[@"pulls_url"] stringByReplacingOccurrencesOfString:@"{/number}" withString:@""]; + + repository.owner = [self createOwner:jsonUser]; + + if ([[jsonUser allKeys] containsObject:@"name"]) { + repository.owner.name = jsonUser[@"name"]; + } + + [realm beginWriteTransaction]; + [realm addOrUpdateObject:repository]; + [realm commitWriteTransaction]; +} + + +-(Owner *)createOwner:(NSDictionary *)jsonOwner { + Owner *owner = [Owner new]; + + owner.login = jsonOwner[@"login"]; + owner.avatar_url = jsonOwner[@"avatar_url"]; + owner.url = jsonOwner[@"url"]; + + return owner; +} + + +-(void)createPullRequest: (NSDictionary *)jsonPullRequest jsonUser:(NSDictionary *)jsonUser updateString:(NSString *)updateString { + RLMRealm *realm = [RLMRealm defaultRealm]; + + PullRequest *pullRequest = [PullRequest new]; + + NSDictionary *repo = jsonPullRequest[@"base"][@"repo"]; + pullRequest.repositoryId = [repo[@"id"] integerValue]; + + pullRequest.id = [jsonPullRequest[@"id"] integerValue]; + pullRequest.url = jsonPullRequest[@"html_url"]; + pullRequest.state = jsonPullRequest[@"state"]; + pullRequest.title = jsonPullRequest[@"title"]; + pullRequest.body = jsonPullRequest[@"body"]; + pullRequest.updateString = updateString; + + NSDateFormatter *dateFormatter = [NSDateFormatter new]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; + pullRequest.created_at = [dateFormatter dateFromString:jsonPullRequest[@"created_at"]]; + + pullRequest.owner = [self createOwner:jsonUser]; + + if ([[jsonUser allKeys] containsObject:@"name"]) { + pullRequest.owner.name = jsonUser[@"name"]; + } + + [realm beginWriteTransaction]; + [realm addOrUpdateObject:pullRequest]; + [realm commitWriteTransaction]; +} + +-(void)deletePullRequestsFromRepository:(NSInteger)repositoryId withUpdateString:(NSString *)updateString { + RLMRealm *realm = [RLMRealm defaultRealm]; + + RLMResults *pullRequests = [PullRequest objectsWhere:@"repositoryId = %i", repositoryId]; + + for (PullRequest *pullRequest in pullRequests) { + if (![pullRequest.updateString isEqualToString:updateString]) { + [realm beginWriteTransaction]; + [realm deleteObject:pullRequest]; + [realm commitWriteTransaction]; + } + } +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.h new file mode 100644 index 0000000..771ee03 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.h @@ -0,0 +1,27 @@ +// +// LibraryAPI.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 11/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import +#import "DatabaseManager.h" +#import "Repository.h" +#import "PullRequest.h" +#import "Constantes.h" +#import "RequestBase.h" +#import "ServiceRepository.h" +#import "ServicePullRequest.h" + +@interface LibraryAPI : NSObject + ++(LibraryAPI *) sharedInstance; + +-(void)getRepositories:(int)page; +-(void)getPullRequestsfromRepository:(Repository *) repository; +-(NSDictionary *)requestSynchronousJSONWithURLString:(NSString *)urlString; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.m new file mode 100644 index 0000000..032ee13 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/LibraryAPI.m @@ -0,0 +1,92 @@ +// +// LibraryAPI.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 11/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "LibraryAPI.h" + +static NSString *const kRepositoriesPath = @"https://api.github.com/search/repositories?q=language:Java&sort=stars&page="; + +@implementation LibraryAPI + ++(LibraryAPI *) sharedInstance { + static LibraryAPI *_sharedInstance = nil; + static dispatch_once_t oncePredicate; + + dispatch_once(&oncePredicate, ^{ + _sharedInstance = [[LibraryAPI alloc] init]; + }); + + return _sharedInstance; +} + + +-(void)getRepositories:(int)page { + RequestBase *request = [[RequestBase alloc] initWithURL:[kRepositoriesPath stringByAppendingString:[NSString stringWithFormat:@"%i", page]] httpMethod:@"GET"]; + ServiceRepository *service = [[ServiceRepository alloc] initWithRequest:request]; + + [service retrieveDataWithSuccessHandler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kStrNotificationRepositoriesFinished object:self userInfo:nil]; + }); + + } andDidFailHandler:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kStrNotificationRepositoriesError object:self userInfo:nil]; + }]; +} + + +-(void)getPullRequestsfromRepository:(Repository *) repository { + RequestBase *request = [[RequestBase alloc] initWithURL:repository.pulls_url httpMethod:@"GET"]; + ServicePullRequest *service = [[ServicePullRequest alloc] initWithRequest:request]; + + [service retrieveDataWithSuccessHandler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kStrNotificationPullRequestFinished object:self userInfo:nil]; + }); + + } andDidFailHandler:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kStrNotificationPullRequestError object:self userInfo:nil]; + }]; +} + + +-(NSDictionary *)requestSynchronousJSONWithURLString:(NSString *)urlString { + dispatch_semaphore_t semaphore; + + NSURL *url = [NSURL URLWithString:urlString]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10]; + request.HTTPMethod = @"GET"; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + __block NSMutableDictionary *resultMutableDictionary = [[NSMutableDictionary alloc] init]; + + semaphore = dispatch_semaphore_create(0); + + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data,NSURLResponse *response,NSError *error) { + + if (error) { + NSLog(@"Erro no request: %@", error); + } else { + resultMutableDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; + } + + dispatch_semaphore_signal(semaphore); + }]; + + [task resume]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + return resultMutableDictionary; +} + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.h new file mode 100644 index 0000000..56831c7 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.h @@ -0,0 +1,15 @@ +// +// RequestBase.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import + +@interface RequestBase : NSMutableURLRequest + +- (instancetype)initWithURL:(NSString *)urlString httpMethod:(NSString *)httpMethod; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.m new file mode 100644 index 0000000..0b5aa25 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/RequestBase.m @@ -0,0 +1,23 @@ +// +// RequestBase.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "RequestBase.h" + +@implementation RequestBase + +- (instancetype)initWithURL:(NSString *)urlString httpMethod:(NSString *)httpMethod { + NSURL *url = [NSURL URLWithString:urlString]; + + self = [super initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:50]; + self.HTTPMethod = httpMethod; + [self setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + return self; +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.h new file mode 100644 index 0000000..0292445 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.h @@ -0,0 +1,21 @@ +// +// ServiceBase.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "RequestBase.h" + +@interface ServiceBase : NSObject + +@property (strong, nonatomic) NSURLSessionDataTask *task; + +- (void)retrieveDataWithSuccessHandler:(void(^)(void))successBlock andDidFailHandler:(void(^)(void))failBlock; +- (instancetype)initWithRequest:(RequestBase *)request; +- (void)parseJSON:(NSDictionary *)json; +- (BOOL)jsonIsValid:(NSDictionary *)json; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.m new file mode 100644 index 0000000..12f18f7 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceBase.m @@ -0,0 +1,59 @@ +// +// ServiceBase.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ServiceBase.h" + +@implementation ServiceBase{ + RequestBase *localRequest; +} + +-(instancetype)initWithRequest:(RequestBase *)request { + self = [super init]; + + if (self) { + localRequest = request; + } + + return self; +} + + +-(void)retrieveDataWithSuccessHandler:(void (^)(void))successBlock andDidFailHandler:(void (^)(void))failBlock { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + + self.task = [session dataTaskWithRequest:localRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + if (data) { + NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + + if ([self jsonIsValid:json]) { + [self parseJSON:json]; + successBlock(); + } else { + NSLog(@"Retorno do JSON é inválido (%@)", json); + failBlock(); + } + } else { + failBlock(); + } + }]; + + [self.task resume]; +} + + +-(void)parseJSON:(NSDictionary *)json { + // abstract +} + + +-(BOOL)jsonIsValid:(NSDictionary *)json { + return YES; +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.h new file mode 100644 index 0000000..31d714c --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.h @@ -0,0 +1,15 @@ +// +// ServicePullRequest.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ServiceBase.h" +#import "DatabaseManager.h" +#import "LibraryAPI.h" + +@interface ServicePullRequest : ServiceBase + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.m new file mode 100644 index 0000000..9f30898 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServicePullRequest.m @@ -0,0 +1,47 @@ +// +// ServicePullRequest.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ServicePullRequest.h" + +@implementation ServicePullRequest + +-(void)parseJSON:(NSDictionary *)json { + NSArray *array = [json copy]; + + NSString *updateString = [[NSUUID UUID] UUIDString]; + + for (NSDictionary *dictionary in array) { + NSDictionary *owner = dictionary[@"user"]; + NSDictionary *userJSON = [[LibraryAPI sharedInstance] requestSynchronousJSONWithURLString:owner[@"url"]]; + [[DatabaseManager sharedInstance] createPullRequest:dictionary jsonUser:userJSON updateString:updateString]; + } + + NSDictionary *repo = array[0][@"base"][@"repo"]; + NSInteger repositoryId = [repo[@"id"] integerValue]; + + [[DatabaseManager sharedInstance] deletePullRequestsFromRepository:repositoryId withUpdateString:updateString]; +} + +-(BOOL)jsonIsValid:(NSDictionary *)json { + if (json) { + if ([json count] > 0) { + if ([json isKindOfClass:[NSArray class]]) { + NSArray *array = [json copy]; + return [[array[0] allKeys] containsObject:@"url"]; + } else { + return NO; + } + } else { + return NO; + } + } else { + return NO; + } +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.h b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.h new file mode 100644 index 0000000..833fef1 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.h @@ -0,0 +1,15 @@ +// +// ServiceRepository.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ServiceBase.h" +#import "DatabaseManager.h" +#import "LibraryAPI.h" + +@interface ServiceRepository : ServiceBase + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.m b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.m new file mode 100644 index 0000000..03232c6 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/API/ServiceRepository.m @@ -0,0 +1,34 @@ +// +// ServiceRepository.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 15/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ServiceRepository.h" + +@implementation ServiceRepository + +-(void)parseJSON:(NSDictionary *)json { + NSArray *array = json[@"items"]; + + for (NSDictionary *dictionary in array) { + + NSDictionary *owner = dictionary[@"owner"]; + NSDictionary *userJSON = [[LibraryAPI sharedInstance] requestSynchronousJSONWithURLString:owner[@"url"]]; + + [[DatabaseManager sharedInstance] createRepository:dictionary jsonUser:userJSON]; + } +} + + +-(BOOL)jsonIsValid:(NSDictionary *)json { + if (json) { + return [[json allKeys] containsObject:@"items"]; + } else { + return NO; + } +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.h b/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.h new file mode 100644 index 0000000..6fb5454 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.h @@ -0,0 +1,18 @@ +// +// AppDelegate.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.m b/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.m new file mode 100644 index 0000000..14ae865 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/AppDelegate.m @@ -0,0 +1,68 @@ +// +// AppDelegate.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + + RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; + // Set the new schema version. This must be greater than the previously used + // version (if you've never set a schema version before, the version is 0). + config.schemaVersion = 9; + + // Set the block which will be called automatically when opening a Realm with a + // schema version lower than the one set above + config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) { + // We haven’t migrated anything yet, so oldSchemaVersion == 0 + if (oldSchemaVersion < 1) { + // Nothing to do! + // Realm will automatically detect new properties and removed properties + // And will update the schema on disk automatically + } + }; + + // Tell Realm to use this new configuration object for the default Realm + [RLMRealmConfiguration setDefaultConfiguration:config]; + + // Now that we've told Realm how to handle the schema change, opening the file + // will automatically perform the migration + [RLMRealm defaultRealm]; + + return YES; +} + + +- (void)applicationWillResignActive:(UIApplication *)application { +} + + +- (void)applicationDidEnterBackground:(UIApplication *)application { +} + + +- (void)applicationWillEnterForeground:(UIApplication *)application { +} + + +- (void)applicationDidBecomeActive:(UIApplication *)application { +} + + +- (void)applicationWillTerminate:(UIApplication *)application { +} + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Desafio Mobile iOS/Desafio Mobile iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/LaunchScreen.storyboard b/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fafaed6 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/Main.storyboard b/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/Main.storyboard new file mode 100644 index 0000000..33c5e18 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Base.lproj/Main.storyboard @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Constantes.h b/Desafio Mobile iOS/Desafio Mobile iOS/Constantes.h new file mode 100644 index 0000000..73374b0 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Constantes.h @@ -0,0 +1,18 @@ +// +// Constantes.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 11/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#ifndef Constantes_h +#define Constantes_h + +#define kStrNotificationRepositoriesFinished @"NotificationRepositoriesFinished" +#define kStrNotificationPullRequestFinished @"NotificationPullRequestFinished" +#define kStrNotificationPullRequestError @"NotificationPullRequestError" +#define kStrNotificationRepositoriesError @"NotificationRepositoriesError" +#define kSeguePullRequests @"SeguePullRequests" + +#endif /* Constantes_h */ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.h b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.h new file mode 100644 index 0000000..a2504eb --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.h @@ -0,0 +1,13 @@ +// +// PrincipalTableViewController.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import + +@interface PrincipalTableViewController : UITableViewController + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.m b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.m new file mode 100644 index 0000000..823cca9 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PrincipalTableViewController.m @@ -0,0 +1,146 @@ +// +// PrincipalTableViewController.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "PrincipalTableViewController.h" +#import "LibraryAPI.h" +#import "Constantes.h" +#import "RepositoriesTableViewCell.h" +#import "LoadingTableViewCell.h" +#import "PullRequestTableViewController.h" + +@interface PrincipalTableViewController () { + UIActivityIndicatorView *activityIndicatorView; + NSArray *repositoriesArray; + int page; +} + +@end + +@implementation PrincipalTableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; + self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:self.navigationItem.backBarButtonItem.style target:nil action:nil]; + [self.tableView registerClass:[LoadingTableViewCell class] forCellReuseIdentifier:@"loadingCell"]; + + [self AtualizarView]; + + activityIndicatorView = [ActivityIndicatorViewBase new]; + activityIndicatorView.center = self.tableView.center; + [activityIndicatorView startAnimating]; + [self.tableView addSubview:activityIndicatorView]; + + page = 1; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AtualizarView) name:kStrNotificationRepositoriesFinished object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificarRepositoriesError:) name:kStrNotificationRepositoriesError object:nil]; + [[LibraryAPI sharedInstance] getRepositories:page]; +} + +-(void)notificarRepositoriesError:(NSNotification *)notification { + NSString *mensagem = notification.userInfo[@"mensagem"]; + + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Falha ao atualizar" message:mensagem delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + [alert show]; + + [activityIndicatorView stopAnimating]; + [activityIndicatorView setHidden:YES]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +-(void)AtualizarView { + [activityIndicatorView stopAnimating]; + [activityIndicatorView setHidden:YES]; + + NSMutableArray *repositoriesMutableArray = [NSMutableArray new]; + + repositoriesArray = [NSArray new]; + + RLMRealm *realm = [RLMRealm defaultRealm]; + RLMResults *repository = [Repository allObjects]; + + [realm transactionWithBlock:^{ + for (Repository *repo in repository) { + [repositoriesMutableArray addObject:repo]; + } + }]; + + repositoriesArray = [repositoriesMutableArray copy]; + [self.tableView reloadData]; +} + +//#pragma mark - Notifications + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if ([repositoriesArray count] > 0) { + return [repositoriesArray count] + 1; + } else { + return 0; + } +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + BOOL lastItemReached = indexPath.row >= [repositoriesArray count]; + + if ((lastItemReached) && [repositoriesArray count] > 0) { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"loadingCell" forIndexPath:indexPath]; + page++; + [[LibraryAPI sharedInstance] getRepositories:page]; + return cell; + } else { + if ([repositoriesArray count] > 0) { + RepositoriesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; + Repository *repository = repositoriesArray[indexPath.row]; + + if (repository) { + [cell configureWith:repository]; + } + + return cell; + } else { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + return cell; + } + } +} + +-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 130; +} + +#pragma mark - Navigation + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if ([[segue identifier] isEqualToString:kSeguePullRequests]) { + + NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; + Repository *repository = repositoriesArray[indexPath.row]; + + if (repository.pulls_url) { + PullRequestTableViewController *destinationViewController = [[PullRequestTableViewController alloc] init]; + [destinationViewController setRepository:repository]; + } + } +} + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.h b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.h new file mode 100644 index 0000000..4e3b936 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.h @@ -0,0 +1,17 @@ +// +// PullRequestTableViewController.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "Repository.h" +#import "ActivityIndicatorViewBase.h" + +@interface PullRequestTableViewController : UITableViewController + +- (void)setRepository:(Repository *) repo; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.m b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.m new file mode 100644 index 0000000..17ec469 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Controller/PullRequestTableViewController.m @@ -0,0 +1,149 @@ +// +// PullRequestTableViewController.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "PullRequestTableViewController.h" +#import "PullRequestTableViewCell.h" +#import "LibraryAPI.h" + +@interface PullRequestTableViewController () { + NSArray *pullRequestsArray; + ActivityIndicatorViewBase *activityIndicatorView; +} + +@end + +static Repository *repository; + +@implementation PullRequestTableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.navigationItem.title = repository.name; + [self AtualizarView]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AtualizarView) name:kStrNotificationPullRequestFinished object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificarPullRequestError:) name:kStrNotificationPullRequestError object:nil]; + + activityIndicatorView = [ActivityIndicatorViewBase new]; + activityIndicatorView.center = self.tableView.center; + [activityIndicatorView startAnimating]; + [self.tableView addSubview:activityIndicatorView]; + + [[LibraryAPI sharedInstance] getPullRequestsfromRepository:repository]; +} + + +- (void)setRepository:(Repository *) repo { + repository = repo; +} + + +-(void)notificarPullRequestError:(NSNotification *)notification { + NSString *mensagem = notification.userInfo[@"mensagem"]; + + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Falha ao atualizar" message:mensagem delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + [alert show]; + + [activityIndicatorView stopAnimating]; + [activityIndicatorView setHidden:YES]; +} + + +-(void)AtualizarView { + [activityIndicatorView stopAnimating]; + [activityIndicatorView setHidden:YES]; + + NSMutableArray *pullRequestsMutableArray = [NSMutableArray new]; + pullRequestsArray = [NSArray new]; + + RLMRealm *realm = [RLMRealm defaultRealm]; + RLMResults *pullRequests = [PullRequest objectsWhere:@"repositoryId = %i", repository.id]; + + [realm transactionWithBlock:^{ + for (PullRequest *pull in pullRequests) { + [pullRequestsMutableArray addObject:pull]; + } + }]; + + pullRequestsArray = [pullRequestsMutableArray copy]; + [self.tableView reloadData]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return [pullRequestsArray count]; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + PullRequestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"pullRequestCell" forIndexPath:indexPath]; + + PullRequest *pullRequest = pullRequestsArray[indexPath.row]; + [cell configureWith:pullRequest]; + + return cell; +} + +-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 130; +} + + +-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + + if ([pullRequestsArray count] > 0) { + UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 30)]; + [view setBackgroundColor:[UIColor whiteColor]]; + + int openedCount = 0; + int closedCount = 0; + + for (PullRequest *pullRequest in pullRequestsArray) { + if ([pullRequest.state isEqualToString:@"open"]) { + openedCount++; + } else if ([pullRequest.state isEqualToString:@"closed"]) { + closedCount++; + } + } + + UILabel *openedLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width / 2, view.frame.size.height)]; + openedLabel.textColor = [UIColor orangeColor]; + openedLabel.font = [UIFont boldSystemFontOfSize:13]; + openedLabel.textAlignment = NSTextAlignmentRight; + openedLabel.text = [NSString stringWithFormat:@"%i opened", openedCount]; + [view addSubview:openedLabel]; + + UILabel *closedLabel = [[UILabel alloc] initWithFrame:CGRectMake(view.frame.size.width / 2, 0, view.frame.size.width / 2, view.frame.size.height)]; + closedLabel.textColor = [UIColor blackColor]; + closedLabel.font = [UIFont boldSystemFontOfSize:13]; + closedLabel.text = [NSString stringWithFormat:@"/ %i closed", closedCount]; + [view addSubview:closedLabel]; + + return view; + } else { + return nil; + } +} + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + PullRequest *pullRequest = pullRequestsArray[indexPath.row]; + + NSURL *url = [NSURL URLWithString:pullRequest.url]; + [[UIApplication sharedApplication] openURL:url]; +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/avatar.png b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/avatar.png new file mode 100644 index 0000000..1bd1cee Binary files /dev/null and b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/avatar.png differ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/fork.png b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/fork.png new file mode 100644 index 0000000..091cdc8 Binary files /dev/null and b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/fork.png differ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/star.png b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/star.png new file mode 100644 index 0000000..c74d4ac Binary files /dev/null and b/Desafio Mobile iOS/Desafio Mobile iOS/Imagens/star.png differ diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Info.plist b/Desafio Mobile iOS/Desafio Mobile iOS/Info.plist new file mode 100644 index 0000000..89d7858 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.h b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.h new file mode 100644 index 0000000..f547b34 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.h @@ -0,0 +1,19 @@ +// +// Owner.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import + +@interface Owner : RLMObject + +@property NSString *login; +@property NSString *avatar_url; +@property NSString *url; +@property NSString *name; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.m b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.m new file mode 100644 index 0000000..09adbac --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Owner.m @@ -0,0 +1,13 @@ +// +// Owner.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "Owner.h" + +@implementation Owner + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.h b/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.h new file mode 100644 index 0000000..951e468 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.h @@ -0,0 +1,24 @@ +// +// PullRequest.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "Owner.h" + +@interface PullRequest : RLMObject + +@property NSInteger id; +@property NSInteger repositoryId; +@property NSString *url; +@property NSString *state; +@property NSString *title; +@property NSString *body; +@property NSString *updateString; +@property NSDate *created_at; +@property Owner* owner; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.m b/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.m new file mode 100644 index 0000000..a081b2d --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/PullRequest.m @@ -0,0 +1,18 @@ +// +// PullRequest.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "PullRequest.h" + +@implementation PullRequest + ++ (NSString *)primaryKey { + return @"id"; +} + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.h b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.h new file mode 100644 index 0000000..bfc75e7 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.h @@ -0,0 +1,23 @@ +// +// Repository.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import +#import "Owner.h" + +@interface Repository : RLMObject + +@property NSInteger id; +@property NSString *name; +@property NSString *repDescription; +@property NSInteger forks_count; +@property NSInteger stargazers_count; +@property NSString *pulls_url; +@property Owner* owner; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.m b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.m new file mode 100644 index 0000000..db6d558 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/Models/Repository.m @@ -0,0 +1,17 @@ +// +// Repository.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "Repository.h" + +@implementation Repository + ++ (NSString *)primaryKey { + return @"id"; +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.h b/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.h new file mode 100644 index 0000000..ccc02d7 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.h @@ -0,0 +1,15 @@ +// +// ActivityIndicatorViewBase.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import + +@interface ActivityIndicatorViewBase : UIActivityIndicatorView + +-(instancetype)init; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.m b/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.m new file mode 100644 index 0000000..f027cae --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/ActivityIndicatorViewBase.m @@ -0,0 +1,25 @@ +// +// ActivityIndicatorViewBase.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "ActivityIndicatorViewBase.h" + +@implementation ActivityIndicatorViewBase + +-(instancetype)init { + self = [super init]; + + if (self) { + self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge; + self.color = [UIColor grayColor]; + self.frame = CGRectMake(0, 0, 20, 20); + } + + return self; +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.h b/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.h new file mode 100644 index 0000000..12292ba --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.h @@ -0,0 +1,15 @@ +// +// LoadingTableViewCell.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import + +@interface LoadingTableViewCell : UITableViewCell + +@property (strong, nonatomic) UIActivityIndicatorView *activityIndicatorView; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.m b/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.m new file mode 100644 index 0000000..dea23a3 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/LoadingTableViewCell.m @@ -0,0 +1,42 @@ +// +// LoadingTableViewCell.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "LoadingTableViewCell.h" + +@implementation LoadingTableViewCell + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + + if (self) { + [self initUIElements]; + } + + return self; +} + + +-(void)initUIElements { + self.selectionStyle = UITableViewCellSelectionStyleNone; + + self.activityIndicatorView = [UIActivityIndicatorView new]; + self.activityIndicatorView.frame = CGRectMake(0, 0, 30, 30); + self.activityIndicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge; + [self.activityIndicatorView setColor:[UIColor grayColor]]; + [self.activityIndicatorView startAnimating]; + [self addSubview:self.activityIndicatorView]; +} + + +-(void)layoutSubviews { + self.activityIndicatorView.center = self.contentView.center; + [self.activityIndicatorView setHidden:NO]; +} + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.h b/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.h new file mode 100644 index 0000000..37c1dec --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.h @@ -0,0 +1,24 @@ +// +// PullRequestTableViewCell.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import +#import "PullRequest.h" + +@interface PullRequestTableViewCell : UITableViewCell + +@property (strong, nonatomic) IBOutlet UILabel *titleLabel; +@property (strong, nonatomic) IBOutlet UILabel *usernameLabel; +@property (strong, nonatomic) IBOutlet UILabel *nomeSobrenomeLabel; +@property (strong, nonatomic) IBOutlet UIImageView *avatarImageView; +@property (strong, nonatomic) IBOutlet UILabel *bodyLabel; +@property (strong, nonatomic) IBOutlet UILabel *created_atLabel; + +-(void)configureWith:(PullRequest *)pullRequest; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.m b/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.m new file mode 100644 index 0000000..cf9d905 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/PullRequestTableViewCell.m @@ -0,0 +1,32 @@ +// +// PullRequestTableViewCell.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 12/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "PullRequestTableViewCell.h" + +@implementation PullRequestTableViewCell + +-(void)configureWith:(PullRequest *)pullRequest { + self.avatarImageView.layer.cornerRadius = self.avatarImageView.frame.size.width / 2; + self.avatarImageView.clipsToBounds = YES; + + self.titleLabel.text = pullRequest.title; + self.bodyLabel.text = pullRequest.body; + + NSDateFormatter *dateFormatter = [NSDateFormatter new]; + [dateFormatter setDateFormat:@"dd/MM/yyyy HH:mm:ss"]; + + self.created_atLabel.text = [dateFormatter stringFromDate:pullRequest.created_at]; + + if (pullRequest.owner) { + self.usernameLabel.text = pullRequest.owner.login; + [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString: pullRequest.owner.avatar_url]]; + self.nomeSobrenomeLabel.text = pullRequest.owner.name; + } +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.h b/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.h new file mode 100644 index 0000000..1229305 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.h @@ -0,0 +1,27 @@ +// +// RepositoriesTableViewCell.h +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 11/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import +#import "Repository.h" + +@interface RepositoriesTableViewCell : UITableViewCell + +@property (strong, nonatomic) IBOutlet UILabel *nomeRepositorioLabel; +@property (strong, nonatomic) IBOutlet UILabel *usernameLabel; +@property (strong, nonatomic) IBOutlet UILabel *nomeSobrenomeLabel; +@property (strong, nonatomic) IBOutlet UIImageView *starImageView; +@property (strong, nonatomic) IBOutlet UIImageView *forkImageView; +@property (strong, nonatomic) IBOutlet UIImageView *avatarImageView; +@property (strong, nonatomic) IBOutlet UILabel *descriptionLabel; +@property (strong, nonatomic) IBOutlet UILabel *forkCountLabel; +@property (strong, nonatomic) IBOutlet UILabel *starCountLabel; + +-(void)configureWith:(Repository *)repository; + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.m b/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.m new file mode 100644 index 0000000..39eeb92 --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/View/RepositoriesTableViewCell.m @@ -0,0 +1,37 @@ +// +// RepositoriesTableViewCell.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 11/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import "RepositoriesTableViewCell.h" + +@implementation RepositoriesTableViewCell + +-(void)configureWith:(Repository *)repository { + self.avatarImageView.layer.cornerRadius = self.avatarImageView.frame.size.width / 2; + self.avatarImageView.clipsToBounds = YES; + + self.nomeRepositorioLabel.text = repository.name; + self.descriptionLabel.text = repository.repDescription; + self.starCountLabel.text = [NSString stringWithFormat:@"%i", (int)repository.stargazers_count]; + self.forkCountLabel.text = [NSString stringWithFormat:@"%i", (int)repository.forks_count]; + + if (repository.owner) { + self.usernameLabel.text = repository.owner.login; + + if (repository.owner.avatar_url) { + [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString: repository.owner.avatar_url]]; + self.avatarImageView.backgroundColor = [UIColor clearColor]; + } else { + self.avatarImageView.image = [UIImage imageNamed:@"avatar.png"]; + self.avatarImageView.backgroundColor = [UIColor grayColor]; + } + + self.nomeSobrenomeLabel.text = repository.owner.name; + } +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOS/main.m b/Desafio Mobile iOS/Desafio Mobile iOS/main.m new file mode 100644 index 0000000..3ff9c0d --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOS/main.m @@ -0,0 +1,16 @@ +// +// main.m +// Desafio Mobile iOS +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Desafio Mobile iOS/Desafio Mobile iOSTests/Info.plist b/Desafio Mobile iOS/Desafio Mobile iOSTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOSTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Desafio Mobile iOS/Desafio Mobile iOSTests/RequestBaseTests.m b/Desafio Mobile iOS/Desafio Mobile iOSTests/RequestBaseTests.m new file mode 100644 index 0000000..e86eb5f --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOSTests/RequestBaseTests.m @@ -0,0 +1,45 @@ +// +// RequestBaseTests.m +// Desafio Mobile iOSTests +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "RequestBase.h" + +@interface RequestBaseTests : XCTestCase + +@end + +@implementation RequestBaseTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)testHTTPMethod { + NSString *expectedResult = @"GET"; + RequestBase *request = [[RequestBase alloc] initWithURL:@"www.url.com.br" httpMethod:expectedResult]; + XCTAssertTrue([request.HTTPMethod isEqualToString:expectedResult], @"HTTPMethod strings are not equal %@ %@", expectedResult, request.HTTPMethod); +} + +- (void)testURL { + NSString *expectedResult = @"www.url.com.br"; + RequestBase *request = [[RequestBase alloc] initWithURL:expectedResult httpMethod:@"GET"]; + XCTAssertEqual([request.URL absoluteString], expectedResult, @"URL strings are not equal %@ %@", expectedResult, [request.URL absoluteString]); +} + +- (void)testHeaderFields { + NSString *expectedResult = @"www.url.com.br"; + RequestBase *request = [[RequestBase alloc] initWithURL:expectedResult httpMethod:@"GET"]; + + XCTAssertTrue([[[request allHTTPHeaderFields] allValues] containsObject:@"application/json"]); +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOSTests/ServiceRepositoryTests.m b/Desafio Mobile iOS/Desafio Mobile iOSTests/ServiceRepositoryTests.m new file mode 100644 index 0000000..9fa7c1c --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOSTests/ServiceRepositoryTests.m @@ -0,0 +1,60 @@ +// +// ServiceRepositoryTests.m +// Desafio Mobile iOSTests +// +// Created by Adriano Rezena on 16/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import +#import "ServiceRepository.h" + +@interface ServiceRepositoryTests : XCTestCase + +@end + +@implementation ServiceRepositoryTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)testJsonIsValid { + ServiceRepository *serviceRepository = [ServiceRepository new]; + + NSMutableDictionary *testDictionary = [NSMutableDictionary new]; + [testDictionary setValue:@"3738268" forKey:@"total_count"]; + [testDictionary setValue:[NSNumber numberWithBool:false] forKey:@"incomplete_results"]; + + NSMutableDictionary *testItemsDictionary = [NSMutableDictionary new]; + [testItemsDictionary setValue:@7508411 forKey:@"id"]; + [testItemsDictionary setValue:@"RxJava" forKey:@"name"]; + [testItemsDictionary setValue:@"ReactiveX/RxJava" forKey:@"full_name"]; + + [testDictionary setValue:testItemsDictionary forKey:@"items"]; + [testDictionary setValue:@"https://api.github.com/repos/ReactiveX/RxJava" forKey:@"url"]; + [serviceRepository jsonIsValid:testDictionary]; + + XCTAssertTrue([serviceRepository jsonIsValid:testDictionary], @"JSON is invalid"); +} + +- (void)testJsonIsInvalid { + ServiceRepository *serviceRepository = [ServiceRepository new]; + + NSMutableDictionary *testDictionary = [NSMutableDictionary new]; + [testDictionary setValue:@"3738268" forKey:@"total_count"]; + [testDictionary setValue:[NSNumber numberWithBool:false] forKey:@"incomplete_results"]; + + [testDictionary setValue:@"https://api.github.com/repos/ReactiveX/RxJava" forKey:@"url"]; + [serviceRepository jsonIsValid:testDictionary]; + + XCTAssertFalse([serviceRepository jsonIsValid:testDictionary]); +} + + + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOSUITests/Desafio_Mobile_iOSUITests.m b/Desafio Mobile iOS/Desafio Mobile iOSUITests/Desafio_Mobile_iOSUITests.m new file mode 100644 index 0000000..81aea4a --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOSUITests/Desafio_Mobile_iOSUITests.m @@ -0,0 +1,40 @@ +// +// Desafio_Mobile_iOSUITests.m +// Desafio Mobile iOSUITests +// +// Created by Adriano Rezena on 10/10/17. +// Copyright © 2017 Adriano Rezena. All rights reserved. +// + +#import + +@interface Desafio_Mobile_iOSUITests : XCTestCase + +@end + +@implementation Desafio_Mobile_iOSUITests + +- (void)setUp { + [super setUp]; + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + [[[XCUIApplication alloc] init] launch]; + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +@end diff --git a/Desafio Mobile iOS/Desafio Mobile iOSUITests/Info.plist b/Desafio Mobile iOS/Desafio Mobile iOSUITests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/Desafio Mobile iOS/Desafio Mobile iOSUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Desafio Mobile iOS/Podfile b/Desafio Mobile iOS/Podfile new file mode 100644 index 0000000..78a32ef --- /dev/null +++ b/Desafio Mobile iOS/Podfile @@ -0,0 +1,10 @@ +platform :ios, '8.0' + +target 'Desafio Mobile iOS' do +pod 'Realm' +pod 'SDWebImage', '~> 4.0' +end + +target "Desafio Mobile iOSTests" do +pod "Realm" +end \ No newline at end of file diff --git a/Desafio Mobile iOS/Podfile.lock b/Desafio Mobile iOS/Podfile.lock new file mode 100644 index 0000000..d79596a --- /dev/null +++ b/Desafio Mobile iOS/Podfile.lock @@ -0,0 +1,19 @@ +PODS: + - Realm (2.6.2): + - Realm/Headers (= 2.6.2) + - Realm/Headers (2.6.2) + - SDWebImage (4.0.0): + - SDWebImage/Core (= 4.0.0) + - SDWebImage/Core (4.0.0) + +DEPENDENCIES: + - Realm + - SDWebImage (~> 4.0) + +SPEC CHECKSUMS: + Realm: 29222766425b9f831228f81b63f0e38a97a4d700 + SDWebImage: 76a6348bdc74eb5a55dd08a091ef298e56b55e41 + +PODFILE CHECKSUM: 6c10f1554a6a71fd3c34afdba4142c2ae43baced + +COCOAPODS: 1.3.1 diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/NSError+RLMSync.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/NSError+RLMSync.h new file mode 120000 index 0000000..d34ec8b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/NSError+RLMSync.h @@ -0,0 +1 @@ +../../../Realm/include/NSError+RLMSync.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMAccessor.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMAccessor.h new file mode 120000 index 0000000..33b603a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMAccessor.h @@ -0,0 +1 @@ +../../../Realm/include/RLMAccessor.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray.h new file mode 120000 index 0000000..886d1ad --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray_Private.h new file mode 120000 index 0000000..ac4f7a6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMArray_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMCollection.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMCollection.h new file mode 120000 index 0000000..781f825 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMCollection.h @@ -0,0 +1 @@ +../../../Realm/include/RLMCollection.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMConstants.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMConstants.h new file mode 120000 index 0000000..7f3eabc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMConstants.h @@ -0,0 +1 @@ +../../../Realm/include/RLMConstants.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMListBase.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMListBase.h new file mode 120000 index 0000000..43725c7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMListBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMListBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration.h new file mode 120000 index 0000000..310bdf0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration_Private.h new file mode 120000 index 0000000..43babfb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMMigration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject.h new file mode 120000 index 0000000..79652c6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase.h new file mode 120000 index 0000000..fd12a13 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h new file mode 120000 index 0000000..ff46837 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase_Dynamic.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema.h new file mode 120000 index 0000000..009a76e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h new file mode 120000 index 0000000..b580971 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectStore.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectStore.h new file mode 120000 index 0000000..e94e3dd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObjectStore.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectStore.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject_Private.h new file mode 120000 index 0000000..d796d64 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMObject_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMOptionalBase.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMOptionalBase.h new file mode 120000 index 0000000..13a480e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMOptionalBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMOptionalBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMPlatform.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMPlatform.h new file mode 120000 index 0000000..41aa0f4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMPlatform.h @@ -0,0 +1 @@ +../../../Realm/include/RLMPlatform.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty.h new file mode 120000 index 0000000..a772ea7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty_Private.h new file mode 120000 index 0000000..bc94be4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMProperty_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm.h new file mode 120000 index 0000000..3df9e03 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h new file mode 120000 index 0000000..ab65245 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration+Sync.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration.h new file mode 120000 index 0000000..26d7c63 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h new file mode 120000 index 0000000..de82958 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h new file mode 120000 index 0000000..6a71a01 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Dynamic.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Private.h new file mode 120000 index 0000000..7c23d77 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMRealm_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults.h new file mode 120000 index 0000000..6da1f7f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults_Private.h new file mode 120000 index 0000000..177a372 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMResults_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema.h new file mode 120000 index 0000000..a460968 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema_Private.h new file mode 120000 index 0000000..f4cea77 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSchema_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration.h new file mode 120000 index 0000000..8762b49 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h new file mode 120000 index 0000000..ce62fbd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncCredentials.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncCredentials.h new file mode 120000 index 0000000..6aaf2b4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncCredentials.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncCredentials.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager.h new file mode 120000 index 0000000..27ac52f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncManager.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager_Private.h new file mode 120000 index 0000000..3a14d9e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncManager_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncManager_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission.h new file mode 120000 index 0000000..841495a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermission.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange.h new file mode 120000 index 0000000..07b4a88 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionChange.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange_Private.h new file mode 120000 index 0000000..f48dc51 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionChange_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionChange_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer.h new file mode 120000 index 0000000..e00483d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOffer.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse.h new file mode 120000 index 0000000..b77c0b9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOfferResponse.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse_Private.h new file mode 120000 index 0000000..6376fea --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOfferResponse_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOfferResponse_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer_Private.h new file mode 120000 index 0000000..f64d97c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermissionOffer_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOffer_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission_Private.h new file mode 120000 index 0000000..4ee4878 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncPermission_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermission_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncSession.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncSession.h new file mode 120000 index 0000000..9aba909 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncSession.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncSession.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUser.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUser.h new file mode 120000 index 0000000..841417a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUser.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUser.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil.h new file mode 120000 index 0000000..c78eb1c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h new file mode 120000 index 0000000..121d2c2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil_Private.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMThreadSafeReference.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMThreadSafeReference.h new file mode 120000 index 0000000..0c989a4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/RLMThreadSafeReference.h @@ -0,0 +1 @@ +../../../Realm/include/RLMThreadSafeReference.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/Realm/Realm.h b/Desafio Mobile iOS/Pods/Headers/Private/Realm/Realm.h new file mode 120000 index 0000000..eb4a08b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/Realm/Realm.h @@ -0,0 +1 @@ +../../../Realm/include/Realm.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h new file mode 120000 index 0000000..8457498 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSData+ImageContentType.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/NSData+ImageContentType.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSImage+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSImage+WebCache.h new file mode 120000 index 0000000..b7a00e1 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/NSImage+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/NSImage+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCache.h new file mode 120000 index 0000000..0040b06 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDImageCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCacheConfig.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCacheConfig.h new file mode 120000 index 0000000..f0f1f8f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDImageCacheConfig.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDImageCacheConfig.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h new file mode 120000 index 0000000..6ca2478 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageCompat.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageCompat.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h new file mode 120000 index 0000000..a2f3a68 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDecoder.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDecoder.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h new file mode 120000 index 0000000..303b03b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloader.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDownloader.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h new file mode 120000 index 0000000..99441c4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageDownloaderOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageManager.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageManager.h new file mode 120000 index 0000000..1b81848 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageManager.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageManager.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h new file mode 120000 index 0000000..20e5b89 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImageOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h new file mode 120000 index 0000000..50585c6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/SDWebImagePrefetcher.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImagePrefetcher.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h new file mode 120000 index 0000000..19d2d8e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIButton+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIButton+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+GIF.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+GIF.h new file mode 120000 index 0000000..14d5aad --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+GIF.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImage+GIF.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h new file mode 120000 index 0000000..1fb9650 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImage+MultiFormat.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImage+MultiFormat.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h new file mode 120000 index 0000000..fd4dea4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+HighlightedWebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h new file mode 120000 index 0000000..0c53a47 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIImageView+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImageView+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCache.h new file mode 120000 index 0000000..641671a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIView+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h new file mode 120000 index 0000000..f9890c4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Private/SDWebImage/UIView+WebCacheOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIView+WebCacheOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/NSError+RLMSync.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/NSError+RLMSync.h new file mode 120000 index 0000000..d34ec8b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/NSError+RLMSync.h @@ -0,0 +1 @@ +../../../Realm/include/NSError+RLMSync.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMArray.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMArray.h new file mode 120000 index 0000000..886d1ad --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMArray.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMCollection.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMCollection.h new file mode 120000 index 0000000..781f825 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMCollection.h @@ -0,0 +1 @@ +../../../Realm/include/RLMCollection.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMConstants.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMConstants.h new file mode 120000 index 0000000..7f3eabc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMConstants.h @@ -0,0 +1 @@ +../../../Realm/include/RLMConstants.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMListBase.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMListBase.h new file mode 120000 index 0000000..43725c7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMListBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMListBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMMigration.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMMigration.h new file mode 120000 index 0000000..310bdf0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMMigration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObject.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObject.h new file mode 120000 index 0000000..79652c6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObject.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase.h new file mode 120000 index 0000000..fd12a13 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h new file mode 120000 index 0000000..ff46837 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase_Dynamic.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectSchema.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectSchema.h new file mode 120000 index 0000000..009a76e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMObjectSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMOptionalBase.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMOptionalBase.h new file mode 120000 index 0000000..13a480e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMOptionalBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMOptionalBase.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMPlatform.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMPlatform.h new file mode 120000 index 0000000..41aa0f4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMPlatform.h @@ -0,0 +1 @@ +../../../Realm/include/RLMPlatform.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMProperty.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMProperty.h new file mode 120000 index 0000000..a772ea7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMProperty.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm.h new file mode 120000 index 0000000..3df9e03 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h new file mode 120000 index 0000000..ab65245 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration+Sync.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration.h new file mode 120000 index 0000000..26d7c63 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealmConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h new file mode 120000 index 0000000..6a71a01 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Dynamic.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMResults.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMResults.h new file mode 120000 index 0000000..6da1f7f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMResults.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSchema.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSchema.h new file mode 120000 index 0000000..a460968 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncConfiguration.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncConfiguration.h new file mode 120000 index 0000000..8762b49 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncCredentials.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncCredentials.h new file mode 120000 index 0000000..6aaf2b4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncCredentials.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncCredentials.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncManager.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncManager.h new file mode 120000 index 0000000..27ac52f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncManager.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncManager.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermission.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermission.h new file mode 120000 index 0000000..841495a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermission.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermission.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionChange.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionChange.h new file mode 120000 index 0000000..07b4a88 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionChange.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionChange.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOffer.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOffer.h new file mode 120000 index 0000000..e00483d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOffer.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOffer.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOfferResponse.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOfferResponse.h new file mode 120000 index 0000000..b77c0b9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncPermissionOfferResponse.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncPermissionOfferResponse.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncSession.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncSession.h new file mode 120000 index 0000000..9aba909 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncSession.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncSession.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUser.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUser.h new file mode 120000 index 0000000..841417a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUser.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUser.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUtil.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUtil.h new file mode 120000 index 0000000..c78eb1c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMSyncUtil.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMThreadSafeReference.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMThreadSafeReference.h new file mode 120000 index 0000000..0c989a4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/RLMThreadSafeReference.h @@ -0,0 +1 @@ +../../../Realm/include/RLMThreadSafeReference.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/Realm/Realm.h b/Desafio Mobile iOS/Pods/Headers/Public/Realm/Realm.h new file mode 120000 index 0000000..eb4a08b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/Realm/Realm.h @@ -0,0 +1 @@ +../../../Realm/include/Realm.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h new file mode 120000 index 0000000..8457498 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSData+ImageContentType.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/NSData+ImageContentType.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSImage+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSImage+WebCache.h new file mode 120000 index 0000000..b7a00e1 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/NSImage+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/NSImage+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCache.h new file mode 120000 index 0000000..0040b06 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDImageCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCacheConfig.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCacheConfig.h new file mode 120000 index 0000000..f0f1f8f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDImageCacheConfig.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDImageCacheConfig.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h new file mode 120000 index 0000000..6ca2478 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageCompat.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageCompat.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h new file mode 120000 index 0000000..a2f3a68 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDecoder.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDecoder.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h new file mode 120000 index 0000000..303b03b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloader.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDownloader.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h new file mode 120000 index 0000000..99441c4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageDownloaderOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageManager.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageManager.h new file mode 120000 index 0000000..1b81848 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageManager.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageManager.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h new file mode 120000 index 0000000..20e5b89 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImageOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImageOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h new file mode 120000 index 0000000..50585c6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/SDWebImagePrefetcher.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/SDWebImagePrefetcher.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h new file mode 120000 index 0000000..19d2d8e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIButton+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIButton+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+GIF.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+GIF.h new file mode 120000 index 0000000..14d5aad --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+GIF.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImage+GIF.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h new file mode 120000 index 0000000..1fb9650 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImage+MultiFormat.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImage+MultiFormat.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h new file mode 120000 index 0000000..fd4dea4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+HighlightedWebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h new file mode 120000 index 0000000..0c53a47 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIImageView+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIImageView+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCache.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCache.h new file mode 120000 index 0000000..641671a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCache.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIView+WebCache.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h new file mode 120000 index 0000000..f9890c4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Headers/Public/SDWebImage/UIView+WebCacheOperation.h @@ -0,0 +1 @@ +../../../SDWebImage/SDWebImage/UIView+WebCacheOperation.h \ No newline at end of file diff --git a/Desafio Mobile iOS/Pods/Manifest.lock b/Desafio Mobile iOS/Pods/Manifest.lock new file mode 100644 index 0000000..d79596a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Manifest.lock @@ -0,0 +1,19 @@ +PODS: + - Realm (2.6.2): + - Realm/Headers (= 2.6.2) + - Realm/Headers (2.6.2) + - SDWebImage (4.0.0): + - SDWebImage/Core (= 4.0.0) + - SDWebImage/Core (4.0.0) + +DEPENDENCIES: + - Realm + - SDWebImage (~> 4.0) + +SPEC CHECKSUMS: + Realm: 29222766425b9f831228f81b63f0e38a97a4d700 + SDWebImage: 76a6348bdc74eb5a55dd08a091ef298e56b55e41 + +PODFILE CHECKSUM: 6c10f1554a6a71fd3c34afdba4142c2ae43baced + +COCOAPODS: 1.3.1 diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/project.pbxproj b/Desafio Mobile iOS/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9104996 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1406 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 01B247B801AD86E23CEF6DDD7D5F62A4 /* NSError+RLMSync.m in Sources */ = {isa = PBXBuildFile; fileRef = E0FDB3D3FF302561E84B93EE43576A8A /* NSError+RLMSync.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0302B0B10C1D94D0723A753E8C2CDED9 /* NSData+ImageContentType.h in Headers */ = {isa = PBXBuildFile; fileRef = B0B6940E9C426189F00EA6E15CABE668 /* NSData+ImageContentType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04502E56918E180A0C90FEC834A77D6B /* UIImage+MultiFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = E52600804C8C497C321CA5BD0B541504 /* UIImage+MultiFormat.m */; }; + 05B9E2885BA1F649F9D12696FCD9008F /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9FA16A455CD0C8043F5150529FF32CE5 /* realm_coordinator.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 05D725264AD881663D8F5023302E2314 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDAA6F76D63D0F7B11821168D44B656 /* SDWebImageOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 07B51B4929416EE72BF877C391D5A7CE /* RLMQueryUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDEB0A2D168B99311B9C5FD187F57C13 /* RLMQueryUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0991371C5A6A50D7E37BDE6254B7F8A8 /* RLMThreadSafeReference.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5AA21461FD31896521033F32C1CCE3A3 /* RLMThreadSafeReference.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0C1AE8225CE0DF160E668EA50AAC5D0E /* Pods-Desafio Mobile iOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BBA73FC47478A7160B4541A14A51D2BD /* Pods-Desafio Mobile iOS-dummy.m */; }; + 0E08CDDDB40DE4C8734CEF05CB7EC12C /* SDWebImageCompat.m in Sources */ = {isa = PBXBuildFile; fileRef = E8216EEEA3B3986A6DF938AD56375771 /* SDWebImageCompat.m */; }; + 0E315A746F1922672D461A801606635A /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEA192D19CEB6542A30F5A1A8FF2A32 /* SDWebImageDownloader.m */; }; + 123F92872D33F759883536D551938E39 /* SDWebImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9CB31A3F767F0A10510AF56B5F20615 /* SDWebImageManager.m */; }; + 12B5E673540601529F136DC0C3E4D0F8 /* RLMOptionalBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6610131A68E9CD9EE8E012594E5820EC /* RLMOptionalBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 14055C8E10BBB0F1566C99E5868E5CEE /* RLMSyncUtil_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 22397DB45B0DC905481B9F5C60E1995D /* RLMSyncUtil_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 141627F89E126C99D54AAEFABD85A972 /* RLMSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 93AF25BA9932865B1011C9DE5A60BF29 /* RLMSchema_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 1450CE304A65FF1B7026E9749BF2A09C /* NSError+RLMSync.h in Headers */ = {isa = PBXBuildFile; fileRef = 60DC3B4C0D8D01198DFA6FD282AE930B /* NSError+RLMSync.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1525952FF84DB0E91C857CB3BB8AA110 /* RLMCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = B13D3DC9F2D296B14C868F27D85C3CAC /* RLMCollection.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 1BCB7326206F8142B556CF941BA51157 /* RLMSyncManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DEA48476564535FB9BCD4CC3A89E184 /* RLMSyncManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F63CDDBC6D8EFC7007D047BF896D808 /* RLMObjectBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = B956E02FD7E2609D27218601ECF76FE8 /* RLMObjectBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 2126F00A63A6F444DF17C2E6AE3D576A /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 1362F52E5DF44C7F11175B870C39B249 /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 21407B4F4B81C8474F8CA91CD286C4CF /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 769A808B3BEC6C2B5C7610F2B2D6E22D /* results.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 224A8C297239095E015C6F9BB54842A5 /* RLMObjectSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 21CAAF37C57AF04C13AFDAA0DA5700DC /* RLMObjectSchema_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 2276E697ACE8E64A466834C49837C8D5 /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0DD37BDBF0626F8C34DA78DD538973C7 /* sync_permission.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 22BA1C0E39F3819C99154728FDD6D44F /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9EBFB93894B5EBE2F8F1D9AD517B4A1D /* list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 2BA28CA92ACA83E385452F0817A458FC /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F6BE95984E42F6D67E00991B95D3265E /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2C4ECA96AB2DB4AA667E16DD632D862D /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A55E7B967F80B4E3E1B9CD954D3A25E9 /* sync_metadata.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 2D3AECB19EEDF32993DC49FC539525CE /* UIView+WebCacheOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 64D99E5DADB2722076BB81B4C0DBD245 /* UIView+WebCacheOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2DC1A87329344911BBAB71E5CFDD43C8 /* RLMPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = C1BB46BA6D3990FCBB46EC22EF5FFEED /* RLMPlatform.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30A5AF360D18CA0274DBC6477A4A1DE9 /* sync_user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84C855EEEDC27E68FF2865F067CA235E /* sync_user.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 30EAFD1C31227451869B61CA1610F126 /* RLMAuthResponseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 19E6087BA47B7F737F80E76048C69DA6 /* RLMAuthResponseModel.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 32D7FA41645B4B66A8F8E5483F0509EC /* RLMObject.h in Headers */ = {isa = PBXBuildFile; fileRef = B16FB1873CA5634B92BCCB39D8C6BA37 /* RLMObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3454A9BDBF49CD926AE7C1E0E2C0204F /* UIImage+MultiFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = F0A560BD422C74C5CFDDE403A624E1AB /* UIImage+MultiFormat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 354D7810AE32D1BDFED7353ED7DE2330 /* RLMObjectBase_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = 27BE8D23940F02EABA465C4DA3239DAB /* RLMObjectBase_Dynamic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35A93FF4049467DB0C6C82A7F1DF71D3 /* RLMUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39E2CFB09710154BF7171B79A1D05AEF /* RLMUpdateChecker.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 37241E326F71425038DFCEC6D28F7785 /* RLMSyncPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F5FE78719A7FE69DF041C1556C14599 /* RLMSyncPermission.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 377EF193077067603BD669750005DC53 /* RLMProperty.mm in Sources */ = {isa = PBXBuildFile; fileRef = 21B5D100083FF70FF4E6A7FA598CA52C /* RLMProperty.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3792263B0EA38B806290894F6F1179AA /* RLMListBase.h in Headers */ = {isa = PBXBuildFile; fileRef = AF17AA6A17934C2136FEF0682AF011FD /* RLMListBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3876930B4B10104CC5E49D7BA5379E73 /* Realm-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E6F9A33E633BFA78C970B9A6D9BFD5D /* Realm-dummy.m */; }; + 38B54D7B7B32B848D944FCA70B619FE8 /* RLMPredicateUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0AC996A4E42A1D484E320454048DC4 /* RLMPredicateUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 38C3A0AA4F5F4EAFDD1F14DAB7A39363 /* RLMSyncSessionRefreshHandle.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26E972922EBAB79646170E2C54A4CB79 /* RLMSyncSessionRefreshHandle.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 38CBD43BEF88FD721954B97290351C4C /* RLMSyncUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 94FE9F896125E14D7129E29C9534404B /* RLMSyncUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3BD311C5B0E5EC6803CCDBF92C4BAE1D /* RLMMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD213C0608EBC35FBBCBF0614B685D6 /* RLMMigration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3EEEAA46855F4007A26FDDBEDD760EA4 /* RLMSyncConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = F5FF66D4FA4217E8CB45171E3167FE92 /* RLMSyncConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3FCE77974B91404AF773195E788F0ADA /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8973FC92EC43A7EE06752F157C8B02EC /* object_schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4027AC2535BB46AC608C030395DE18C1 /* RLMAnalytics.mm in Sources */ = {isa = PBXBuildFile; fileRef = F302CCC43EDDD3E242714DDE80A23BFB /* RLMAnalytics.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 40CF53EE017EEC9979CCCEC3F4DCC175 /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FD3FD7CFFE5C1010EF9A3E05AB64FB5D /* thread_safe_reference.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4248E38695EB8443CDDE2503A966AB2C /* RLMArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 0716B9F43BC5160CEFABA4D4DFDBCEB9 /* RLMArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 42AA286D61B95AAA0AD7E81A51395371 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */; }; + 44DEC525B80B7F5DA345B812AF6BA9F7 /* RLMMigration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 593E5502D5C2A2E653EFA26B3C6458AE /* RLMMigration_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 451DBB2303E9A71FE5BE52242C377CCF /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DA1019C2A5117D3EB62D5AF2F23683AB /* external_commit_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 476FF98879CF14D6444E13572DF08672 /* RLMSyncPermissionOffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 220A12690C158F4D788C82DBD372547D /* RLMSyncPermissionOffer.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 47BD034B18CC661F3888FEA4B697128B /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = E288314559144BA2C4B9DD3722A3D403 /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 487C9CBD3BF11BBCB1297D8BE3B7C840 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 635A4BD1CEF7E623C71F97A54BFC3616 /* ImageIO.framework */; }; + 48C0BCA3AF1F8B701FBAF4F645CA8BCB /* RLMArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB87049A31AFDC36C225747336EF831E /* RLMArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4A44EB1ABEF0EBA4016BAA4748403C32 /* SDWebImage-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F1A46FAE7B82BCCD0B8B7E9589E822F3 /* SDWebImage-dummy.m */; }; + 4B10C33DD31589C209CCF742F569740F /* RLMTokenModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4DD37E2EEDC35FD41274E27DC63DE0 /* RLMTokenModels.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4D121ABC760675DF76A8505A8EDA699E /* RLMListBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8361E0DB72B22FB078D199DBC4E22A42 /* RLMListBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5047A45D29A20BEB1616EF347C83EF7C /* RLMSyncSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B30121CD919A4F16A8D4CEBBECDD912 /* RLMSyncSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5271AE2F5495F812A8E559B966234E58 /* UIImageView+HighlightedWebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FB2E6781D27936DAA0B3C01E9D13CC27 /* UIImageView+HighlightedWebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 52F6F1CF4F9A2598D1CB2FEC720F6D32 /* RLMSyncUser.h in Headers */ = {isa = PBXBuildFile; fileRef = DFE13D841BB61B67678562B498135D81 /* RLMSyncUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55F060170CF1AFB690CA72E46DDE6986 /* UIImageView+HighlightedWebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A01752D41D4B2DE07A4392FB842B67B /* UIImageView+HighlightedWebCache.m */; }; + 5621A5BA6607FFF193BCDCC00D36664F /* RLMSyncErrorResponseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FB4BBE40FF20BD61EC05DC016D09AAA /* RLMSyncErrorResponseModel.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 562B95A60240347DDD6714747676CAB7 /* RLMSyncPermission_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EF6C7597A35770AA0F35B1099F916E /* RLMSyncPermission_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 564B1837D496ADE5E44E993DC4033885 /* RLMRealmConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16E7A5C32559A819868F5C53EFE77DFD /* RLMRealmConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 56E4D1DCE57F8333D5CC41DDCD5E1719 /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0F4DB00ECDC3064B087ECDDF0F10F0 /* transact_log_handler.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5732D2F2844A26BD4389B78DFE4BBF67 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C8A29904B3F950CFBDF1ACAF6D3E4624 /* RLMSyncConfiguration_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 59E15B410C9C0DE2CBB42DA6CC9A68EE /* RLMSyncUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8FF56E3E0E6852BDFF322495EE2F80F6 /* RLMSyncUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5D497981E9FE2911A8311945A3765EAB /* NSImage+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B4EC692C74E86115865A9201BC007C /* NSImage+WebCache.m */; }; + 5D57F43CF46D37F06ADE45ECDB7FC1FA /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D60BC2C9851AF382A4CD4774360BF2 /* UIImage+GIF.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 607303BD04E5BA1CE1E7422ECF0863A4 /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9590678F806C40E5A5FFC6C9101B9D7B /* SDWebImageDecoder.m */; }; + 60AA0F3B68D6CFB8F492BDD9579BEAD8 /* RLMSyncConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 422D00835B6AB84344FB361CA87E9160 /* RLMSyncConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 63A3B299A9D660BF9D76E3DD8718FC1C /* RLMObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = BF8A5B79466FFFFCF6D179236F19624C /* RLMObject.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 668ACCE1ABC6AC4EF6F95EEA911C362D /* UIView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CA6F32964BE8ECC00A9DB0BDFAC5243 /* UIView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 67FAF86CF8315CBF3513071E1CF24B56 /* UIImageView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FDA9DB26D6B5AF80941AE65BC2A0A811 /* UIImageView+WebCache.m */; }; + 6BC803C0AFFCDB0676C85315C029F59B /* RLMResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6CCA204A8DBD6C1731D61372F026B150 /* RLMResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 6EED8AD252264FB9D383AA079A8DC097 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 480BA2678EA0B20747D28954B9457349 /* binding_callback_thread_observer.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 6F39CAABD1C74CB8151ECE7EAB1325EE /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0521EB34E4CB550CE47626783B1916D6 /* sync_session.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 70C5340C3F4588C0A3B858A4F8E1E6EB /* UIButton+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DE3ED622494B0462F7B72423C91832D /* UIButton+WebCache.m */; }; + 724F74D45D88D433447305A9DEE16594 /* RLMSyncPermissionOfferResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 13949A73AEAD1F2A34072F8F54B1DF8E /* RLMSyncPermissionOfferResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 73B3C48BE5D40F6BD71A94760F93B981 /* RLMThreadSafeReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 759C5BAB73B1D5AB350781ED3102911C /* RLMThreadSafeReference.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74B3CFF8A3C3F674D47831D408AD1B7E /* RLMSyncPermissionChange.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FDECBBF23CD9C3FBE77BBFC7E96ED5B /* RLMSyncPermissionChange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 761E6D64223D6F0218BF475E9D96E7EE /* RLMObjectSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 517D872E100B92DACB9CBBD119BFA669 /* RLMObjectSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 76F7FACF1253E5169FCB5723B71ABAB5 /* RLMProperty_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 734CB0970A71F73360F4DED8364555FB /* RLMProperty_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 77A3574CFA4B4DD2D14F094AA7EE3B4C /* RLMSyncManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A6B923BC942E39D47071F0D5A7BDEC /* RLMSyncManager_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 7A746CD18A4984BD97FD5576D24F3F62 /* collection_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F616D63BF66707E6D45930A06800CE7C /* collection_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 7DE5802D3F6BC60BA6CDB034B61DF00D /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6E3BC93D3D17069855E0F22EE09ED40 /* object.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 7E4000A5C45E4FEF641F8DF9C45285F9 /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF59782C23C0289C7AD7FEC02805D9D4 /* collection_change_builder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 804D5270D45E5C6C640DC6DE3AB2E430 /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E260A54AD827D2E5463D21F47D42ABA /* sync_manager.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8225CE5B2B0C86FC3A88A7D9873B732E /* RLMSyncSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 59D6D321140F8A82333E2770F87236F4 /* RLMSyncSession.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 824BD920E778C35AC0F9FEA950047218 /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3BD12ED93702EB66B485C5FD9BCF72 /* format.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 831940C817C5E294A1BB2CE8B06EF3C7 /* RLMRealmConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CB9047710CCE36410D1B49BD0E48A261 /* RLMRealmConfiguration_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 836058F9E0E48099F6F06652F664EF6D /* RLMObservation.mm in Sources */ = {isa = PBXBuildFile; fileRef = D89AED0074B7E408BCE7E686353F6718 /* RLMObservation.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 850F47047482200F094356EA5D51088B /* Realm.h in Headers */ = {isa = PBXBuildFile; fileRef = A2CA6093C584B3343F0A8807A3DB715D /* Realm.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 87A1886A1F0228B2CDAF3CF0D7641F82 /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B27474262B3809F0316D027A69EA3C1 /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 87FA44456BACBCA490BAED3E1677FF5B /* RLMCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = CD4A097060C3801879092F226A5AB1C6 /* RLMCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8A641BE8547B61F34D74872C8AD4C04E /* Pods-Desafio Mobile iOSTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 87917E9B1B2D08BF213F53FFE1CE9D4B /* Pods-Desafio Mobile iOSTests-dummy.m */; }; + 8A65374F77C2BC6983B3AE9B486F8482 /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86DB7DB8C2F9C52FDFC18A0B55209988 /* sync_file.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8C25FEC1DF7A22F5979F2208A5FA060B /* SDImageCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 38EA12D78AE4A3417F01E37BC28CCAE5 /* SDImageCacheConfig.m */; }; + 8C48685FC55534F7AEE7797EF6158904 /* RLMSwiftSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E3ADB24AF23510778AC8368F8350E56 /* RLMSwiftSupport.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8CACD5879EE057347ECAC4DAA253FEF2 /* RLMSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 950B5184CF33C77407470C00DD42A77D /* RLMSchema.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8CEC40420F25D5359617454BCE715AD0 /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6C93097CD5BF2617EE04F5DFB5506171 /* list.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8D3748873DFADF4470A59CD5BF937E59 /* RLMSyncPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = F45834B7576EF0DF143F939105F02D8A /* RLMSyncPermission.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8DA5BCC3E7ABBB3CF8596D813E8C1870 /* RLMRealmConfiguration+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BFC61720DC164E671A1C9D6FA83412D /* RLMRealmConfiguration+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8DDBF6B1E05DD5FB2B1B1ED6D5F50970 /* RLMObjectBase.h in Headers */ = {isa = PBXBuildFile; fileRef = BBA82F3ECEADE0E821DA4940159A5002 /* RLMObjectBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8E9B431E8A8EBBACBE974076B1FD3D51 /* RLMSyncPermissionOffer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FE210EE438C3A3B5193D6623F9310B17 /* RLMSyncPermissionOffer_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8EC74957040F2A8AA632967B704367CA /* RLMRealmConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0C0B6784685A4609D09FAB9881DA9D /* RLMRealmConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 903798B2C621828732445974BD55B291 /* RLMSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 85B8B0BDF6A4A4B7AE5C2D73E53B98E5 /* RLMSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 9103580F3D619DD52A205E48EDC70181 /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41ECB19FD12F5D933BC8175EDC1C528E /* keychain_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 91F8EB501EF10C0CCC2DCA9A2B425D25 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F009073D69CE9F3D3E72CC6A4D7373F6 /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92A666CBFB1B735FC2B7C51394276FCF /* NSImage+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C745587F08365BB46ADCCCCF49E5BA0 /* NSImage+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 934FB8AB326C322CA518978AA42ADA41 /* RLMArray_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 05EEB40E22F2D62CDE5B62B016ECC84E /* RLMArray_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 967244C6ADD24666A4A24B6C0B2853EA /* RLMResults_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E51FEF8D2A6E612B6005BADBA049CF10 /* RLMResults_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 97A294DADC46EE22447504CA4DDF4881 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = B4ECAF64B51C462CD02D736CA0DADFCB /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9847189348EBD2DA432204217AFC011D /* SDImageCacheConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DA12F6599566891226E26F3779D65128 /* SDImageCacheConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9932ECB76BC57F19C5AB0120FB8C5885 /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6F6740654F4F686F2BA043704C2A9384 /* object_store.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 9D69C3BDE676C6125BFFAD2BA1F37715 /* RLMSyncCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = C6A0EADEE1754130D100D2732C16D1DD /* RLMSyncCredentials.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 9E785D847C3929D6FE134719914E0B4D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */; }; + A35AC7B43E80A89D56B544657F07B506 /* RLMRealm_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AB3690AA4798C7FBF70ABC5F6E3453FC /* RLMRealm_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A4A078CBFA04163C2363B131C7AFFCC9 /* RLMClassInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = EBDB598A010C1BA1CB96BE949347951B /* RLMClassInfo.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AAF8C337FADDDC88E00D14C90BD9D319 /* RLMRealmUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2962DD70AE0673F59388F75BA54971A /* RLMRealmUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AB61EECC730A6FA3F51AA6195B3A4B83 /* RLMConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 65E123B54B6D20EA415F8403E9765FED /* RLMConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ACCBF0F7BD6A744ABF1CC653878FF4F2 /* RLMConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = F6DD54862A15C98E3C2E1D2C9CE2BD0A /* RLMConstants.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AEBF88DA579882DB79AE8DBA08C4BB95 /* RLMSyncPermissionChange_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C6D75E91EC2A7A480A3448C202A86DC /* RLMSyncPermissionChange_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + B0F58B07F4C2F6934002FD51734545A2 /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 646A7B3A448DFB761E1977174B5A7013 /* results_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + B1B33D1BA74899E877E674B48ADD828D /* RLMAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = B9DDABFE0960F37FE332E358A94F7EAF /* RLMAccessor.h */; settings = {ATTRIBUTES = (Private, ); }; }; + B8B4453A3824E8860AB81F2E6F1CC39E /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 77661AFF7883EC57BDAA0E3A2ED0771A /* shared_realm.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + BC65A75A1E54A79B3FBEF745F3D634FE /* RLMRealmConfiguration+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = B5942FFA45E36E4CF78550F1809BA751 /* RLMRealmConfiguration+Sync.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C06A5EBBA4F5F42C735A887D4ECA307C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */; }; + C2F9B2FCA3348720C35B41C66A0367F6 /* RLMSyncManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5203C5C302EC376987BA35B49E447C84 /* RLMSyncManager.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C344987A26383C45E6A3512A2201A408 /* RLMRealm_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = 67A5B48E045080C17F02CF5211E8C23A /* RLMRealm_Dynamic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C3EA75F30CA2E561CEAFA69E6CF13359 /* RLMObjectSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 7479D62E72AF5740C05151D2D3D7559B /* RLMObjectSchema.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C59685218F74891CDC0287BAB1F54485 /* object_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11C14E42F4CA776D66F02E1890C37795 /* object_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C9F17C5AF12EDBB0C7973F8492A4C8FC /* RLMNetworkClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C36730D8B3C481AA3017F50B86398779 /* RLMNetworkClient.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + CD71ACF717F5AC79F7148DE85421FDD4 /* RLMRealm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5D7EABAEE67288A5E87C358894E1599B /* RLMRealm.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + CE37A7F1D232436E91F2080324768677 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = CF9074A5B32C58DB476755D22216636F /* SDImageCache.m */; }; + CEAECB51979251B9AA2369592FFE8EBC /* NSData+ImageContentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD162DB966535DC7DACCD51EB925A1E /* NSData+ImageContentType.m */; }; + CF6B0C9906E27B7462E7366A060E1916 /* RLMResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 147F70A36C7F34A87CDEC9471965A107 /* RLMResults.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CF98D0A1E15130EB88E9AB32246052E9 /* RLMObject_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A6C409EE08898A9F75B979D1716E280 /* RLMObject_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D0A711692FD5298A867F7CC647C370D9 /* RLMOptionalBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 76AB5FBBD310D8F3D1F5B59109FC7692 /* RLMOptionalBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D1872A5B435BA6FCAF59AC01A37A6B1F /* RLMSyncUser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1D0FF5E7C342CDDA865A7FD9DEAE072F /* RLMSyncUser.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D30D56DA4941651DAE66FFE7C81E4722 /* placeholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4671ECBDB8117E509BC970D325903AB /* placeholder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D5F0FE0C9BA3404546C6E36A0799D1AC /* RLMSyncCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 058356C59693A79C1F85D180E992F091 /* RLMSyncCredentials.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D69B186E2DD0A903BA663F469EE8AAB3 /* RLMMigration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05BA76C0FA2CD68B2D1D8327F2EDEC66 /* RLMMigration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D6DD56DAD60BA0D429205BBF37E108F4 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0980C0164722B2FAA1F278F4603FD7A2 /* SDWebImageDownloaderOperation.m */; }; + D865A274D47B349C448A39F5653BE8C8 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 527C73D5AEE12B4056584D0BD7B7E5E1 /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8B996BE11845CC4A6FB517DFACC150D /* RLMObjectStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDCE27162CDD9C5B8AD8E4F8B84165 /* RLMObjectStore.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DA0E48C6263EA6D6BAC8879E540C69BE /* RLMSyncPermissionChange.m in Sources */ = {isa = PBXBuildFile; fileRef = 45F74D1F561F8A89497DD9F07E4650BA /* RLMSyncPermissionChange.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DBE5C6EE205B677670810D0BB9963C6E /* UIView+WebCacheOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D02AFC73FF58DB0EC38BB9E8D68E65 /* UIView+WebCacheOperation.m */; }; + DCF564B01D6FFA5AEBD02795B2E37063 /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AAC87378E207F74D0C50B3271F006F0 /* schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DE96CDBF0683DFD4B501789487F531BB /* UIView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C52265C1B79322AEB93DEB670A2A6C /* UIView+WebCache.m */; }; + E50EFBA088BAC04108C4D132D9BDE72A /* RLMSyncPermissionOfferResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F526340DE76D2721709F77E1390A7F4 /* RLMSyncPermissionOfferResponse.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + E60C4553740D5C742B835B6573FFCDDF /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5784110D2D9D440E7529052CD4983B5D /* index_set.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + E8FAF58FA7BEBD867EC15C91EB4BDB62 /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670A05949EDA53CBF2DF4DE915542CA4 /* weak_realm_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + E90D25561127F86CD9E03D192377FA53 /* RLMRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C98DED74949E03CE103B50AA57088F7 /* RLMRealm.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EBFD7517DDF368169A26C2560448C483 /* RLMAccessor.mm in Sources */ = {isa = PBXBuildFile; fileRef = C7CE7D4DDF14A198856A3FB83E8B6AD5 /* RLMAccessor.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + F0E5CEA5B7FFB9FB5BFAB4BCB2C0324B /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 37541A8B864CC0126A2EA16F173B3E6E /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F0F318EF039D0DD32F79CD564FC1DD03 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */; }; + F297A6B55B8F509A20736A9E0ABA0DCB /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DE703A1BC9789DB3AC346DD15A1F690 /* UIImage+GIF.m */; }; + F2B14EE9901CA12D7143CF62997E7547 /* RLMSyncPermissionOffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 469BAE3C50A964F499E4A579E7C5741C /* RLMSyncPermissionOffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F316BEC004F816982C72E98700127F97 /* RLMArrayLinkView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3FEB6A4D56141A837470FBB2D9AB0C56 /* RLMArrayLinkView.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + F3AEE01B6C0764C231C881449EC575DD /* RLMSyncPermissionOfferResponse_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = F602E82472C8D0D9B550572C60145B61 /* RLMSyncPermissionOfferResponse_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F7CD3EC1C2FCA9D700BAB868A9D53CC7 /* RLMUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 94ED30374869F0F107250E36C790C73E /* RLMUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + FA4FB93CD480C52649D5A3E742514740 /* RLMObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = CE3AEBCDDD9941C5F04FB1932DBCD708 /* RLMObjectStore.h */; settings = {ATTRIBUTES = (Private, ); }; }; + FA96BE4B80FBF12B7DB8CF9F33C44E76 /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B84F30F571C9014C9354C57D8A2E367 /* collection_notifications.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + FA9E11D4654958E095C1122C52CC33E1 /* RLMProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 277473E55172F1FC7AA0EEBFACEE5CE4 /* RLMProperty.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB6D89931F604F6333666C67EC557532 /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D70C08AFAD2005E596B0A1B32561A7F8 /* uuid.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.6.2\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + FBDDD64965449B93D229A68CB84446DD /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A0BB95733259AFD89E3EFF9FCFDCE68 /* SDWebImageDownloaderOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FF1850B9A6EEB1F984D3A32B21779E08 /* SDWebImagePrefetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 704D2448775DB8C97961E8846FBF4074 /* SDWebImagePrefetcher.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0A08D202103213613CAFD366968B424C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 90B4085450060013AFE6FDAABCAB6E8D; + remoteInfo = Realm; + }; + 0DF9AD3353A6AAE64204A85D3F2B1720 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 90B4085450060013AFE6FDAABCAB6E8D; + remoteInfo = Realm; + }; + 546C147B3C250A4B755FED678CB89394 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 12A5BD51A2DF76AE0D835BDA57C208D7; + remoteInfo = SDWebImage; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0521EB34E4CB550CE47626783B1916D6 /* sync_session.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_session.cpp; path = Realm/ObjectStore/src/sync/sync_session.cpp; sourceTree = ""; }; + 058356C59693A79C1F85D180E992F091 /* RLMSyncCredentials.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncCredentials.h; path = include/RLMSyncCredentials.h; sourceTree = ""; }; + 05BA76C0FA2CD68B2D1D8327F2EDEC66 /* RLMMigration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMMigration.mm; path = Realm/RLMMigration.mm; sourceTree = ""; }; + 05EEB40E22F2D62CDE5B62B016ECC84E /* RLMArray_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray_Private.h; path = include/RLMArray_Private.h; sourceTree = ""; }; + 06E777B7DC7266D5C62E62B00BB1F182 /* Pods-Desafio Mobile iOS-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Desafio Mobile iOS-frameworks.sh"; sourceTree = ""; }; + 0716B9F43BC5160CEFABA4D4DFDBCEB9 /* RLMArray.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray.h; path = include/RLMArray.h; sourceTree = ""; }; + 08C19DD1D938007A493F915DCFC19B3D /* libSDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libSDWebImage.a; path = libSDWebImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0980C0164722B2FAA1F278F4603FD7A2 /* SDWebImageDownloaderOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloaderOperation.m; path = SDWebImage/SDWebImageDownloaderOperation.m; sourceTree = ""; }; + 0AAC87378E207F74D0C50B3271F006F0 /* schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = schema.cpp; path = Realm/ObjectStore/src/schema.cpp; sourceTree = ""; }; + 0C6D75E91EC2A7A480A3448C202A86DC /* RLMSyncPermissionChange_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionChange_Private.h; path = include/RLMSyncPermissionChange_Private.h; sourceTree = ""; }; + 0D9974B0DE0180DA0A3C38EA5A53FB0F /* Pods-Desafio Mobile iOSTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Desafio Mobile iOSTests-resources.sh"; sourceTree = ""; }; + 0DD37BDBF0626F8C34DA78DD538973C7 /* sync_permission.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_permission.cpp; path = Realm/ObjectStore/src/sync/sync_permission.cpp; sourceTree = ""; }; + 0E260A54AD827D2E5463D21F47D42ABA /* sync_manager.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_manager.cpp; path = Realm/ObjectStore/src/sync/sync_manager.cpp; sourceTree = ""; }; + 10505976D22548D10C51FDD5819BA4A6 /* Pods-Desafio Mobile iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Desafio Mobile iOSTests.release.xcconfig"; sourceTree = ""; }; + 11C14E42F4CA776D66F02E1890C37795 /* object_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_notifier.cpp; path = Realm/ObjectStore/src/impl/object_notifier.cpp; sourceTree = ""; }; + 1362F52E5DF44C7F11175B870C39B249 /* SDWebImageManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageManager.h; path = SDWebImage/SDWebImageManager.h; sourceTree = ""; }; + 13949A73AEAD1F2A34072F8F54B1DF8E /* RLMSyncPermissionOfferResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOfferResponse.h; path = include/RLMSyncPermissionOfferResponse.h; sourceTree = ""; }; + 147F70A36C7F34A87CDEC9471965A107 /* RLMResults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults.h; path = include/RLMResults.h; sourceTree = ""; }; + 16E7A5C32559A819868F5C53EFE77DFD /* RLMRealmConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmConfiguration.mm; path = Realm/RLMRealmConfiguration.mm; sourceTree = ""; }; + 19E6087BA47B7F737F80E76048C69DA6 /* RLMAuthResponseModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMAuthResponseModel.m; path = Realm/RLMAuthResponseModel.m; sourceTree = ""; }; + 1C0AC996A4E42A1D484E320454048DC4 /* RLMPredicateUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMPredicateUtil.mm; path = Realm/RLMPredicateUtil.mm; sourceTree = ""; }; + 1D0FF5E7C342CDDA865A7FD9DEAE072F /* RLMSyncUser.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUser.mm; path = Realm/RLMSyncUser.mm; sourceTree = ""; }; + 1EEFA9E7914F3F083F26526834392B40 /* librealmcore-ios.a */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = archive.ar; name = "librealmcore-ios.a"; path = "core/librealmcore-ios.a"; sourceTree = ""; }; + 1FD162DB966535DC7DACCD51EB925A1E /* NSData+ImageContentType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+ImageContentType.m"; path = "SDWebImage/NSData+ImageContentType.m"; sourceTree = ""; }; + 21B5D100083FF70FF4E6A7FA598CA52C /* RLMProperty.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMProperty.mm; path = Realm/RLMProperty.mm; sourceTree = ""; }; + 21CAAF37C57AF04C13AFDAA0DA5700DC /* RLMObjectSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema_Private.h; path = include/RLMObjectSchema_Private.h; sourceTree = ""; }; + 220A12690C158F4D788C82DBD372547D /* RLMSyncPermissionOffer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionOffer.m; path = Realm/RLMSyncPermissionOffer.m; sourceTree = ""; }; + 22397DB45B0DC905481B9F5C60E1995D /* RLMSyncUtil_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil_Private.h; path = include/RLMSyncUtil_Private.h; sourceTree = ""; }; + 26E972922EBAB79646170E2C54A4CB79 /* RLMSyncSessionRefreshHandle.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSessionRefreshHandle.mm; path = Realm/RLMSyncSessionRefreshHandle.mm; sourceTree = ""; }; + 277473E55172F1FC7AA0EEBFACEE5CE4 /* RLMProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty.h; path = include/RLMProperty.h; sourceTree = ""; }; + 27BE8D23940F02EABA465C4DA3239DAB /* RLMObjectBase_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Dynamic.h; path = include/RLMObjectBase_Dynamic.h; sourceTree = ""; }; + 2A4DD37E2EEDC35FD41274E27DC63DE0 /* RLMTokenModels.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMTokenModels.m; path = Realm/RLMTokenModels.m; sourceTree = ""; }; + 2FDC28BB1F950A28F4952E24A637E09E /* Pods-Desafio Mobile iOS-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Desafio Mobile iOS-acknowledgements.markdown"; sourceTree = ""; }; + 33EF6C7597A35770AA0F35B1099F916E /* RLMSyncPermission_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermission_Private.h; path = include/RLMSyncPermission_Private.h; sourceTree = ""; }; + 37541A8B864CC0126A2EA16F173B3E6E /* SDImageCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDImageCache.h; path = SDWebImage/SDImageCache.h; sourceTree = ""; }; + 38EA12D78AE4A3417F01E37BC28CCAE5 /* SDImageCacheConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDImageCacheConfig.m; path = SDWebImage/SDImageCacheConfig.m; sourceTree = ""; }; + 39E2CFB09710154BF7171B79A1D05AEF /* RLMUpdateChecker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUpdateChecker.mm; path = Realm/RLMUpdateChecker.mm; sourceTree = ""; }; + 3FEB6A4D56141A837470FBB2D9AB0C56 /* RLMArrayLinkView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArrayLinkView.mm; path = Realm/RLMArrayLinkView.mm; sourceTree = ""; }; + 41ECB19FD12F5D933BC8175EDC1C528E /* keychain_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = keychain_helper.cpp; path = Realm/ObjectStore/src/impl/apple/keychain_helper.cpp; sourceTree = ""; }; + 422D00835B6AB84344FB361CA87E9160 /* RLMSyncConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncConfiguration.mm; path = Realm/RLMSyncConfiguration.mm; sourceTree = ""; }; + 45F74D1F561F8A89497DD9F07E4650BA /* RLMSyncPermissionChange.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionChange.m; path = Realm/RLMSyncPermissionChange.m; sourceTree = ""; }; + 469BAE3C50A964F499E4A579E7C5741C /* RLMSyncPermissionOffer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOffer.h; path = include/RLMSyncPermissionOffer.h; sourceTree = ""; }; + 479E305AC8E86C904474E5B765368534 /* Pods-Desafio Mobile iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Desafio Mobile iOS.release.xcconfig"; sourceTree = ""; }; + 47D248AA38929821C24CAC934A468BE2 /* Pods-Desafio Mobile iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Desafio Mobile iOSTests.debug.xcconfig"; sourceTree = ""; }; + 480BA2678EA0B20747D28954B9457349 /* binding_callback_thread_observer.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = binding_callback_thread_observer.cpp; path = Realm/ObjectStore/src/binding_callback_thread_observer.cpp; sourceTree = ""; }; + 4A6C409EE08898A9F75B979D1716E280 /* RLMObject_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject_Private.h; path = include/RLMObject_Private.h; sourceTree = ""; }; + 4C745587F08365BB46ADCCCCF49E5BA0 /* NSImage+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSImage+WebCache.h"; path = "SDWebImage/NSImage+WebCache.h"; sourceTree = ""; }; + 4F526340DE76D2721709F77E1390A7F4 /* RLMSyncPermissionOfferResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionOfferResponse.m; path = Realm/RLMSyncPermissionOfferResponse.m; sourceTree = ""; }; + 4FDECBBF23CD9C3FBE77BBFC7E96ED5B /* RLMSyncPermissionChange.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionChange.h; path = include/RLMSyncPermissionChange.h; sourceTree = ""; }; + 517D872E100B92DACB9CBBD119BFA669 /* RLMObjectSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectSchema.mm; path = Realm/RLMObjectSchema.mm; sourceTree = ""; }; + 5203C5C302EC376987BA35B49E447C84 /* RLMSyncManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncManager.mm; path = Realm/RLMSyncManager.mm; sourceTree = ""; }; + 527C73D5AEE12B4056584D0BD7B7E5E1 /* SDWebImageDecoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDecoder.h; path = SDWebImage/SDWebImageDecoder.h; sourceTree = ""; }; + 55C52265C1B79322AEB93DEB670A2A6C /* UIView+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+WebCache.m"; path = "SDWebImage/UIView+WebCache.m"; sourceTree = ""; }; + 5784110D2D9D440E7529052CD4983B5D /* index_set.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = index_set.cpp; path = Realm/ObjectStore/src/index_set.cpp; sourceTree = ""; }; + 593E5502D5C2A2E653EFA26B3C6458AE /* RLMMigration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration_Private.h; path = include/RLMMigration_Private.h; sourceTree = ""; }; + 59D6D321140F8A82333E2770F87236F4 /* RLMSyncSession.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSession.mm; path = Realm/RLMSyncSession.mm; sourceTree = ""; }; + 59F10F8EEB026DAA095E43E202775EBA /* libRealm.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libRealm.a; path = libRealm.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 5A01752D41D4B2DE07A4392FB842B67B /* UIImageView+HighlightedWebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+HighlightedWebCache.m"; path = "SDWebImage/UIImageView+HighlightedWebCache.m"; sourceTree = ""; }; + 5AA21461FD31896521033F32C1CCE3A3 /* RLMThreadSafeReference.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMThreadSafeReference.mm; path = Realm/RLMThreadSafeReference.mm; sourceTree = ""; }; + 5B84F30F571C9014C9354C57D8A2E367 /* collection_notifications.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifications.cpp; path = Realm/ObjectStore/src/collection_notifications.cpp; sourceTree = ""; }; + 5BEA192D19CEB6542A30F5A1A8FF2A32 /* SDWebImageDownloader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloader.m; path = SDWebImage/SDWebImageDownloader.m; sourceTree = ""; }; + 5D7EABAEE67288A5E87C358894E1599B /* RLMRealm.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealm.mm; path = Realm/RLMRealm.mm; sourceTree = ""; }; + 5DDDCE27162CDD9C5B8AD8E4F8B84165 /* RLMObjectStore.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectStore.mm; path = Realm/RLMObjectStore.mm; sourceTree = ""; }; + 601E0080B3DDBE1E440DAC7E43C93146 /* libPods-Desafio Mobile iOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-Desafio Mobile iOSTests.a"; path = "libPods-Desafio Mobile iOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 60DC3B4C0D8D01198DFA6FD282AE930B /* NSError+RLMSync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+RLMSync.h"; path = "include/NSError+RLMSync.h"; sourceTree = ""; }; + 618D2C64EB8124EE3883027FAD9E0BBF /* Pods-Desafio Mobile iOS-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Desafio Mobile iOS-resources.sh"; sourceTree = ""; }; + 635A4BD1CEF7E623C71F97A54BFC3616 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; }; + 646A7B3A448DFB761E1977174B5A7013 /* results_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results_notifier.cpp; path = Realm/ObjectStore/src/impl/results_notifier.cpp; sourceTree = ""; }; + 64D99E5DADB2722076BB81B4C0DBD245 /* UIView+WebCacheOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCacheOperation.h"; path = "SDWebImage/UIView+WebCacheOperation.h"; sourceTree = ""; }; + 65E123B54B6D20EA415F8403E9765FED /* RLMConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMConstants.h; path = include/RLMConstants.h; sourceTree = ""; }; + 6610131A68E9CD9EE8E012594E5820EC /* RLMOptionalBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMOptionalBase.mm; path = Realm/RLMOptionalBase.mm; sourceTree = ""; }; + 670A05949EDA53CBF2DF4DE915542CA4 /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = weak_realm_notifier.cpp; path = Realm/ObjectStore/src/impl/weak_realm_notifier.cpp; sourceTree = ""; }; + 67A5B48E045080C17F02CF5211E8C23A /* RLMRealm_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Dynamic.h; path = include/RLMRealm_Dynamic.h; sourceTree = ""; }; + 6B30121CD919A4F16A8D4CEBBECDD912 /* RLMSyncSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSession.h; path = include/RLMSyncSession.h; sourceTree = ""; }; + 6C93097CD5BF2617EE04F5DFB5506171 /* list.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list.cpp; path = Realm/ObjectStore/src/list.cpp; sourceTree = ""; }; + 6C98DED74949E03CE103B50AA57088F7 /* RLMRealm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm.h; path = include/RLMRealm.h; sourceTree = ""; }; + 6CA6F32964BE8ECC00A9DB0BDFAC5243 /* UIView+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCache.h"; path = "SDWebImage/UIView+WebCache.h"; sourceTree = ""; }; + 6CCA204A8DBD6C1731D61372F026B150 /* RLMResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMResults.mm; path = Realm/RLMResults.mm; sourceTree = ""; }; + 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 6DEA48476564535FB9BCD4CC3A89E184 /* RLMSyncManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager.h; path = include/RLMSyncManager.h; sourceTree = ""; }; + 6F5FE78719A7FE69DF041C1556C14599 /* RLMSyncPermission.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermission.m; path = Realm/RLMSyncPermission.m; sourceTree = ""; }; + 6F6740654F4F686F2BA043704C2A9384 /* object_store.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_store.cpp; path = Realm/ObjectStore/src/object_store.cpp; sourceTree = ""; }; + 704D2448775DB8C97961E8846FBF4074 /* SDWebImagePrefetcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImagePrefetcher.m; path = SDWebImage/SDWebImagePrefetcher.m; sourceTree = ""; }; + 72A8A7B1447872ED0FC8B2E10F1104E0 /* libPods-Desafio Mobile iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-Desafio Mobile iOS.a"; path = "libPods-Desafio Mobile iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 734CB0970A71F73360F4DED8364555FB /* RLMProperty_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty_Private.h; path = include/RLMProperty_Private.h; sourceTree = ""; }; + 7479D62E72AF5740C05151D2D3D7559B /* RLMObjectSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema.h; path = include/RLMObjectSchema.h; sourceTree = ""; }; + 759C5BAB73B1D5AB350781ED3102911C /* RLMThreadSafeReference.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMThreadSafeReference.h; path = include/RLMThreadSafeReference.h; sourceTree = ""; }; + 769A808B3BEC6C2B5C7610F2B2D6E22D /* results.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results.cpp; path = Realm/ObjectStore/src/results.cpp; sourceTree = ""; }; + 76AB5FBBD310D8F3D1F5B59109FC7692 /* RLMOptionalBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMOptionalBase.h; path = include/RLMOptionalBase.h; sourceTree = ""; }; + 77661AFF7883EC57BDAA0E3A2ED0771A /* shared_realm.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = shared_realm.cpp; path = Realm/ObjectStore/src/shared_realm.cpp; sourceTree = ""; }; + 7B27474262B3809F0316D027A69EA3C1 /* SDWebImageCompat.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageCompat.h; path = SDWebImage/SDWebImageCompat.h; sourceTree = ""; }; + 7E6F9A33E633BFA78C970B9A6D9BFD5D /* Realm-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Realm-dummy.m"; sourceTree = ""; }; + 7FB4BBE40FF20BD61EC05DC016D09AAA /* RLMSyncErrorResponseModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncErrorResponseModel.m; path = Realm/RLMSyncErrorResponseModel.m; sourceTree = ""; }; + 8361E0DB72B22FB078D199DBC4E22A42 /* RLMListBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMListBase.mm; path = Realm/RLMListBase.mm; sourceTree = ""; }; + 84C855EEEDC27E68FF2865F067CA235E /* sync_user.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_user.cpp; path = Realm/ObjectStore/src/sync/sync_user.cpp; sourceTree = ""; }; + 85B8B0BDF6A4A4B7AE5C2D73E53B98E5 /* RLMSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSchema.mm; path = Realm/RLMSchema.mm; sourceTree = ""; }; + 86D60BC2C9851AF382A4CD4774360BF2 /* UIImage+GIF.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+GIF.h"; path = "SDWebImage/UIImage+GIF.h"; sourceTree = ""; }; + 86DB7DB8C2F9C52FDFC18A0B55209988 /* sync_file.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_file.cpp; path = Realm/ObjectStore/src/sync/impl/sync_file.cpp; sourceTree = ""; }; + 87917E9B1B2D08BF213F53FFE1CE9D4B /* Pods-Desafio Mobile iOSTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Desafio Mobile iOSTests-dummy.m"; sourceTree = ""; }; + 8973FC92EC43A7EE06752F157C8B02EC /* object_schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_schema.cpp; path = Realm/ObjectStore/src/object_schema.cpp; sourceTree = ""; }; + 8A0BB95733259AFD89E3EFF9FCFDCE68 /* SDWebImageDownloaderOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderOperation.h; path = SDWebImage/SDWebImageDownloaderOperation.h; sourceTree = ""; }; + 8BFC61720DC164E671A1C9D6FA83412D /* RLMRealmConfiguration+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealmConfiguration+Sync.mm"; path = "Realm/RLMRealmConfiguration+Sync.mm"; sourceTree = ""; }; + 8DE3ED622494B0462F7B72423C91832D /* UIButton+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIButton+WebCache.m"; path = "SDWebImage/UIButton+WebCache.m"; sourceTree = ""; }; + 8FC57A42C3B2D51AFE89FCAE2E12668A /* SDWebImage-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SDWebImage-prefix.pch"; sourceTree = ""; }; + 8FF56E3E0E6852BDFF322495EE2F80F6 /* RLMSyncUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUtil.mm; path = Realm/RLMSyncUtil.mm; sourceTree = ""; }; + 911B6B50388E4B12B8535401AA5CD0B0 /* Pods-Desafio Mobile iOSTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Desafio Mobile iOSTests-acknowledgements.markdown"; sourceTree = ""; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 93AF25BA9932865B1011C9DE5A60BF29 /* RLMSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema_Private.h; path = include/RLMSchema_Private.h; sourceTree = ""; }; + 94ED30374869F0F107250E36C790C73E /* RLMUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUtil.mm; path = Realm/RLMUtil.mm; sourceTree = ""; }; + 94FE9F896125E14D7129E29C9534404B /* RLMSyncUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil.h; path = include/RLMSyncUtil.h; sourceTree = ""; }; + 950B5184CF33C77407470C00DD42A77D /* RLMSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema.h; path = include/RLMSchema.h; sourceTree = ""; }; + 9590678F806C40E5A5FFC6C9101B9D7B /* SDWebImageDecoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDecoder.m; path = SDWebImage/SDWebImageDecoder.m; sourceTree = ""; }; + 95E5FC8D54560B994FF2EE689169A98C /* Pods-Desafio Mobile iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Desafio Mobile iOS.debug.xcconfig"; sourceTree = ""; }; + 99D02AFC73FF58DB0EC38BB9E8D68E65 /* UIView+WebCacheOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+WebCacheOperation.m"; path = "SDWebImage/UIView+WebCacheOperation.m"; sourceTree = ""; }; + 9CDAA6F76D63D0F7B11821168D44B656 /* SDWebImageOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageOperation.h; path = SDWebImage/SDWebImageOperation.h; sourceTree = ""; }; + 9DE703A1BC9789DB3AC346DD15A1F690 /* UIImage+GIF.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+GIF.m"; path = "SDWebImage/UIImage+GIF.m"; sourceTree = ""; }; + 9E3ADB24AF23510778AC8368F8350E56 /* RLMSwiftSupport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSwiftSupport.m; path = Realm/RLMSwiftSupport.m; sourceTree = ""; }; + 9EBFB93894B5EBE2F8F1D9AD517B4A1D /* list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list_notifier.cpp; path = Realm/ObjectStore/src/impl/list_notifier.cpp; sourceTree = ""; }; + 9FA16A455CD0C8043F5150529FF32CE5 /* realm_coordinator.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = realm_coordinator.cpp; path = Realm/ObjectStore/src/impl/realm_coordinator.cpp; sourceTree = ""; }; + A2CA6093C584B3343F0A8807A3DB715D /* Realm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Realm.h; path = include/Realm.h; sourceTree = ""; }; + A55E7B967F80B4E3E1B9CD954D3A25E9 /* sync_metadata.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_metadata.cpp; path = Realm/ObjectStore/src/sync/impl/sync_metadata.cpp; sourceTree = ""; }; + AB3690AA4798C7FBF70ABC5F6E3453FC /* RLMRealm_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Private.h; path = include/RLMRealm_Private.h; sourceTree = ""; }; + AD6E110FDDE91723A513F55B9A8D9D01 /* Pods-Desafio Mobile iOSTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Desafio Mobile iOSTests-acknowledgements.plist"; sourceTree = ""; }; + AEA0B33E9028B4A3F568A4F0945B3B5F /* Realm-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Realm-prefix.pch"; sourceTree = ""; }; + AF17AA6A17934C2136FEF0682AF011FD /* RLMListBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMListBase.h; path = include/RLMListBase.h; sourceTree = ""; }; + AF59782C23C0289C7AD7FEC02805D9D4 /* collection_change_builder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_change_builder.cpp; path = Realm/ObjectStore/src/impl/collection_change_builder.cpp; sourceTree = ""; }; + B0B6940E9C426189F00EA6E15CABE668 /* NSData+ImageContentType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+ImageContentType.h"; path = "SDWebImage/NSData+ImageContentType.h"; sourceTree = ""; }; + B13D3DC9F2D296B14C868F27D85C3CAC /* RLMCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMCollection.mm; path = Realm/RLMCollection.mm; sourceTree = ""; }; + B16FB1873CA5634B92BCCB39D8C6BA37 /* RLMObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject.h; path = include/RLMObject.h; sourceTree = ""; }; + B1A6B923BC942E39D47071F0D5A7BDEC /* RLMSyncManager_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager_Private.h; path = include/RLMSyncManager_Private.h; sourceTree = ""; }; + B2962DD70AE0673F59388F75BA54971A /* RLMRealmUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmUtil.mm; path = Realm/RLMRealmUtil.mm; sourceTree = ""; }; + B4ECAF64B51C462CD02D736CA0DADFCB /* SDWebImageDownloader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloader.h; path = SDWebImage/SDWebImageDownloader.h; sourceTree = ""; }; + B5942FFA45E36E4CF78550F1809BA751 /* RLMRealmConfiguration+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealmConfiguration+Sync.h"; path = "include/RLMRealmConfiguration+Sync.h"; sourceTree = ""; }; + B6AE1CE6A6B5823DBE1069B163226021 /* SDWebImage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SDWebImage.xcconfig; sourceTree = ""; }; + B956E02FD7E2609D27218601ECF76FE8 /* RLMObjectBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectBase.mm; path = Realm/RLMObjectBase.mm; sourceTree = ""; }; + B9DDABFE0960F37FE332E358A94F7EAF /* RLMAccessor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMAccessor.h; path = include/RLMAccessor.h; sourceTree = ""; }; + BAD213C0608EBC35FBBCBF0614B685D6 /* RLMMigration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration.h; path = include/RLMMigration.h; sourceTree = ""; }; + BB87049A31AFDC36C225747336EF831E /* RLMArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArray.mm; path = Realm/RLMArray.mm; sourceTree = ""; }; + BBA73FC47478A7160B4541A14A51D2BD /* Pods-Desafio Mobile iOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Desafio Mobile iOS-dummy.m"; sourceTree = ""; }; + BBA82F3ECEADE0E821DA4940159A5002 /* RLMObjectBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase.h; path = include/RLMObjectBase.h; sourceTree = ""; }; + BDDCE852809E39202D2EA130C7BE5801 /* Pods-Desafio Mobile iOS-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Desafio Mobile iOS-acknowledgements.plist"; sourceTree = ""; }; + BF8A5B79466FFFFCF6D179236F19624C /* RLMObject.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObject.mm; path = Realm/RLMObject.mm; sourceTree = ""; }; + C1BB46BA6D3990FCBB46EC22EF5FFEED /* RLMPlatform.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMPlatform.h; path = include/RLMPlatform.h; sourceTree = ""; }; + C36730D8B3C481AA3017F50B86398779 /* RLMNetworkClient.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMNetworkClient.m; path = Realm/RLMNetworkClient.m; sourceTree = ""; }; + C6A0EADEE1754130D100D2732C16D1DD /* RLMSyncCredentials.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncCredentials.m; path = Realm/RLMSyncCredentials.m; sourceTree = ""; }; + C7CE7D4DDF14A198856A3FB83E8B6AD5 /* RLMAccessor.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAccessor.mm; path = Realm/RLMAccessor.mm; sourceTree = ""; }; + C8A29904B3F950CFBDF1ACAF6D3E4624 /* RLMSyncConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration_Private.h; path = include/RLMSyncConfiguration_Private.h; sourceTree = ""; }; + CB9047710CCE36410D1B49BD0E48A261 /* RLMRealmConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration_Private.h; path = include/RLMRealmConfiguration_Private.h; sourceTree = ""; }; + CD3BD12ED93702EB66B485C5FD9BCF72 /* format.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = format.cpp; path = Realm/ObjectStore/src/util/format.cpp; sourceTree = ""; }; + CD4A097060C3801879092F226A5AB1C6 /* RLMCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection.h; path = include/RLMCollection.h; sourceTree = ""; }; + CE3AEBCDDD9941C5F04FB1932DBCD708 /* RLMObjectStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectStore.h; path = include/RLMObjectStore.h; sourceTree = ""; }; + CF9074A5B32C58DB476755D22216636F /* SDImageCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDImageCache.m; path = SDWebImage/SDImageCache.m; sourceTree = ""; }; + D2B4EC692C74E86115865A9201BC007C /* NSImage+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSImage+WebCache.m"; path = "SDWebImage/NSImage+WebCache.m"; sourceTree = ""; }; + D6E3BC93D3D17069855E0F22EE09ED40 /* object.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object.cpp; path = Realm/ObjectStore/src/object.cpp; sourceTree = ""; }; + D70C08AFAD2005E596B0A1B32561A7F8 /* uuid.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = uuid.cpp; path = Realm/ObjectStore/src/util/uuid.cpp; sourceTree = ""; }; + D89AED0074B7E408BCE7E686353F6718 /* RLMObservation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObservation.mm; path = Realm/RLMObservation.mm; sourceTree = ""; }; + DA1019C2A5117D3EB62D5AF2F23683AB /* external_commit_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = external_commit_helper.cpp; path = Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp; sourceTree = ""; }; + DA12F6599566891226E26F3779D65128 /* SDImageCacheConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDImageCacheConfig.h; path = SDWebImage/SDImageCacheConfig.h; sourceTree = ""; }; + DD0C0B6784685A4609D09FAB9881DA9D /* RLMRealmConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration.h; path = include/RLMRealmConfiguration.h; sourceTree = ""; }; + DF77562C65F58F802D3A08691A01E39E /* Realm.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Realm.xcconfig; sourceTree = ""; }; + DFE13D841BB61B67678562B498135D81 /* RLMSyncUser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUser.h; path = include/RLMSyncUser.h; sourceTree = ""; }; + E0FDB3D3FF302561E84B93EE43576A8A /* NSError+RLMSync.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+RLMSync.m"; path = "Realm/NSError+RLMSync.m"; sourceTree = ""; }; + E288314559144BA2C4B9DD3722A3D403 /* SDWebImagePrefetcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImagePrefetcher.h; path = SDWebImage/SDWebImagePrefetcher.h; sourceTree = ""; }; + E3D6BD3C36D777BFC6B9FED40AE7AB71 /* Pods-Desafio Mobile iOSTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Desafio Mobile iOSTests-frameworks.sh"; sourceTree = ""; }; + E4671ECBDB8117E509BC970D325903AB /* placeholder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = placeholder.cpp; path = Realm/ObjectStore/src/placeholder.cpp; sourceTree = ""; }; + E51FEF8D2A6E612B6005BADBA049CF10 /* RLMResults_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults_Private.h; path = include/RLMResults_Private.h; sourceTree = ""; }; + E52600804C8C497C321CA5BD0B541504 /* UIImage+MultiFormat.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MultiFormat.m"; path = "SDWebImage/UIImage+MultiFormat.m"; sourceTree = ""; }; + E8216EEEA3B3986A6DF938AD56375771 /* SDWebImageCompat.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageCompat.m; path = SDWebImage/SDWebImageCompat.m; sourceTree = ""; }; + E9CB31A3F767F0A10510AF56B5F20615 /* SDWebImageManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageManager.m; path = SDWebImage/SDWebImageManager.m; sourceTree = ""; }; + EBDB598A010C1BA1CB96BE949347951B /* RLMClassInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMClassInfo.mm; path = Realm/RLMClassInfo.mm; sourceTree = ""; }; + EDEB0A2D168B99311B9C5FD187F57C13 /* RLMQueryUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMQueryUtil.mm; path = Realm/RLMQueryUtil.mm; sourceTree = ""; }; + F009073D69CE9F3D3E72CC6A4D7373F6 /* UIImageView+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WebCache.h"; path = "SDWebImage/UIImageView+WebCache.h"; sourceTree = ""; }; + F0A560BD422C74C5CFDDE403A624E1AB /* UIImage+MultiFormat.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MultiFormat.h"; path = "SDWebImage/UIImage+MultiFormat.h"; sourceTree = ""; }; + F1A46FAE7B82BCCD0B8B7E9589E822F3 /* SDWebImage-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SDWebImage-dummy.m"; sourceTree = ""; }; + F302CCC43EDDD3E242714DDE80A23BFB /* RLMAnalytics.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAnalytics.mm; path = Realm/RLMAnalytics.mm; sourceTree = ""; }; + F45834B7576EF0DF143F939105F02D8A /* RLMSyncPermission.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermission.h; path = include/RLMSyncPermission.h; sourceTree = ""; }; + F5FF66D4FA4217E8CB45171E3167FE92 /* RLMSyncConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration.h; path = include/RLMSyncConfiguration.h; sourceTree = ""; }; + F602E82472C8D0D9B550572C60145B61 /* RLMSyncPermissionOfferResponse_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOfferResponse_Private.h; path = include/RLMSyncPermissionOfferResponse_Private.h; sourceTree = ""; }; + F616D63BF66707E6D45930A06800CE7C /* collection_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifier.cpp; path = Realm/ObjectStore/src/impl/collection_notifier.cpp; sourceTree = ""; }; + F6BE95984E42F6D67E00991B95D3265E /* UIButton+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIButton+WebCache.h"; path = "SDWebImage/UIButton+WebCache.h"; sourceTree = ""; }; + F6DD54862A15C98E3C2E1D2C9CE2BD0A /* RLMConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMConstants.m; path = Realm/RLMConstants.m; sourceTree = ""; }; + FA0F4DB00ECDC3064B087ECDDF0F10F0 /* transact_log_handler.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = transact_log_handler.cpp; path = Realm/ObjectStore/src/impl/transact_log_handler.cpp; sourceTree = ""; }; + FB2E6781D27936DAA0B3C01E9D13CC27 /* UIImageView+HighlightedWebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+HighlightedWebCache.h"; path = "SDWebImage/UIImageView+HighlightedWebCache.h"; sourceTree = ""; }; + FD3FD7CFFE5C1010EF9A3E05AB64FB5D /* thread_safe_reference.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = thread_safe_reference.cpp; path = Realm/ObjectStore/src/thread_safe_reference.cpp; sourceTree = ""; }; + FDA9DB26D6B5AF80941AE65BC2A0A811 /* UIImageView+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WebCache.m"; path = "SDWebImage/UIImageView+WebCache.m"; sourceTree = ""; }; + FE210EE438C3A3B5193D6623F9310B17 /* RLMSyncPermissionOffer_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOffer_Private.h; path = include/RLMSyncPermissionOffer_Private.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 11D2B9968AC98336FF8C3F2D07B0ADAF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C06A5EBBA4F5F42C735A887D4ECA307C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2028F5563847A93F8BA6F8FCE4B3DB0A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E785D847C3929D6FE134719914E0B4D /* Foundation.framework in Frameworks */, + 487C9CBD3BF11BBCB1297D8BE3B7C840 /* ImageIO.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 413F75054B882315EFBCFFD54BFDEF69 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F0F318EF039D0DD32F79CD564FC1DD03 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F546234BAFAD057857078831EC1D5441 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42AA286D61B95AAA0AD7E81A51395371 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0B5BD33D87409DF778565F548C77CD61 /* Headers */ = { + isa = PBXGroup; + children = ( + 60DC3B4C0D8D01198DFA6FD282AE930B /* NSError+RLMSync.h */, + A2CA6093C584B3343F0A8807A3DB715D /* Realm.h */, + 0716B9F43BC5160CEFABA4D4DFDBCEB9 /* RLMArray.h */, + CD4A097060C3801879092F226A5AB1C6 /* RLMCollection.h */, + 65E123B54B6D20EA415F8403E9765FED /* RLMConstants.h */, + BAD213C0608EBC35FBBCBF0614B685D6 /* RLMMigration.h */, + B16FB1873CA5634B92BCCB39D8C6BA37 /* RLMObject.h */, + BBA82F3ECEADE0E821DA4940159A5002 /* RLMObjectBase.h */, + 27BE8D23940F02EABA465C4DA3239DAB /* RLMObjectBase_Dynamic.h */, + 7479D62E72AF5740C05151D2D3D7559B /* RLMObjectSchema.h */, + C1BB46BA6D3990FCBB46EC22EF5FFEED /* RLMPlatform.h */, + 277473E55172F1FC7AA0EEBFACEE5CE4 /* RLMProperty.h */, + 6C98DED74949E03CE103B50AA57088F7 /* RLMRealm.h */, + 67A5B48E045080C17F02CF5211E8C23A /* RLMRealm_Dynamic.h */, + DD0C0B6784685A4609D09FAB9881DA9D /* RLMRealmConfiguration.h */, + B5942FFA45E36E4CF78550F1809BA751 /* RLMRealmConfiguration+Sync.h */, + 147F70A36C7F34A87CDEC9471965A107 /* RLMResults.h */, + 950B5184CF33C77407470C00DD42A77D /* RLMSchema.h */, + F5FF66D4FA4217E8CB45171E3167FE92 /* RLMSyncConfiguration.h */, + 058356C59693A79C1F85D180E992F091 /* RLMSyncCredentials.h */, + 6DEA48476564535FB9BCD4CC3A89E184 /* RLMSyncManager.h */, + F45834B7576EF0DF143F939105F02D8A /* RLMSyncPermission.h */, + 4FDECBBF23CD9C3FBE77BBFC7E96ED5B /* RLMSyncPermissionChange.h */, + 469BAE3C50A964F499E4A579E7C5741C /* RLMSyncPermissionOffer.h */, + 13949A73AEAD1F2A34072F8F54B1DF8E /* RLMSyncPermissionOfferResponse.h */, + 6B30121CD919A4F16A8D4CEBBECDD912 /* RLMSyncSession.h */, + DFE13D841BB61B67678562B498135D81 /* RLMSyncUser.h */, + 94FE9F896125E14D7129E29C9534404B /* RLMSyncUtil.h */, + 759C5BAB73B1D5AB350781ED3102911C /* RLMThreadSafeReference.h */, + ); + name = Headers; + sourceTree = ""; + }; + 1FADA36CBBEBF5DF6696194D87183478 /* iOS */ = { + isa = PBXGroup; + children = ( + 6CE29CF4FF5C02028C0A06EC0D2A6120 /* Foundation.framework */, + 635A4BD1CEF7E623C71F97A54BFC3616 /* ImageIO.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 24819CA9E700D8BE070CD24B935EB9B5 /* Core */ = { + isa = PBXGroup; + children = ( + B0B6940E9C426189F00EA6E15CABE668 /* NSData+ImageContentType.h */, + 1FD162DB966535DC7DACCD51EB925A1E /* NSData+ImageContentType.m */, + 4C745587F08365BB46ADCCCCF49E5BA0 /* NSImage+WebCache.h */, + D2B4EC692C74E86115865A9201BC007C /* NSImage+WebCache.m */, + 37541A8B864CC0126A2EA16F173B3E6E /* SDImageCache.h */, + CF9074A5B32C58DB476755D22216636F /* SDImageCache.m */, + DA12F6599566891226E26F3779D65128 /* SDImageCacheConfig.h */, + 38EA12D78AE4A3417F01E37BC28CCAE5 /* SDImageCacheConfig.m */, + 7B27474262B3809F0316D027A69EA3C1 /* SDWebImageCompat.h */, + E8216EEEA3B3986A6DF938AD56375771 /* SDWebImageCompat.m */, + 527C73D5AEE12B4056584D0BD7B7E5E1 /* SDWebImageDecoder.h */, + 9590678F806C40E5A5FFC6C9101B9D7B /* SDWebImageDecoder.m */, + B4ECAF64B51C462CD02D736CA0DADFCB /* SDWebImageDownloader.h */, + 5BEA192D19CEB6542A30F5A1A8FF2A32 /* SDWebImageDownloader.m */, + 8A0BB95733259AFD89E3EFF9FCFDCE68 /* SDWebImageDownloaderOperation.h */, + 0980C0164722B2FAA1F278F4603FD7A2 /* SDWebImageDownloaderOperation.m */, + 1362F52E5DF44C7F11175B870C39B249 /* SDWebImageManager.h */, + E9CB31A3F767F0A10510AF56B5F20615 /* SDWebImageManager.m */, + 9CDAA6F76D63D0F7B11821168D44B656 /* SDWebImageOperation.h */, + E288314559144BA2C4B9DD3722A3D403 /* SDWebImagePrefetcher.h */, + 704D2448775DB8C97961E8846FBF4074 /* SDWebImagePrefetcher.m */, + F6BE95984E42F6D67E00991B95D3265E /* UIButton+WebCache.h */, + 8DE3ED622494B0462F7B72423C91832D /* UIButton+WebCache.m */, + 86D60BC2C9851AF382A4CD4774360BF2 /* UIImage+GIF.h */, + 9DE703A1BC9789DB3AC346DD15A1F690 /* UIImage+GIF.m */, + F0A560BD422C74C5CFDDE403A624E1AB /* UIImage+MultiFormat.h */, + E52600804C8C497C321CA5BD0B541504 /* UIImage+MultiFormat.m */, + FB2E6781D27936DAA0B3C01E9D13CC27 /* UIImageView+HighlightedWebCache.h */, + 5A01752D41D4B2DE07A4392FB842B67B /* UIImageView+HighlightedWebCache.m */, + F009073D69CE9F3D3E72CC6A4D7373F6 /* UIImageView+WebCache.h */, + FDA9DB26D6B5AF80941AE65BC2A0A811 /* UIImageView+WebCache.m */, + 6CA6F32964BE8ECC00A9DB0BDFAC5243 /* UIView+WebCache.h */, + 55C52265C1B79322AEB93DEB670A2A6C /* UIView+WebCache.m */, + 64D99E5DADB2722076BB81B4C0DBD245 /* UIView+WebCacheOperation.h */, + 99D02AFC73FF58DB0EC38BB9E8D68E65 /* UIView+WebCacheOperation.m */, + ); + name = Core; + sourceTree = ""; + }; + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1FADA36CBBEBF5DF6696194D87183478 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 45763AB1233EBF7EF4589B0CAACAC535 /* Support Files */ = { + isa = PBXGroup; + children = ( + B6AE1CE6A6B5823DBE1069B163226021 /* SDWebImage.xcconfig */, + F1A46FAE7B82BCCD0B8B7E9589E822F3 /* SDWebImage-dummy.m */, + 8FC57A42C3B2D51AFE89FCAE2E12668A /* SDWebImage-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/SDWebImage"; + sourceTree = ""; + }; + 5104C2E772DA44E8C8B024E50AB84D52 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + B98AA33523FD93AE2A3F20DDF2D52D1A /* Pods-Desafio Mobile iOS */, + 8688D9DD12E0231ACF4BA24614D025C5 /* Pods-Desafio Mobile iOSTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 5E659255C254CE207805D99712B2B611 /* Support Files */ = { + isa = PBXGroup; + children = ( + DF77562C65F58F802D3A08691A01E39E /* Realm.xcconfig */, + 7E6F9A33E633BFA78C970B9A6D9BFD5D /* Realm-dummy.m */, + AEA0B33E9028B4A3F568A4F0945B3B5F /* Realm-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Realm"; + sourceTree = ""; + }; + 780A3B6B9B4252B0E8402AD17B459F37 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1EEFA9E7914F3F083F26526834392B40 /* librealmcore-ios.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 433CD3331B6C3787F473C941B61FC68F /* Frameworks */, + 8DEB430C0FB0DC826B718845F4E36BA2 /* Pods */, + D89CD6A2FFADDBFB4B2ECEF938FCE672 /* Products */, + 5104C2E772DA44E8C8B024E50AB84D52 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 8688D9DD12E0231ACF4BA24614D025C5 /* Pods-Desafio Mobile iOSTests */ = { + isa = PBXGroup; + children = ( + 911B6B50388E4B12B8535401AA5CD0B0 /* Pods-Desafio Mobile iOSTests-acknowledgements.markdown */, + AD6E110FDDE91723A513F55B9A8D9D01 /* Pods-Desafio Mobile iOSTests-acknowledgements.plist */, + 87917E9B1B2D08BF213F53FFE1CE9D4B /* Pods-Desafio Mobile iOSTests-dummy.m */, + E3D6BD3C36D777BFC6B9FED40AE7AB71 /* Pods-Desafio Mobile iOSTests-frameworks.sh */, + 0D9974B0DE0180DA0A3C38EA5A53FB0F /* Pods-Desafio Mobile iOSTests-resources.sh */, + 47D248AA38929821C24CAC934A468BE2 /* Pods-Desafio Mobile iOSTests.debug.xcconfig */, + 10505976D22548D10C51FDD5819BA4A6 /* Pods-Desafio Mobile iOSTests.release.xcconfig */, + ); + name = "Pods-Desafio Mobile iOSTests"; + path = "Target Support Files/Pods-Desafio Mobile iOSTests"; + sourceTree = ""; + }; + 8DEB430C0FB0DC826B718845F4E36BA2 /* Pods */ = { + isa = PBXGroup; + children = ( + E5AE5820771D0BE4F64E378946B7017C /* Realm */, + EBEDF697E1527B722F3A531FCD565B34 /* SDWebImage */, + ); + name = Pods; + sourceTree = ""; + }; + B98AA33523FD93AE2A3F20DDF2D52D1A /* Pods-Desafio Mobile iOS */ = { + isa = PBXGroup; + children = ( + 2FDC28BB1F950A28F4952E24A637E09E /* Pods-Desafio Mobile iOS-acknowledgements.markdown */, + BDDCE852809E39202D2EA130C7BE5801 /* Pods-Desafio Mobile iOS-acknowledgements.plist */, + BBA73FC47478A7160B4541A14A51D2BD /* Pods-Desafio Mobile iOS-dummy.m */, + 06E777B7DC7266D5C62E62B00BB1F182 /* Pods-Desafio Mobile iOS-frameworks.sh */, + 618D2C64EB8124EE3883027FAD9E0BBF /* Pods-Desafio Mobile iOS-resources.sh */, + 95E5FC8D54560B994FF2EE689169A98C /* Pods-Desafio Mobile iOS.debug.xcconfig */, + 479E305AC8E86C904474E5B765368534 /* Pods-Desafio Mobile iOS.release.xcconfig */, + ); + name = "Pods-Desafio Mobile iOS"; + path = "Target Support Files/Pods-Desafio Mobile iOS"; + sourceTree = ""; + }; + D89CD6A2FFADDBFB4B2ECEF938FCE672 /* Products */ = { + isa = PBXGroup; + children = ( + 72A8A7B1447872ED0FC8B2E10F1104E0 /* libPods-Desafio Mobile iOS.a */, + 601E0080B3DDBE1E440DAC7E43C93146 /* libPods-Desafio Mobile iOSTests.a */, + 59F10F8EEB026DAA095E43E202775EBA /* libRealm.a */, + 08C19DD1D938007A493F915DCFC19B3D /* libSDWebImage.a */, + ); + name = Products; + sourceTree = ""; + }; + E5AE5820771D0BE4F64E378946B7017C /* Realm */ = { + isa = PBXGroup; + children = ( + 480BA2678EA0B20747D28954B9457349 /* binding_callback_thread_observer.cpp */, + AF59782C23C0289C7AD7FEC02805D9D4 /* collection_change_builder.cpp */, + 5B84F30F571C9014C9354C57D8A2E367 /* collection_notifications.cpp */, + F616D63BF66707E6D45930A06800CE7C /* collection_notifier.cpp */, + DA1019C2A5117D3EB62D5AF2F23683AB /* external_commit_helper.cpp */, + CD3BD12ED93702EB66B485C5FD9BCF72 /* format.cpp */, + 5784110D2D9D440E7529052CD4983B5D /* index_set.cpp */, + 41ECB19FD12F5D933BC8175EDC1C528E /* keychain_helper.cpp */, + 6C93097CD5BF2617EE04F5DFB5506171 /* list.cpp */, + 9EBFB93894B5EBE2F8F1D9AD517B4A1D /* list_notifier.cpp */, + E0FDB3D3FF302561E84B93EE43576A8A /* NSError+RLMSync.m */, + D6E3BC93D3D17069855E0F22EE09ED40 /* object.cpp */, + 11C14E42F4CA776D66F02E1890C37795 /* object_notifier.cpp */, + 8973FC92EC43A7EE06752F157C8B02EC /* object_schema.cpp */, + 6F6740654F4F686F2BA043704C2A9384 /* object_store.cpp */, + E4671ECBDB8117E509BC970D325903AB /* placeholder.cpp */, + 9FA16A455CD0C8043F5150529FF32CE5 /* realm_coordinator.cpp */, + 769A808B3BEC6C2B5C7610F2B2D6E22D /* results.cpp */, + 646A7B3A448DFB761E1977174B5A7013 /* results_notifier.cpp */, + B9DDABFE0960F37FE332E358A94F7EAF /* RLMAccessor.h */, + C7CE7D4DDF14A198856A3FB83E8B6AD5 /* RLMAccessor.mm */, + F302CCC43EDDD3E242714DDE80A23BFB /* RLMAnalytics.mm */, + BB87049A31AFDC36C225747336EF831E /* RLMArray.mm */, + 05EEB40E22F2D62CDE5B62B016ECC84E /* RLMArray_Private.h */, + 3FEB6A4D56141A837470FBB2D9AB0C56 /* RLMArrayLinkView.mm */, + 19E6087BA47B7F737F80E76048C69DA6 /* RLMAuthResponseModel.m */, + EBDB598A010C1BA1CB96BE949347951B /* RLMClassInfo.mm */, + B13D3DC9F2D296B14C868F27D85C3CAC /* RLMCollection.mm */, + F6DD54862A15C98E3C2E1D2C9CE2BD0A /* RLMConstants.m */, + AF17AA6A17934C2136FEF0682AF011FD /* RLMListBase.h */, + 8361E0DB72B22FB078D199DBC4E22A42 /* RLMListBase.mm */, + 05BA76C0FA2CD68B2D1D8327F2EDEC66 /* RLMMigration.mm */, + 593E5502D5C2A2E653EFA26B3C6458AE /* RLMMigration_Private.h */, + C36730D8B3C481AA3017F50B86398779 /* RLMNetworkClient.m */, + BF8A5B79466FFFFCF6D179236F19624C /* RLMObject.mm */, + 4A6C409EE08898A9F75B979D1716E280 /* RLMObject_Private.h */, + B956E02FD7E2609D27218601ECF76FE8 /* RLMObjectBase.mm */, + 517D872E100B92DACB9CBBD119BFA669 /* RLMObjectSchema.mm */, + 21CAAF37C57AF04C13AFDAA0DA5700DC /* RLMObjectSchema_Private.h */, + CE3AEBCDDD9941C5F04FB1932DBCD708 /* RLMObjectStore.h */, + 5DDDCE27162CDD9C5B8AD8E4F8B84165 /* RLMObjectStore.mm */, + D89AED0074B7E408BCE7E686353F6718 /* RLMObservation.mm */, + 76AB5FBBD310D8F3D1F5B59109FC7692 /* RLMOptionalBase.h */, + 6610131A68E9CD9EE8E012594E5820EC /* RLMOptionalBase.mm */, + 1C0AC996A4E42A1D484E320454048DC4 /* RLMPredicateUtil.mm */, + 21B5D100083FF70FF4E6A7FA598CA52C /* RLMProperty.mm */, + 734CB0970A71F73360F4DED8364555FB /* RLMProperty_Private.h */, + EDEB0A2D168B99311B9C5FD187F57C13 /* RLMQueryUtil.mm */, + 5D7EABAEE67288A5E87C358894E1599B /* RLMRealm.mm */, + AB3690AA4798C7FBF70ABC5F6E3453FC /* RLMRealm_Private.h */, + 16E7A5C32559A819868F5C53EFE77DFD /* RLMRealmConfiguration.mm */, + 8BFC61720DC164E671A1C9D6FA83412D /* RLMRealmConfiguration+Sync.mm */, + CB9047710CCE36410D1B49BD0E48A261 /* RLMRealmConfiguration_Private.h */, + B2962DD70AE0673F59388F75BA54971A /* RLMRealmUtil.mm */, + 6CCA204A8DBD6C1731D61372F026B150 /* RLMResults.mm */, + E51FEF8D2A6E612B6005BADBA049CF10 /* RLMResults_Private.h */, + 85B8B0BDF6A4A4B7AE5C2D73E53B98E5 /* RLMSchema.mm */, + 93AF25BA9932865B1011C9DE5A60BF29 /* RLMSchema_Private.h */, + 9E3ADB24AF23510778AC8368F8350E56 /* RLMSwiftSupport.m */, + 422D00835B6AB84344FB361CA87E9160 /* RLMSyncConfiguration.mm */, + C8A29904B3F950CFBDF1ACAF6D3E4624 /* RLMSyncConfiguration_Private.h */, + C6A0EADEE1754130D100D2732C16D1DD /* RLMSyncCredentials.m */, + 7FB4BBE40FF20BD61EC05DC016D09AAA /* RLMSyncErrorResponseModel.m */, + 5203C5C302EC376987BA35B49E447C84 /* RLMSyncManager.mm */, + B1A6B923BC942E39D47071F0D5A7BDEC /* RLMSyncManager_Private.h */, + 6F5FE78719A7FE69DF041C1556C14599 /* RLMSyncPermission.m */, + 33EF6C7597A35770AA0F35B1099F916E /* RLMSyncPermission_Private.h */, + 45F74D1F561F8A89497DD9F07E4650BA /* RLMSyncPermissionChange.m */, + 0C6D75E91EC2A7A480A3448C202A86DC /* RLMSyncPermissionChange_Private.h */, + 220A12690C158F4D788C82DBD372547D /* RLMSyncPermissionOffer.m */, + FE210EE438C3A3B5193D6623F9310B17 /* RLMSyncPermissionOffer_Private.h */, + 4F526340DE76D2721709F77E1390A7F4 /* RLMSyncPermissionOfferResponse.m */, + F602E82472C8D0D9B550572C60145B61 /* RLMSyncPermissionOfferResponse_Private.h */, + 59D6D321140F8A82333E2770F87236F4 /* RLMSyncSession.mm */, + 26E972922EBAB79646170E2C54A4CB79 /* RLMSyncSessionRefreshHandle.mm */, + 1D0FF5E7C342CDDA865A7FD9DEAE072F /* RLMSyncUser.mm */, + 8FF56E3E0E6852BDFF322495EE2F80F6 /* RLMSyncUtil.mm */, + 22397DB45B0DC905481B9F5C60E1995D /* RLMSyncUtil_Private.h */, + 5AA21461FD31896521033F32C1CCE3A3 /* RLMThreadSafeReference.mm */, + 2A4DD37E2EEDC35FD41274E27DC63DE0 /* RLMTokenModels.m */, + 39E2CFB09710154BF7171B79A1D05AEF /* RLMUpdateChecker.mm */, + 94ED30374869F0F107250E36C790C73E /* RLMUtil.mm */, + 0AAC87378E207F74D0C50B3271F006F0 /* schema.cpp */, + 77661AFF7883EC57BDAA0E3A2ED0771A /* shared_realm.cpp */, + 86DB7DB8C2F9C52FDFC18A0B55209988 /* sync_file.cpp */, + 0E260A54AD827D2E5463D21F47D42ABA /* sync_manager.cpp */, + A55E7B967F80B4E3E1B9CD954D3A25E9 /* sync_metadata.cpp */, + 0DD37BDBF0626F8C34DA78DD538973C7 /* sync_permission.cpp */, + 0521EB34E4CB550CE47626783B1916D6 /* sync_session.cpp */, + 84C855EEEDC27E68FF2865F067CA235E /* sync_user.cpp */, + FD3FD7CFFE5C1010EF9A3E05AB64FB5D /* thread_safe_reference.cpp */, + FA0F4DB00ECDC3064B087ECDDF0F10F0 /* transact_log_handler.cpp */, + D70C08AFAD2005E596B0A1B32561A7F8 /* uuid.cpp */, + 670A05949EDA53CBF2DF4DE915542CA4 /* weak_realm_notifier.cpp */, + 780A3B6B9B4252B0E8402AD17B459F37 /* Frameworks */, + 0B5BD33D87409DF778565F548C77CD61 /* Headers */, + 5E659255C254CE207805D99712B2B611 /* Support Files */, + ); + name = Realm; + path = Realm; + sourceTree = ""; + }; + EBEDF697E1527B722F3A531FCD565B34 /* SDWebImage */ = { + isa = PBXGroup; + children = ( + 24819CA9E700D8BE070CD24B935EB9B5 /* Core */, + 45763AB1233EBF7EF4589B0CAACAC535 /* Support Files */, + ); + name = SDWebImage; + path = SDWebImage; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0FAB02922CF8CE05EEEA734151778ED7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1450CE304A65FF1B7026E9749BF2A09C /* NSError+RLMSync.h in Headers */, + 850F47047482200F094356EA5D51088B /* Realm.h in Headers */, + B1B33D1BA74899E877E674B48ADD828D /* RLMAccessor.h in Headers */, + 4248E38695EB8443CDDE2503A966AB2C /* RLMArray.h in Headers */, + 934FB8AB326C322CA518978AA42ADA41 /* RLMArray_Private.h in Headers */, + 87FA44456BACBCA490BAED3E1677FF5B /* RLMCollection.h in Headers */, + AB61EECC730A6FA3F51AA6195B3A4B83 /* RLMConstants.h in Headers */, + 3792263B0EA38B806290894F6F1179AA /* RLMListBase.h in Headers */, + 3BD311C5B0E5EC6803CCDBF92C4BAE1D /* RLMMigration.h in Headers */, + 44DEC525B80B7F5DA345B812AF6BA9F7 /* RLMMigration_Private.h in Headers */, + 32D7FA41645B4B66A8F8E5483F0509EC /* RLMObject.h in Headers */, + CF98D0A1E15130EB88E9AB32246052E9 /* RLMObject_Private.h in Headers */, + 8DDBF6B1E05DD5FB2B1B1ED6D5F50970 /* RLMObjectBase.h in Headers */, + 354D7810AE32D1BDFED7353ED7DE2330 /* RLMObjectBase_Dynamic.h in Headers */, + C3EA75F30CA2E561CEAFA69E6CF13359 /* RLMObjectSchema.h in Headers */, + 224A8C297239095E015C6F9BB54842A5 /* RLMObjectSchema_Private.h in Headers */, + FA4FB93CD480C52649D5A3E742514740 /* RLMObjectStore.h in Headers */, + D0A711692FD5298A867F7CC647C370D9 /* RLMOptionalBase.h in Headers */, + 2DC1A87329344911BBAB71E5CFDD43C8 /* RLMPlatform.h in Headers */, + FA9E11D4654958E095C1122C52CC33E1 /* RLMProperty.h in Headers */, + 76F7FACF1253E5169FCB5723B71ABAB5 /* RLMProperty_Private.h in Headers */, + E90D25561127F86CD9E03D192377FA53 /* RLMRealm.h in Headers */, + C344987A26383C45E6A3512A2201A408 /* RLMRealm_Dynamic.h in Headers */, + A35AC7B43E80A89D56B544657F07B506 /* RLMRealm_Private.h in Headers */, + BC65A75A1E54A79B3FBEF745F3D634FE /* RLMRealmConfiguration+Sync.h in Headers */, + 8EC74957040F2A8AA632967B704367CA /* RLMRealmConfiguration.h in Headers */, + 831940C817C5E294A1BB2CE8B06EF3C7 /* RLMRealmConfiguration_Private.h in Headers */, + CF6B0C9906E27B7462E7366A060E1916 /* RLMResults.h in Headers */, + 967244C6ADD24666A4A24B6C0B2853EA /* RLMResults_Private.h in Headers */, + 8CACD5879EE057347ECAC4DAA253FEF2 /* RLMSchema.h in Headers */, + 141627F89E126C99D54AAEFABD85A972 /* RLMSchema_Private.h in Headers */, + 3EEEAA46855F4007A26FDDBEDD760EA4 /* RLMSyncConfiguration.h in Headers */, + 5732D2F2844A26BD4389B78DFE4BBF67 /* RLMSyncConfiguration_Private.h in Headers */, + D5F0FE0C9BA3404546C6E36A0799D1AC /* RLMSyncCredentials.h in Headers */, + 1BCB7326206F8142B556CF941BA51157 /* RLMSyncManager.h in Headers */, + 77A3574CFA4B4DD2D14F094AA7EE3B4C /* RLMSyncManager_Private.h in Headers */, + 8D3748873DFADF4470A59CD5BF937E59 /* RLMSyncPermission.h in Headers */, + 562B95A60240347DDD6714747676CAB7 /* RLMSyncPermission_Private.h in Headers */, + 74B3CFF8A3C3F674D47831D408AD1B7E /* RLMSyncPermissionChange.h in Headers */, + AEBF88DA579882DB79AE8DBA08C4BB95 /* RLMSyncPermissionChange_Private.h in Headers */, + F2B14EE9901CA12D7143CF62997E7547 /* RLMSyncPermissionOffer.h in Headers */, + 8E9B431E8A8EBBACBE974076B1FD3D51 /* RLMSyncPermissionOffer_Private.h in Headers */, + 724F74D45D88D433447305A9DEE16594 /* RLMSyncPermissionOfferResponse.h in Headers */, + F3AEE01B6C0764C231C881449EC575DD /* RLMSyncPermissionOfferResponse_Private.h in Headers */, + 5047A45D29A20BEB1616EF347C83EF7C /* RLMSyncSession.h in Headers */, + 52F6F1CF4F9A2598D1CB2FEC720F6D32 /* RLMSyncUser.h in Headers */, + 38CBD43BEF88FD721954B97290351C4C /* RLMSyncUtil.h in Headers */, + 14055C8E10BBB0F1566C99E5868E5CEE /* RLMSyncUtil_Private.h in Headers */, + 73B3C48BE5D40F6BD71A94760F93B981 /* RLMThreadSafeReference.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7BEFC1F0470C236F0E159CA24EA8C932 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0302B0B10C1D94D0723A753E8C2CDED9 /* NSData+ImageContentType.h in Headers */, + 92A666CBFB1B735FC2B7C51394276FCF /* NSImage+WebCache.h in Headers */, + F0E5CEA5B7FFB9FB5BFAB4BCB2C0324B /* SDImageCache.h in Headers */, + 9847189348EBD2DA432204217AFC011D /* SDImageCacheConfig.h in Headers */, + 87A1886A1F0228B2CDAF3CF0D7641F82 /* SDWebImageCompat.h in Headers */, + D865A274D47B349C448A39F5653BE8C8 /* SDWebImageDecoder.h in Headers */, + 97A294DADC46EE22447504CA4DDF4881 /* SDWebImageDownloader.h in Headers */, + FBDDD64965449B93D229A68CB84446DD /* SDWebImageDownloaderOperation.h in Headers */, + 2126F00A63A6F444DF17C2E6AE3D576A /* SDWebImageManager.h in Headers */, + 05D725264AD881663D8F5023302E2314 /* SDWebImageOperation.h in Headers */, + 47BD034B18CC661F3888FEA4B697128B /* SDWebImagePrefetcher.h in Headers */, + 2BA28CA92ACA83E385452F0817A458FC /* UIButton+WebCache.h in Headers */, + 5D57F43CF46D37F06ADE45ECDB7FC1FA /* UIImage+GIF.h in Headers */, + 3454A9BDBF49CD926AE7C1E0E2C0204F /* UIImage+MultiFormat.h in Headers */, + 5271AE2F5495F812A8E559B966234E58 /* UIImageView+HighlightedWebCache.h in Headers */, + 91F8EB501EF10C0CCC2DCA9A2B425D25 /* UIImageView+WebCache.h in Headers */, + 668ACCE1ABC6AC4EF6F95EEA911C362D /* UIView+WebCache.h in Headers */, + 2D3AECB19EEDF32993DC49FC539525CE /* UIView+WebCacheOperation.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 12A5BD51A2DF76AE0D835BDA57C208D7 /* SDWebImage */ = { + isa = PBXNativeTarget; + buildConfigurationList = C58B4F0D9532E8983F84715052D39A6A /* Build configuration list for PBXNativeTarget "SDWebImage" */; + buildPhases = ( + 60FA4E6CECFBEC663BDF965A00347A65 /* Sources */, + 2028F5563847A93F8BA6F8FCE4B3DB0A /* Frameworks */, + 7BEFC1F0470C236F0E159CA24EA8C932 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SDWebImage; + productName = SDWebImage; + productReference = 08C19DD1D938007A493F915DCFC19B3D /* libSDWebImage.a */; + productType = "com.apple.product-type.library.static"; + }; + 23BEC5E362EE33056B666F830B295817 /* Pods-Desafio Mobile iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F1620000694C12BBD68CF200FC9D23A /* Build configuration list for PBXNativeTarget "Pods-Desafio Mobile iOSTests" */; + buildPhases = ( + DAD7946E60CDDE148EBD111842CCA772 /* Sources */, + F546234BAFAD057857078831EC1D5441 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 1269A0170165E5E703CE74B99E82F87C /* PBXTargetDependency */, + ); + name = "Pods-Desafio Mobile iOSTests"; + productName = "Pods-Desafio Mobile iOSTests"; + productReference = 601E0080B3DDBE1E440DAC7E43C93146 /* libPods-Desafio Mobile iOSTests.a */; + productType = "com.apple.product-type.library.static"; + }; + 90B4085450060013AFE6FDAABCAB6E8D /* Realm */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4612945467D9C2905CBBFAABF0F1198C /* Build configuration list for PBXNativeTarget "Realm" */; + buildPhases = ( + 77B7375553C1D645C078C311D2F260F5 /* Sources */, + 413F75054B882315EFBCFFD54BFDEF69 /* Frameworks */, + 0FAB02922CF8CE05EEEA734151778ED7 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Realm; + productName = Realm; + productReference = 59F10F8EEB026DAA095E43E202775EBA /* libRealm.a */; + productType = "com.apple.product-type.library.static"; + }; + D5F19FDB746FB374B81A028086958FBF /* Pods-Desafio Mobile iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = AC5F3E7D1C0173C9F67BF3C774ABF72A /* Build configuration list for PBXNativeTarget "Pods-Desafio Mobile iOS" */; + buildPhases = ( + 2CC25D1F8FE49826A4D1874A1B03E7C4 /* Sources */, + 11D2B9968AC98336FF8C3F2D07B0ADAF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3DA98CAC87BCA4B4D18BB9B8EDEA71D6 /* PBXTargetDependency */, + D67AD4072163FB3F1E8C12949B6CD3B3 /* PBXTargetDependency */, + ); + name = "Pods-Desafio Mobile iOS"; + productName = "Pods-Desafio Mobile iOS"; + productReference = 72A8A7B1447872ED0FC8B2E10F1104E0 /* libPods-Desafio Mobile iOS.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = D89CD6A2FFADDBFB4B2ECEF938FCE672 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D5F19FDB746FB374B81A028086958FBF /* Pods-Desafio Mobile iOS */, + 23BEC5E362EE33056B666F830B295817 /* Pods-Desafio Mobile iOSTests */, + 90B4085450060013AFE6FDAABCAB6E8D /* Realm */, + 12A5BD51A2DF76AE0D835BDA57C208D7 /* SDWebImage */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 2CC25D1F8FE49826A4D1874A1B03E7C4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C1AE8225CE0DF160E668EA50AAC5D0E /* Pods-Desafio Mobile iOS-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 60FA4E6CECFBEC663BDF965A00347A65 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEAECB51979251B9AA2369592FFE8EBC /* NSData+ImageContentType.m in Sources */, + 5D497981E9FE2911A8311945A3765EAB /* NSImage+WebCache.m in Sources */, + CE37A7F1D232436E91F2080324768677 /* SDImageCache.m in Sources */, + 8C25FEC1DF7A22F5979F2208A5FA060B /* SDImageCacheConfig.m in Sources */, + 4A44EB1ABEF0EBA4016BAA4748403C32 /* SDWebImage-dummy.m in Sources */, + 0E08CDDDB40DE4C8734CEF05CB7EC12C /* SDWebImageCompat.m in Sources */, + 607303BD04E5BA1CE1E7422ECF0863A4 /* SDWebImageDecoder.m in Sources */, + 0E315A746F1922672D461A801606635A /* SDWebImageDownloader.m in Sources */, + D6DD56DAD60BA0D429205BBF37E108F4 /* SDWebImageDownloaderOperation.m in Sources */, + 123F92872D33F759883536D551938E39 /* SDWebImageManager.m in Sources */, + FF1850B9A6EEB1F984D3A32B21779E08 /* SDWebImagePrefetcher.m in Sources */, + 70C5340C3F4588C0A3B858A4F8E1E6EB /* UIButton+WebCache.m in Sources */, + F297A6B55B8F509A20736A9E0ABA0DCB /* UIImage+GIF.m in Sources */, + 04502E56918E180A0C90FEC834A77D6B /* UIImage+MultiFormat.m in Sources */, + 55F060170CF1AFB690CA72E46DDE6986 /* UIImageView+HighlightedWebCache.m in Sources */, + 67FAF86CF8315CBF3513071E1CF24B56 /* UIImageView+WebCache.m in Sources */, + DE96CDBF0683DFD4B501789487F531BB /* UIView+WebCache.m in Sources */, + DBE5C6EE205B677670810D0BB9963C6E /* UIView+WebCacheOperation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 77B7375553C1D645C078C311D2F260F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EED8AD252264FB9D383AA079A8DC097 /* binding_callback_thread_observer.cpp in Sources */, + 7E4000A5C45E4FEF641F8DF9C45285F9 /* collection_change_builder.cpp in Sources */, + FA96BE4B80FBF12B7DB8CF9F33C44E76 /* collection_notifications.cpp in Sources */, + 7A746CD18A4984BD97FD5576D24F3F62 /* collection_notifier.cpp in Sources */, + 451DBB2303E9A71FE5BE52242C377CCF /* external_commit_helper.cpp in Sources */, + 824BD920E778C35AC0F9FEA950047218 /* format.cpp in Sources */, + E60C4553740D5C742B835B6573FFCDDF /* index_set.cpp in Sources */, + 9103580F3D619DD52A205E48EDC70181 /* keychain_helper.cpp in Sources */, + 8CEC40420F25D5359617454BCE715AD0 /* list.cpp in Sources */, + 22BA1C0E39F3819C99154728FDD6D44F /* list_notifier.cpp in Sources */, + 01B247B801AD86E23CEF6DDD7D5F62A4 /* NSError+RLMSync.m in Sources */, + 7DE5802D3F6BC60BA6CDB034B61DF00D /* object.cpp in Sources */, + C59685218F74891CDC0287BAB1F54485 /* object_notifier.cpp in Sources */, + 3FCE77974B91404AF773195E788F0ADA /* object_schema.cpp in Sources */, + 9932ECB76BC57F19C5AB0120FB8C5885 /* object_store.cpp in Sources */, + D30D56DA4941651DAE66FFE7C81E4722 /* placeholder.cpp in Sources */, + 3876930B4B10104CC5E49D7BA5379E73 /* Realm-dummy.m in Sources */, + 05B9E2885BA1F649F9D12696FCD9008F /* realm_coordinator.cpp in Sources */, + 21407B4F4B81C8474F8CA91CD286C4CF /* results.cpp in Sources */, + B0F58B07F4C2F6934002FD51734545A2 /* results_notifier.cpp in Sources */, + EBFD7517DDF368169A26C2560448C483 /* RLMAccessor.mm in Sources */, + 4027AC2535BB46AC608C030395DE18C1 /* RLMAnalytics.mm in Sources */, + 48C0BCA3AF1F8B701FBAF4F645CA8BCB /* RLMArray.mm in Sources */, + F316BEC004F816982C72E98700127F97 /* RLMArrayLinkView.mm in Sources */, + 30EAFD1C31227451869B61CA1610F126 /* RLMAuthResponseModel.m in Sources */, + A4A078CBFA04163C2363B131C7AFFCC9 /* RLMClassInfo.mm in Sources */, + 1525952FF84DB0E91C857CB3BB8AA110 /* RLMCollection.mm in Sources */, + ACCBF0F7BD6A744ABF1CC653878FF4F2 /* RLMConstants.m in Sources */, + 4D121ABC760675DF76A8505A8EDA699E /* RLMListBase.mm in Sources */, + D69B186E2DD0A903BA663F469EE8AAB3 /* RLMMigration.mm in Sources */, + C9F17C5AF12EDBB0C7973F8492A4C8FC /* RLMNetworkClient.m in Sources */, + 63A3B299A9D660BF9D76E3DD8718FC1C /* RLMObject.mm in Sources */, + 1F63CDDBC6D8EFC7007D047BF896D808 /* RLMObjectBase.mm in Sources */, + 761E6D64223D6F0218BF475E9D96E7EE /* RLMObjectSchema.mm in Sources */, + D8B996BE11845CC4A6FB517DFACC150D /* RLMObjectStore.mm in Sources */, + 836058F9E0E48099F6F06652F664EF6D /* RLMObservation.mm in Sources */, + 12B5E673540601529F136DC0C3E4D0F8 /* RLMOptionalBase.mm in Sources */, + 38B54D7B7B32B848D944FCA70B619FE8 /* RLMPredicateUtil.mm in Sources */, + 377EF193077067603BD669750005DC53 /* RLMProperty.mm in Sources */, + 07B51B4929416EE72BF877C391D5A7CE /* RLMQueryUtil.mm in Sources */, + CD71ACF717F5AC79F7148DE85421FDD4 /* RLMRealm.mm in Sources */, + 8DA5BCC3E7ABBB3CF8596D813E8C1870 /* RLMRealmConfiguration+Sync.mm in Sources */, + 564B1837D496ADE5E44E993DC4033885 /* RLMRealmConfiguration.mm in Sources */, + AAF8C337FADDDC88E00D14C90BD9D319 /* RLMRealmUtil.mm in Sources */, + 6BC803C0AFFCDB0676C85315C029F59B /* RLMResults.mm in Sources */, + 903798B2C621828732445974BD55B291 /* RLMSchema.mm in Sources */, + 8C48685FC55534F7AEE7797EF6158904 /* RLMSwiftSupport.m in Sources */, + 60AA0F3B68D6CFB8F492BDD9579BEAD8 /* RLMSyncConfiguration.mm in Sources */, + 9D69C3BDE676C6125BFFAD2BA1F37715 /* RLMSyncCredentials.m in Sources */, + 5621A5BA6607FFF193BCDCC00D36664F /* RLMSyncErrorResponseModel.m in Sources */, + C2F9B2FCA3348720C35B41C66A0367F6 /* RLMSyncManager.mm in Sources */, + 37241E326F71425038DFCEC6D28F7785 /* RLMSyncPermission.m in Sources */, + DA0E48C6263EA6D6BAC8879E540C69BE /* RLMSyncPermissionChange.m in Sources */, + 476FF98879CF14D6444E13572DF08672 /* RLMSyncPermissionOffer.m in Sources */, + E50EFBA088BAC04108C4D132D9BDE72A /* RLMSyncPermissionOfferResponse.m in Sources */, + 8225CE5B2B0C86FC3A88A7D9873B732E /* RLMSyncSession.mm in Sources */, + 38C3A0AA4F5F4EAFDD1F14DAB7A39363 /* RLMSyncSessionRefreshHandle.mm in Sources */, + D1872A5B435BA6FCAF59AC01A37A6B1F /* RLMSyncUser.mm in Sources */, + 59E15B410C9C0DE2CBB42DA6CC9A68EE /* RLMSyncUtil.mm in Sources */, + 0991371C5A6A50D7E37BDE6254B7F8A8 /* RLMThreadSafeReference.mm in Sources */, + 4B10C33DD31589C209CCF742F569740F /* RLMTokenModels.m in Sources */, + 35A93FF4049467DB0C6C82A7F1DF71D3 /* RLMUpdateChecker.mm in Sources */, + F7CD3EC1C2FCA9D700BAB868A9D53CC7 /* RLMUtil.mm in Sources */, + DCF564B01D6FFA5AEBD02795B2E37063 /* schema.cpp in Sources */, + B8B4453A3824E8860AB81F2E6F1CC39E /* shared_realm.cpp in Sources */, + 8A65374F77C2BC6983B3AE9B486F8482 /* sync_file.cpp in Sources */, + 804D5270D45E5C6C640DC6DE3AB2E430 /* sync_manager.cpp in Sources */, + 2C4ECA96AB2DB4AA667E16DD632D862D /* sync_metadata.cpp in Sources */, + 2276E697ACE8E64A466834C49837C8D5 /* sync_permission.cpp in Sources */, + 6F39CAABD1C74CB8151ECE7EAB1325EE /* sync_session.cpp in Sources */, + 30A5AF360D18CA0274DBC6477A4A1DE9 /* sync_user.cpp in Sources */, + 40CF53EE017EEC9979CCCEC3F4DCC175 /* thread_safe_reference.cpp in Sources */, + 56E4D1DCE57F8333D5CC41DDCD5E1719 /* transact_log_handler.cpp in Sources */, + FB6D89931F604F6333666C67EC557532 /* uuid.cpp in Sources */, + E8FAF58FA7BEBD867EC15C91EB4BDB62 /* weak_realm_notifier.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAD7946E60CDDE148EBD111842CCA772 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8A641BE8547B61F34D74872C8AD4C04E /* Pods-Desafio Mobile iOSTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1269A0170165E5E703CE74B99E82F87C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Realm; + target = 90B4085450060013AFE6FDAABCAB6E8D /* Realm */; + targetProxy = 0DF9AD3353A6AAE64204A85D3F2B1720 /* PBXContainerItemProxy */; + }; + 3DA98CAC87BCA4B4D18BB9B8EDEA71D6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Realm; + target = 90B4085450060013AFE6FDAABCAB6E8D /* Realm */; + targetProxy = 0A08D202103213613CAFD366968B424C /* PBXContainerItemProxy */; + }; + D67AD4072163FB3F1E8C12949B6CD3B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SDWebImage; + target = 12A5BD51A2DF76AE0D835BDA57C208D7 /* SDWebImage */; + targetProxy = 546C147B3C250A4B755FED678CB89394 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1F79667317A16B3D98BB7865A81F2C14 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 47D248AA38929821C24CAC934A468BE2 /* Pods-Desafio Mobile iOSTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2ABFA3C61AE930E2D4DA3933B5DAE6B8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DF77562C65F58F802D3A08691A01E39E /* Realm.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2EE6225547AB2121A3706128B26416CC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 10505976D22548D10C51FDD5819BA4A6 /* Pods-Desafio Mobile iOSTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3EF69C4395A75B79B1F4D93F152CC7DB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B6AE1CE6A6B5823DBE1069B163226021 /* SDWebImage.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/SDWebImage/SDWebImage-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8308FC04B3D43EF0273BD365648DF1D5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 479E305AC8E86C904474E5B765368534 /* Pods-Desafio Mobile iOS.release.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B254DAA6CF0CE39F4A3D11B90A7E059A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + DF43BD4B98DA57E7F418F26649ABB6BE /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 95E5FC8D54560B994FF2EE689169A98C /* Pods-Desafio Mobile iOS.debug.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E1A873D8571BF5F6FC3971CA1A3D27FC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DF77562C65F58F802D3A08691A01E39E /* Realm.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E4B68EE12B21C47CB798D9B1ECA6D7A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + FB7E9BED9DA52F4FB5884F87144AF451 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B6AE1CE6A6B5823DBE1069B163226021 /* SDWebImage.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/SDWebImage/SDWebImage-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0F1620000694C12BBD68CF200FC9D23A /* Build configuration list for PBXNativeTarget "Pods-Desafio Mobile iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1F79667317A16B3D98BB7865A81F2C14 /* Debug */, + 2EE6225547AB2121A3706128B26416CC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E4B68EE12B21C47CB798D9B1ECA6D7A7 /* Debug */, + B254DAA6CF0CE39F4A3D11B90A7E059A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4612945467D9C2905CBBFAABF0F1198C /* Build configuration list for PBXNativeTarget "Realm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E1A873D8571BF5F6FC3971CA1A3D27FC /* Debug */, + 2ABFA3C61AE930E2D4DA3933B5DAE6B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AC5F3E7D1C0173C9F67BF3C774ABF72A /* Build configuration list for PBXNativeTarget "Pods-Desafio Mobile iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DF43BD4B98DA57E7F418F26649ABB6BE /* Debug */, + 8308FC04B3D43EF0273BD365648DF1D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C58B4F0D9532E8983F84715052D39A6A /* Build configuration list for PBXNativeTarget "SDWebImage" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FB7E9BED9DA52F4FB5884F87144AF451 /* Debug */, + 3EF69C4395A75B79B1F4D93F152CC7DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOS.xcscheme b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOS.xcscheme new file mode 100644 index 0000000..e20ad39 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOS.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOSTests.xcscheme b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOSTests.xcscheme new file mode 100644 index 0000000..d2c8f31 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Pods-Desafio Mobile iOSTests.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Realm.xcscheme b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Realm.xcscheme new file mode 100644 index 0000000..e7f1244 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/Realm.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/SDWebImage.xcscheme b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/SDWebImage.xcscheme new file mode 100644 index 0000000..fae08ae --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/SDWebImage.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..f6812f8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Pods.xcodeproj/xcuserdata/adriano.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,31 @@ + + + + + SchemeUserState + + Pods-Desafio Mobile iOS.xcscheme + + isShown + + + Pods-Desafio Mobile iOSTests.xcscheme + + isShown + + + Realm.xcscheme + + isShown + + + SDWebImage.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + + diff --git a/Desafio Mobile iOS/Pods/Realm/LICENSE b/Desafio Mobile iOS/Pods/Realm/LICENSE new file mode 100644 index 0000000..8a84f2b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/LICENSE @@ -0,0 +1,244 @@ +TABLE OF CONTENTS + +1. Apache License version 2.0 +2. Realm Components +3. Export Compliance + +------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +REALM COMPONENTS + +This software contains components with separate copyright and license terms. +Your use of these components is subject to the terms and conditions of the +following licenses. + +For the Realm Core component + + Realm Core Binary License + + Copyright (c) 2011-2016 Realm Inc All rights reserved + + Redistribution and use in binary form, with or without modification, is + permitted provided that the following conditions are met: + + 1. You agree not to attempt to decompile, disassemble, reverse engineer or + otherwise discover the source code from which the binary code was derived. + You may, however, access and obtain a separate license for most of the + source code from which this Software was created, at + http://realm.io/pricing/. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +EXPORT COMPLIANCE + +You understand that the Software may contain cryptographic functions that may be +subject to export restrictions, and you represent and warrant that you are not +(i) located in a jurisdiction that is subject to United States economic +sanctions (“Prohibited Jurisdiction”), including Cuba, Iran, North Korea, +Sudan, Syria or the Crimea region, (ii) a person listed on any U.S. government +blacklist (to include the List of Specially Designated Nationals and Blocked +Persons or the Consolidated Sanctions List administered by the U.S. Department +of the Treasury’s Office of Foreign Assets Control, or the Denied Persons List +or Entity List administered by the U.S. Department of Commerce) +(“Sanctioned Person”), or (iii) controlled or 50% or more owned by a Sanctioned +Person. + +You agree to comply with all export, re-export and import restrictions and +regulations of the U.S. Department of Commerce or other agency or authority of +the United States or other applicable countries. You also agree not to transfer, +or authorize the transfer of, directly or indirectly, of the Software to any +Prohibited Jurisdiction, or otherwise in violation of any such restrictions or +regulations. diff --git a/Desafio Mobile iOS/Pods/Realm/README.md b/Desafio Mobile iOS/Pods/Realm/README.md new file mode 100644 index 0000000..0a95cb9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/README.md @@ -0,0 +1,74 @@ +![Realm](https://github.com/realm/realm-cocoa/raw/master/logo.png) + +Realm is a mobile database that runs directly inside phones, tablets or wearables. +This repository holds the source code for the iOS, macOS, tvOS & watchOS versions of Realm Swift & Realm Objective-C. + +## Features + +* **Mobile-first:** Realm is the first database built from the ground up to run directly inside phones, tablets and wearables. +* **Simple:** Data is directly [exposed as objects](https://realm.io/docs/objc/latest/#models) and [queryable by code](https://realm.io/docs/objc/latest/#queries), removing the need for ORM's riddled with performance & maintenance issues. Most of our users pick it up intuitively, getting simple apps up & running in minutes. +* **Modern:** Realm supports relationships, generics, vectorization and even Swift. +* **Fast:** Realm is faster than even raw SQLite on common operations, while maintaining an extremely rich feature set. + +## Getting Started + +Please see the detailed instructions in our docs to add [Realm Objective-C](https://realm.io/docs/objc/latest/#installation) _or_ [Realm Swift](https://realm.io/docs/swift/latest/#installation) to your Xcode project. + +## Documentation + +### Realm Objective-C + +The documentation can be found at [realm.io/docs/objc/latest](https://realm.io/docs/objc/latest). +The API reference is located at [realm.io/docs/objc/latest/api](https://realm.io/docs/objc/latest/api). + +### Realm Swift + +The documentation can be found at [realm.io/docs/swift/latest](https://realm.io/docs/swift/latest). +The API reference is located at [realm.io/docs/swift/latest/api](https://realm.io/docs/swift/latest/api). + +## Getting Help + +- **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We actively monitor & answer questions on SO! +- **Have a bug to report?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue. +- **Have a feature request?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). Tell us what the feature should do, and why you want the feature. +- Sign up for our [**Community Newsletter**](https://www2.realm.io/l/210132/2016-12-05/fy9m) to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm. + +## Building Realm + +In case you don't want to use the precompiled version, you can build Realm yourself from source. + +Prerequisites: + +* Building Realm requires Xcode 8.x. +* If cloning from git, submodules are required: `git submodule update --init --recursive`. +* Building Realm documentation requires [jazzy](https://github.com/realm/jazzy) + +Once you have all the necessary prerequisites, building Realm.framework just takes a single command: `sh build.sh build`. You'll need an internet connection the first time you build Realm to download the core binary. + +Run `sh build.sh help` to see all the actions you can perform (build ios/osx, generate docs, test, etc.). + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for more details! + +This project adheres to the [Contributor Covenant Code of Conduct](https://realm.io/conduct). +By participating, you are expected to uphold this code. Please report +unacceptable behavior to [info@realm.io](mailto:info@realm.io). + +## License + +Realm Objective-C & Realm Swift are published under the Apache 2.0 license. +Realm Core is also published under the Apache 2.0 license and is available +[here](https://github.com/realm/realm-core). + +**This product is not being made available to any person located in Cuba, Iran, +North Korea, Sudan, Syria or the Crimea region, or to any other person that is +not eligible to receive the product under U.S. law.** + +## Feedback + +**_If you use Realm and are happy with it, all we ask is that you please consider sending out a tweet mentioning [@realm](https://twitter.com/realm) or email [help@realm.io](mailto:help@realm.io) to share your thoughts!_** + +**_And if you don't like it, please let us know what you would like improved, so we can fix it!_** + +![analytics](https://ga-beacon.appspot.com/UA-50247013-2/realm-cocoa/README?pixel) diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/NSError+RLMSync.m b/Desafio Mobile iOS/Pods/Realm/Realm/NSError+RLMSync.m new file mode 100644 index 0000000..20938a7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/NSError+RLMSync.m @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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 "NSError+RLMSync.h" + +#import "RLMSyncUtil.h" + +@implementation NSError (RLMSync) + +- (void(^)(void))rlmSync_clientResetBlock { + if (self.domain == RLMSyncErrorDomain && self.code == RLMSyncErrorClientResetError) { + return self.userInfo[kRLMSyncInitiateClientResetBlockKey]; + } + return nil; +} + +- (NSString *)rlmSync_clientResetBackedUpRealmPath { + if (self.domain == RLMSyncErrorDomain && self.code == RLMSyncErrorClientResetError) { + return self.userInfo[kRLMSyncPathOfRealmBackupCopyKey]; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp new file mode 100644 index 0000000..0c388d0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "binding_callback_thread_observer.hpp" + +namespace realm { +BindingCallbackThreadObserver* g_binding_callback_thread_observer = nullptr; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp new file mode 100644 index 0000000..165021d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "collection_notifications.hpp" + +#include "impl/collection_notifier.hpp" + +using namespace realm; +using namespace realm::_impl; + +NotificationToken::NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token) +: m_notifier(std::move(notifier)), m_token(token) +{ +} + +NotificationToken::~NotificationToken() +{ + // m_notifier itself (and not just the pointed-to thing) needs to be accessed + // atomically to ensure that there are no data races when the token is + // destroyed after being modified on a different thread. + // This is needed despite the token not being thread-safe in general as + // users find it very surprising for obj-c objects to care about what + // thread they are deallocated on. + if (auto notifier = m_notifier.exchange({})) { + notifier->remove_callback(m_token); + } +} + +NotificationToken::NotificationToken(NotificationToken&&) = default; + +NotificationToken& NotificationToken::operator=(realm::NotificationToken&& rgt) +{ + if (this != &rgt) { + if (auto notifier = m_notifier.exchange({})) { + notifier->remove_callback(m_token); + } + m_notifier = std::move(rgt.m_notifier); + m_token = rgt.m_token; + } + return *this; +} + +void NotificationToken::suppress_next() +{ + m_notifier.load()->suppress_next_notification(m_token); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp new file mode 100644 index 0000000..60f7a66 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/external_commit_helper.hpp" + +#include "impl/realm_coordinator.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace realm; +using namespace realm::_impl; + +namespace { +// Write a byte to a pipe to notify anyone waiting for data on the pipe +void notify_fd(int fd, int read_fd) +{ + while (true) { + char c = 0; + ssize_t ret = write(fd, &c, 1); + if (ret == 1) { + break; + } + + // If the pipe's buffer is full, we need to read some of the old data in + // it to make space. We don't just read in the code waiting for + // notifications so that we can notify multiple waiters with a single + // write. + assert(ret == -1 && errno == EAGAIN); + char buff[1024]; + read(read_fd, buff, sizeof buff); + } +} +} // anonymous namespace + +void ExternalCommitHelper::FdHolder::close() +{ + if (m_fd != -1) { + ::close(m_fd); + } + m_fd = -1; +} + +// Inter-thread and inter-process notifications of changes are done using a +// named pipe in the filesystem next to the Realm file. Everyone who wants to be +// notified of commits waits for data to become available on the pipe, and anyone +// who commits a write transaction writes data to the pipe after releasing the +// write lock. Note that no one ever actually *reads* from the pipe: the data +// actually written is meaningless, and trying to read from a pipe from multiple +// processes at once is fraught with race conditions. + +// When a RLMRealm instance is created, we add a CFRunLoopSource to the current +// thread's runloop. On each cycle of the run loop, the run loop checks each of +// its sources for work to do, which in the case of CFRunLoopSource is just +// checking if CFRunLoopSourceSignal has been called since the last time it ran, +// and if so invokes the function pointer supplied when the source is created, +// which in our case just invokes `[realm handleExternalChange]`. + +// Listening for external changes is done using kqueue() on a background thread. +// kqueue() lets us efficiently wait until the amount of data which can be read +// from one or more file descriptors has changed, and tells us which of the file +// descriptors it was that changed. We use this to wait on both the shared named +// pipe, and a local anonymous pipe. When data is written to the named pipe, we +// signal the runloop source and wake up the target runloop, and when data is +// written to the anonymous pipe the background thread removes the runloop +// source from the runloop and and shuts down. +ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent) +: m_parent(parent) +{ + m_kq = kqueue(); + if (m_kq == -1) { + throw std::system_error(errno, std::system_category()); + } + +#if !TARGET_OS_TV + auto path = parent.get_path() + ".note"; + + // Create and open the named pipe + int ret = mkfifo(path.c_str(), 0600); + if (ret == -1) { + int err = errno; + if (err == ENOTSUP) { + // Filesystem doesn't support named pipes, so try putting it in tmp instead + // Hash collisions are okay here because they just result in doing + // extra work, as opposed to correctness problems + std::ostringstream ss; + ss << getenv("TMPDIR"); + ss << "realm_" << std::hash()(path) << ".note"; + path = ss.str(); + ret = mkfifo(path.c_str(), 0600); + err = errno; + } + // the fifo already existing isn't an error + if (ret == -1 && err != EEXIST) { + throw std::system_error(err, std::system_category()); + } + } + + m_notify_fd = open(path.c_str(), O_RDWR); + if (m_notify_fd == -1) { + throw std::system_error(errno, std::system_category()); + } + + // Make writing to the pipe return -1 when the pipe's buffer is full + // rather than blocking until there's space available + ret = fcntl(m_notify_fd, F_SETFL, O_NONBLOCK); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + +#else // !TARGET_OS_TV + + // tvOS does not support named pipes, so use an anonymous pipe instead + int notification_pipe[2]; + int ret = pipe(notification_pipe); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + + m_notify_fd = notification_pipe[0]; + m_notify_fd_write = notification_pipe[1]; + +#endif // TARGET_OS_TV + + // Create the anonymous pipe for shutdown notifications + int shutdown_pipe[2]; + ret = pipe(shutdown_pipe); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + + m_shutdown_read_fd = shutdown_pipe[0]; + m_shutdown_write_fd = shutdown_pipe[1]; + + m_thread = std::async(std::launch::async, [=] { + try { + listen(); + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + catch (std::exception const& e) { + fprintf(stderr, "uncaught exception in notifier thread: %s: %s\n", typeid(e).name(), e.what()); + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread: %s: %s", typeid(e).name(), e.what()); + throw; + } + catch (...) { + fprintf(stderr, "uncaught exception in notifier thread\n"); + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread"); + throw; + } +#pragma clang diagnostic pop + }); +} + +ExternalCommitHelper::~ExternalCommitHelper() +{ + notify_fd(m_shutdown_write_fd, m_shutdown_read_fd); + m_thread.wait(); // Wait for the thread to exit +} + +void ExternalCommitHelper::listen() +{ + pthread_setname_np("RLMRealm notification listener"); + + // Set up the kqueue + // EVFILT_READ indicates that we care about data being available to read + // on the given file descriptor. + // EV_CLEAR makes it wait for the amount of data available to be read to + // change rather than just returning when there is any data to read. + struct kevent ke[2]; + EV_SET(&ke[0], m_notify_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); + EV_SET(&ke[1], m_shutdown_read_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); + int ret = kevent(m_kq, ke, 2, nullptr, 0, nullptr); + assert(ret == 0); + + while (true) { + struct kevent event; + // Wait for data to become on either fd + // Return code is number of bytes available or -1 on error + ret = kevent(m_kq, nullptr, 0, &event, 1, nullptr); + assert(ret >= 0); + if (ret == 0) { + // Spurious wakeup; just wait again + continue; + } + + // Check which file descriptor had activity: if it's the shutdown + // pipe, then someone called -stop; otherwise it's the named pipe + // and someone committed a write transaction + if (event.ident == (uint32_t)m_shutdown_read_fd) { + return; + } + assert(event.ident == (uint32_t)m_notify_fd); + + m_parent.on_change(); + } +} + +void ExternalCommitHelper::notify_others() +{ + if (m_notify_fd_write != -1) { + notify_fd(m_notify_fd_write, m_notify_fd); + } + else { + notify_fd(m_notify_fd, m_notify_fd); + } +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp new file mode 100644 index 0000000..b12128d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp @@ -0,0 +1,115 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/apple/keychain_helper.hpp" + +#include "util/format.hpp" + +#include +#include + +#include + +#include + +using realm::util::CFPtr; +using realm::util::adoptCF; + +namespace realm { +namespace keychain { + +KeychainAccessException::KeychainAccessException(int32_t error_code) +: std::runtime_error(util::format("Keychain returned unexpected status code: %1", error_code)) { } + +CFPtr convert_string(const std::string& string); +CFPtr build_search_dictionary(const std::string& account, const std::string& service, + util::Optional group); + +CFPtr convert_string(const std::string& string) +{ + auto result = adoptCF(CFStringCreateWithBytes(nullptr, reinterpret_cast(string.data()), + string.size(), kCFStringEncodingASCII, false)); + if (!result) { + throw std::bad_alloc(); + } + return result; +} + +CFPtr build_search_dictionary(const std::string& account, const std::string& service, + __unused util::Optional group) +{ + auto d = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + if (!d) { + throw std::bad_alloc(); + } + CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword); + CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue); + CFDictionaryAddValue(d.get(), kSecAttrAccessible, kSecAttrAccessibleAlways); + CFDictionaryAddValue(d.get(), kSecAttrAccount, convert_string(account).get()); + CFDictionaryAddValue(d.get(), kSecAttrService, convert_string(service).get()); +#if TARGET_IPHONE_SIMULATOR +#else + if (group) { + CFDictionaryAddValue(d.get(), kSecAttrAccessGroup, convert_string(*group).get()); + } +#endif + return d; +} + +std::vector metadata_realm_encryption_key() +{ + const size_t key_size = 64; + const std::string service = "io.realm.sync.keychain"; + const std::string account = "metadata"; + + auto search_dictionary = build_search_dictionary(account, service, none); + CFDataRef retained_key_data; + if (OSStatus status = SecItemCopyMatching(search_dictionary.get(), (CFTypeRef *)&retained_key_data)) { + if (status != errSecItemNotFound) { + throw KeychainAccessException(status); + } + + // Key was not found. Generate a new key, store it, and return it. + std::vector key(key_size); + arc4random_buf(key.data(), key_size); + auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast(key.data()), key_size)); + if (!key_data) { + throw std::bad_alloc(); + } + + CFDictionaryAddValue(search_dictionary.get(), kSecValueData, key_data.get()); + if (OSStatus status = SecItemAdd(search_dictionary.get(), nullptr)) { + throw KeychainAccessException(status); + } + + return key; + } + CFPtr key_data = adoptCF(retained_key_data); + + // Key was previously stored. Extract it. + if (key_size != CFDataGetLength(key_data.get())) { + throw std::runtime_error("Password stored in keychain was not expected size."); + } + + auto key_bytes = reinterpret_cast(CFDataGetBytePtr(key_data.get())); + return std::vector(key_bytes, key_bytes + key_size); +} + +} +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp new file mode 100644 index 0000000..7d78e8d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp @@ -0,0 +1,885 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/collection_change_builder.hpp" + +#include +#include + +#include + +using namespace realm; +using namespace realm::_impl; + +CollectionChangeBuilder::CollectionChangeBuilder(IndexSet deletions, + IndexSet insertions, + IndexSet modifications, + std::vector moves) +: CollectionChangeSet({std::move(deletions), std::move(insertions), std::move(modifications), {}, std::move(moves)}) +{ + for (auto&& move : this->moves) { + this->deletions.add(move.from); + this->insertions.add(move.to); + } +} + +void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c) +{ + if (c.empty()) + return; + if (empty()) { + *this = std::move(c); + return; + } + + verify(); + c.verify(); + + auto for_each_col = [&](auto&& f) { + f(modifications, c.modifications); + if (m_track_columns) { + if (columns.size() < c.columns.size()) + columns.resize(c.columns.size()); + else if (columns.size() > c.columns.size()) + c.columns.resize(columns.size()); + for (size_t i = 0; i < columns.size(); ++i) + f(columns[i], c.columns[i]); + } + }; + + // First update any old moves + if (!c.moves.empty() || !c.deletions.empty() || !c.insertions.empty()) { + auto it = std::remove_if(begin(moves), end(moves), [&](auto& old) { + // Check if the moved row was moved again, and if so just update the destination + auto it = find_if(begin(c.moves), end(c.moves), [&](auto const& m) { + return old.to == m.from; + }); + if (it != c.moves.end()) { + for_each_col([&](auto& col, auto& other) { + if (col.contains(it->from)) + other.add(it->to); + }); + old.to = it->to; + *it = c.moves.back(); + c.moves.pop_back(); + return false; + } + + // Check if the destination was deleted + // Removing the insert for this move will happen later + if (c.deletions.contains(old.to)) + return true; + + // Update the destination to adjust for any new insertions and deletions + old.to = c.insertions.shift(c.deletions.unshift(old.to)); + return false; + }); + moves.erase(it, end(moves)); + } + + // Ignore new moves of rows which were previously inserted (the implicit + // delete from the move will remove the insert) + if (!insertions.empty() && !c.moves.empty()) { + c.moves.erase(std::remove_if(begin(c.moves), end(c.moves), + [&](auto const& m) { return insertions.contains(m.from); }), + end(c.moves)); + } + + // Ensure that any previously modified rows which were moved are still modified + if (!modifications.empty() && !c.moves.empty()) { + for (auto const& move : c.moves) { + for_each_col([&](auto& col, auto& other) { + if (col.contains(move.from)) + other.add(move.to); + }); + } + } + + // Update the source position of new moves to compensate for the changes made + // in the old changeset + if (!deletions.empty() || !insertions.empty()) { + for (auto& move : c.moves) + move.from = deletions.shift(insertions.unshift(move.from)); + } + + moves.insert(end(moves), begin(c.moves), end(c.moves)); + + // New deletion indices have been shifted by the insertions, so unshift them + // before adding + deletions.add_shifted_by(insertions, c.deletions); + + // Drop any inserted-then-deleted rows, then merge in new insertions + insertions.erase_at(c.deletions); + insertions.insert_at(c.insertions); + + clean_up_stale_moves(); + + for_each_col([&](auto& col, auto& other) { + col.erase_at(c.deletions); + col.shift_for_insert_at(c.insertions); + col.add(other); + }); + + c = {}; + verify(); +} + +void CollectionChangeBuilder::clean_up_stale_moves() +{ + // Look for moves which are now no-ops, and remove them plus the associated + // insert+delete. Note that this isn't just checking for from == to due to + // that rows can also be shifted by other inserts and deletes + moves.erase(std::remove_if(begin(moves), end(moves), [&](auto const& move) { + if (move.from - deletions.count(0, move.from) != move.to - insertions.count(0, move.to)) + return false; + deletions.remove(move.from); + insertions.remove(move.to); + return true; + }), end(moves)); +} + +void CollectionChangeBuilder::parse_complete() +{ + moves.reserve(m_move_mapping.size()); + for (auto move : m_move_mapping) { + REALM_ASSERT_DEBUG(deletions.contains(move.second)); + REALM_ASSERT_DEBUG(insertions.contains(move.first)); + if (move.first == move.second) { + deletions.remove(move.second); + insertions.remove(move.first); + } + else + moves.push_back({move.second, move.first}); + } + m_move_mapping.clear(); + std::sort(begin(moves), end(moves), + [](auto const& a, auto const& b) { return a.from < b.from; }); +} + +void CollectionChangeBuilder::modify(size_t ndx, size_t col) +{ + modifications.add(ndx); + if (!m_track_columns || col == IndexSet::npos) + return; + + if (col >= columns.size()) + columns.resize(col + 1); + columns[col].add(ndx); +} + +template +void CollectionChangeBuilder::for_each_col(Func&& f) +{ + f(modifications); + if (m_track_columns) { + for (auto& col : columns) + f(col); + } +} + +void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_moves) +{ + for_each_col([=](auto& col) { col.shift_for_insert_at(index, count); }); + if (!track_moves) + return; + + insertions.insert_at(index, count); + + for (auto& move : moves) { + if (move.to >= index) + ++move.to; + } + + if (m_move_mapping.empty()) + return; + + // m_move_mapping is new_ndx -> old_ndx, so updating the keys requires + // deleting and re-inserting at the new index + std::vector> shifted; + for (auto it = m_move_mapping.begin(); it != m_move_mapping.end(); ) { + if (it->first >= index) { + shifted.emplace_back(it->first + count, it->second); + it = m_move_mapping.erase(it); + } + else { + ++it; + } + } + for (auto& pair : shifted) + m_move_mapping.insert(pair); +} + +void CollectionChangeBuilder::erase(size_t index) +{ + for_each_col([=](auto& col) { col.erase_at(index); }); + size_t unshifted = insertions.erase_or_unshift(index); + if (unshifted != IndexSet::npos) + deletions.add_shifted(unshifted); + + for (size_t i = 0; i < moves.size(); ++i) { + auto& move = moves[i]; + if (move.to == index) { + moves.erase(moves.begin() + i); + --i; + } + else if (move.to > index) + --move.to; + } +} + +void CollectionChangeBuilder::clear(size_t old_size) +{ + if (old_size != std::numeric_limits::max()) { + for (auto range : deletions) + old_size += range.second - range.first; + for (auto range : insertions) + old_size -= range.second - range.first; + } + + modifications.clear(); + insertions.clear(); + moves.clear(); + m_move_mapping.clear(); + columns.clear(); + deletions.set(old_size); +} + +void CollectionChangeBuilder::move(size_t from, size_t to) +{ + REALM_ASSERT(from != to); + + bool updated_existing_move = false; + for (auto& move : moves) { + if (move.to != from) { + // Shift other moves if this row is moving from one side of them + // to the other + if (move.to >= to && move.to < from) + ++move.to; + else if (move.to <= to && move.to > from) + --move.to; + continue; + } + REALM_ASSERT(!updated_existing_move); + + // Collapse A -> B, B -> C into a single A -> C move + move.to = to; + updated_existing_move = true; + + insertions.erase_at(from); + insertions.insert_at(to); + } + + if (!updated_existing_move) { + auto shifted_from = insertions.erase_or_unshift(from); + insertions.insert_at(to); + + // Don't report deletions/moves for newly inserted rows + if (shifted_from != IndexSet::npos) { + shifted_from = deletions.add_shifted(shifted_from); + moves.push_back({shifted_from, to}); + } + } + + for_each_col([=](auto& col) { + bool modified = col.contains(from); + col.erase_at(from); + + if (modified) + col.insert_at(to); + else + col.shift_for_insert_at(to); + }); +} + +void CollectionChangeBuilder::move_over(size_t row_ndx, size_t last_row, bool track_moves) +{ + REALM_ASSERT(row_ndx <= last_row); + REALM_ASSERT(insertions.empty() || prev(insertions.end())->second - 1 <= last_row); + REALM_ASSERT(modifications.empty() || prev(modifications.end())->second - 1 <= last_row); + + if (row_ndx == last_row) { + if (track_moves) { + auto shifted_from = insertions.erase_or_unshift(row_ndx); + if (shifted_from != IndexSet::npos) + deletions.add_shifted(shifted_from); + m_move_mapping.erase(row_ndx); + } + for_each_col([=](auto& col) { col.remove(row_ndx); }); + return; + } + + for_each_col([=](auto& col) { + bool modified = col.contains(last_row); + if (modified) { + col.remove(last_row); + col.add(row_ndx); + } + else + col.remove(row_ndx); + }); + + if (!track_moves) + return; + + bool row_is_insertion = insertions.contains(row_ndx); + bool last_is_insertion = !insertions.empty() && prev(insertions.end())->second == last_row + 1; + REALM_ASSERT_DEBUG(insertions.empty() || prev(insertions.end())->second <= last_row + 1); + + // Collapse A -> B, B -> C into a single A -> C move + bool last_was_already_moved = false; + if (last_is_insertion) { + auto it = m_move_mapping.find(last_row); + if (it != m_move_mapping.end() && it->first == last_row) { + m_move_mapping[row_ndx] = it->second; + m_move_mapping.erase(it); + last_was_already_moved = true; + } + } + + // Remove moves to the row being deleted + if (row_is_insertion && !last_was_already_moved) { + auto it = m_move_mapping.find(row_ndx); + if (it != m_move_mapping.end() && it->first == row_ndx) + m_move_mapping.erase(it); + } + + // Don't report deletions/moves if last_row is newly inserted + if (last_is_insertion) { + insertions.remove(last_row); + } + // If it was previously moved, the unshifted source row has already been marked as deleted + else if (!last_was_already_moved) { + auto shifted_last_row = insertions.unshift(last_row); + shifted_last_row = deletions.add_shifted(shifted_last_row); + m_move_mapping[row_ndx] = shifted_last_row; + } + + // Don't mark the moved-over row as deleted if it was a new insertion + if (!row_is_insertion) { + deletions.add_shifted(insertions.unshift(row_ndx)); + insertions.add(row_ndx); + } + verify(); +} + +void CollectionChangeBuilder::swap(size_t ndx_1, size_t ndx_2, bool track_moves) +{ + REALM_ASSERT(ndx_1 != ndx_2); + // The order of the two indices doesn't matter semantically, but making them + // consistent simplifies the logic + if (ndx_1 > ndx_2) + std::swap(ndx_1, ndx_2); + + for_each_col([=](auto& col) { + bool row_1_modified = col.contains(ndx_1); + bool row_2_modified = col.contains(ndx_2); + if (row_1_modified != row_2_modified) { + if (row_1_modified) { + col.remove(ndx_1); + col.add(ndx_2); + } + else { + col.remove(ndx_2); + col.add(ndx_1); + } + } + }); + + if (!track_moves) + return; + + auto update_move = [&](auto existing_it, auto ndx_1, auto ndx_2) { + // update the existing move to ndx_2 to point at ndx_1 + auto original = existing_it->second; + m_move_mapping.erase(existing_it); + m_move_mapping[ndx_1] = original; + + // add a move from 1 -> 2 unless 1 was a new insertion + if (!insertions.contains(ndx_1)) { + m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1)); + insertions.add(ndx_1); + } + REALM_ASSERT_DEBUG(insertions.contains(ndx_2)); + }; + + auto move_1 = m_move_mapping.find(ndx_1); + auto move_2 = m_move_mapping.find(ndx_2); + bool have_move_1 = move_1 != end(m_move_mapping) && move_1->first == ndx_1; + bool have_move_2 = move_2 != end(m_move_mapping) && move_2->first == ndx_2; + if (have_move_1 && have_move_2) { + // both are already moves, so just swap the destinations + std::swap(move_1->second, move_2->second); + } + else if (have_move_1) { + update_move(move_1, ndx_2, ndx_1); + } + else if (have_move_2) { + update_move(move_2, ndx_1, ndx_2); + } + else { + // ndx_2 needs to be done before 1 to avoid incorrect shifting + if (!insertions.contains(ndx_2)) { + m_move_mapping[ndx_1] = deletions.add_shifted(insertions.unshift(ndx_2)); + insertions.add(ndx_2); + } + if (!insertions.contains(ndx_1)) { + m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1)); + insertions.add(ndx_1); + } + } +} + +void CollectionChangeBuilder::subsume(size_t old_ndx, size_t new_ndx, bool track_moves) +{ + REALM_ASSERT(old_ndx != new_ndx); + + for_each_col([=](auto& col) { + if (col.contains(old_ndx)) { + col.add(new_ndx); + } + }); + + if (!track_moves) + return; + + REALM_ASSERT_DEBUG(insertions.contains(new_ndx)); + REALM_ASSERT_DEBUG(!m_move_mapping.count(new_ndx)); + + // If the source row was already moved, update the existing move + auto it = m_move_mapping.find(old_ndx); + if (it != m_move_mapping.end() && it->first == old_ndx) { + m_move_mapping[new_ndx] = it->second; + m_move_mapping.erase(it); + } + // otherwise add a new move unless it was a new insertion + else if (!insertions.contains(old_ndx)) { + m_move_mapping[new_ndx] = deletions.shift(insertions.unshift(old_ndx)); + } + + verify(); +} + +void CollectionChangeBuilder::verify() +{ +#ifdef REALM_DEBUG + for (auto&& move : moves) { + REALM_ASSERT(deletions.contains(move.from)); + REALM_ASSERT(insertions.contains(move.to)); + } +#endif +} + +void CollectionChangeBuilder::insert_column(size_t ndx) +{ + if (ndx < columns.size()) + columns.insert(columns.begin() + ndx, IndexSet{}); +} + +void CollectionChangeBuilder::move_column(size_t from, size_t to) +{ + if (from >= columns.size() && to >= columns.size()) + return; + if (from >= columns.size() || to >= columns.size()) + columns.resize(std::max(from, to) + 1); + if (from < to) + std::rotate(begin(columns) + from, begin(columns) + from + 1, begin(columns) + to + 1); + else + std::rotate(begin(columns) + to, begin(columns) + from, begin(columns) + from + 1); +} + +namespace { +struct RowInfo { + size_t row_index; + size_t prev_tv_index; + size_t tv_index; + size_t shifted_tv_index; +}; + +// Calculates the insertions/deletions required for a query on a table without +// a sort, where `removed` includes the rows which were modified to no longer +// match the query (but not outright deleted rows, which are filtered out long +// before any of this logic), and `move_candidates` tracks the rows which may +// be the result of a move. +// +// This function is not strictly required, as calculate_moves_sorted() will +// produce correct results even for the scenarios where this function is used. +// However, this function has asymptotically better worst-case performance and +// extremely cheap best-case performance, and is guaranteed to produce a minimal +// diff when the only row moves are due to move_last_over(). +void calculate_moves_unsorted(std::vector& new_rows, IndexSet& removed, + IndexSet const& move_candidates, + CollectionChangeSet& changeset) +{ + // Here we track which row we expect to see, which in the absence of swap() + // is always the row immediately after the last row which was not moved. + size_t expected = 0; + for (auto& row : new_rows) { + if (row.shifted_tv_index == expected) { + ++expected; + continue; + } + + // We didn't find the row we were expecting to find, which means that + // either a row was moved forward to here, the row we were expecting was + // removed, or the row we were expecting moved back. + + // First check if this row even could have moved. If it can't, just + // treat it as a match and move on, and we'll handle the row we were + // expecting when we hit it later. + if (!move_candidates.contains(row.row_index)) { + expected = row.shifted_tv_index + 1; + continue; + } + + // Next calculate where we expect this row to be based on the insertions + // and removals (i.e. rows changed to not match the query), as it could + // be that the row actually ends up in this spot due to the rows before + // it being removed. + size_t calc_expected = row.tv_index - changeset.insertions.count(0, row.tv_index) + removed.count(0, row.prev_tv_index); + if (row.shifted_tv_index == calc_expected) { + expected = calc_expected + 1; + continue; + } + + // The row still isn't the expected one, so record it as a move + changeset.moves.push_back({row.prev_tv_index, row.tv_index}); + changeset.insertions.add(row.tv_index); + removed.add(row.prev_tv_index); + } +} + +class LongestCommonSubsequenceCalculator { +public: + // A pair of an index in the table and an index in the table view + struct Row { + size_t row_index; + size_t tv_index; + }; + + struct Match { + // The index in `a` at which this match begins + size_t i; + // The index in `b` at which this match begins + size_t j; + // The length of this match + size_t size; + // The number of rows in this block which were modified + size_t modified; + }; + std::vector m_longest_matches; + + LongestCommonSubsequenceCalculator(std::vector& a, std::vector& b, + size_t start_index, + IndexSet const& modifications) + : m_modified(modifications) + , a(a), b(b) + { + find_longest_matches(start_index, a.size(), + start_index, b.size()); + m_longest_matches.push_back({a.size(), b.size(), 0}); + } + +private: + IndexSet const& m_modified; + + // The two arrays of rows being diffed + // a is sorted by tv_index, b is sorted by row_index + std::vector &a, &b; + + // Find the longest matching range in (a + begin1, a + end1) and (b + begin2, b + end2) + // "Matching" is defined as "has the same row index"; the TV index is just + // there to let us turn an index in a/b into an index which can be reported + // in the output changeset. + // + // This is done with the O(N) space variant of the dynamic programming + // algorithm for longest common subsequence, where N is the maximum number + // of the most common row index (which for everything but linkview-derived + // TVs will be 1). + Match find_longest_match(size_t begin1, size_t end1, size_t begin2, size_t end2) + { + struct Length { + size_t j, len; + }; + // The length of the matching block for each `j` for the previously checked row + std::vector prev; + // The length of the matching block for each `j` for the row currently being checked + std::vector cur; + + // Calculate the length of the matching block *ending* at b[j], which + // is 1 if b[j - 1] did not match, and b[j - 1] + 1 otherwise. + auto length = [&](size_t j) -> size_t { + for (auto const& pair : prev) { + if (pair.j + 1 == j) + return pair.len + 1; + } + return 1; + }; + + // Iterate over each `j` which has the same row index as a[i] and falls + // within the range begin2 <= j < end2 + auto for_each_b_match = [&](size_t i, auto&& f) { + size_t ai = a[i].row_index; + // Find the TV indicies at which this row appears in the new results + // There should always be at least one (or it would have been + // filtered out earlier), but there can be multiple if there are dupes + auto it = lower_bound(begin(b), end(b), ai, + [](auto lft, auto rgt) { return lft.row_index < rgt; }); + REALM_ASSERT(it != end(b) && it->row_index == ai); + for (; it != end(b) && it->row_index == ai; ++it) { + size_t j = it->tv_index; + if (j < begin2) + continue; + if (j >= end2) + break; // b is sorted by tv_index so this can't transition from false to true + f(j); + } + }; + + Match best = {begin1, begin2, 0, 0}; + for (size_t i = begin1; i < end1; ++i) { + // prev = std::move(cur), but avoids discarding prev's heap allocation + cur.swap(prev); + cur.clear(); + + for_each_b_match(i, [&](size_t j) { + size_t size = length(j); + + cur.push_back({j, size}); + + // If the matching block ending at a[i] and b[j] is longer than + // the previous one, select it as the best + if (size > best.size) + best = {i - size + 1, j - size + 1, size, IndexSet::npos}; + // Given two equal-length matches, prefer the one with fewer modified rows + else if (size == best.size) { + if (best.modified == IndexSet::npos) + best.modified = m_modified.count(best.j - size + 1, best.j + 1); + auto count = m_modified.count(j - size + 1, j + 1); + if (count < best.modified) + best = {i - size + 1, j - size + 1, size, count}; + } + + // The best block should always fall within the range being searched + REALM_ASSERT(best.i >= begin1 && best.i + best.size <= end1); + REALM_ASSERT(best.j >= begin2 && best.j + best.size <= end2); + }); + } + return best; + } + + void find_longest_matches(size_t begin1, size_t end1, size_t begin2, size_t end2) + { + // FIXME: recursion could get too deep here + // recursion depth worst case is currently O(N) and each recursion uses 320 bytes of stack + // could reduce worst case to O(sqrt(N)) (and typical case to O(log N)) + // biasing equal selections towards the middle, but that's still + // insufficient for Android's 8 KB stacks + auto m = find_longest_match(begin1, end1, begin2, end2); + if (!m.size) + return; + if (m.i > begin1 && m.j > begin2) + find_longest_matches(begin1, m.i, begin2, m.j); + m_longest_matches.push_back(m); + if (m.i + m.size < end2 && m.j + m.size < end2) + find_longest_matches(m.i + m.size, end1, m.j + m.size, end2); + } +}; + +void calculate_moves_sorted(std::vector& rows, CollectionChangeSet& changeset) +{ + // The RowInfo array contains information about the old and new TV indices of + // each row, which we need to turn into two sequences of rows, which we'll + // then find matches in + std::vector a, b; + + a.reserve(rows.size()); + for (auto& row : rows) { + a.push_back({row.row_index, row.prev_tv_index}); + } + std::sort(begin(a), end(a), [](auto lft, auto rgt) { + return std::tie(lft.tv_index, lft.row_index) < std::tie(rgt.tv_index, rgt.row_index); + }); + + // Before constructing `b`, first find the first index in `a` which will + // actually differ in `b`, and skip everything else if there aren't any + size_t first_difference = IndexSet::npos; + for (size_t i = 0; i < a.size(); ++i) { + if (a[i].row_index != rows[i].row_index) { + first_difference = i; + break; + } + } + if (first_difference == IndexSet::npos) + return; + + // Note that `b` is sorted by row_index, while `a` is sorted by tv_index + b.reserve(rows.size()); + for (size_t i = 0; i < rows.size(); ++i) + b.push_back({rows[i].row_index, i}); + std::sort(begin(b), end(b), [](auto lft, auto rgt) { + return std::tie(lft.row_index, lft.tv_index) < std::tie(rgt.row_index, rgt.tv_index); + }); + + // Calculate the LCS of the two sequences + auto matches = LongestCommonSubsequenceCalculator(a, b, first_difference, + changeset.modifications).m_longest_matches; + + // And then insert and delete rows as needed to align them + size_t i = first_difference, j = first_difference; + for (auto match : matches) { + for (; i < match.i; ++i) + changeset.deletions.add(a[i].tv_index); + for (; j < match.j; ++j) + changeset.insertions.add(rows[j].tv_index); + i += match.size; + j += match.size; + } +} + +} // Anonymous namespace + +CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector const& prev_rows, + std::vector const& next_rows, + std::function row_did_change, + util::Optional const& move_candidates) +{ + REALM_ASSERT_DEBUG(!move_candidates || std::is_sorted(begin(next_rows), end(next_rows))); + + CollectionChangeBuilder ret; + + size_t deleted = 0; + std::vector old_rows; + old_rows.reserve(prev_rows.size()); + for (size_t i = 0; i < prev_rows.size(); ++i) { + if (prev_rows[i] == IndexSet::npos) { + ++deleted; + ret.deletions.add(i); + } + else + old_rows.push_back({prev_rows[i], IndexSet::npos, i, i - deleted}); + } + std::sort(begin(old_rows), end(old_rows), [](auto& lft, auto& rgt) { + return lft.row_index < rgt.row_index; + }); + + std::vector new_rows; + new_rows.reserve(next_rows.size()); + for (size_t i = 0; i < next_rows.size(); ++i) { + new_rows.push_back({next_rows[i], IndexSet::npos, i, 0}); + } + std::sort(begin(new_rows), end(new_rows), [](auto& lft, auto& rgt) { + return lft.row_index < rgt.row_index; + }); + + // Don't add rows which were modified to not match the query to `deletions` + // immediately because the unsorted move logic needs to be able to + // distinguish them from rows which were outright deleted + IndexSet removed; + + // Now that our old and new sets of rows are sorted by row index, we can + // iterate over them and either record old+new TV indices for rows present + // in both, or mark them as inserted/deleted if they appear only in one + size_t i = 0, j = 0; + while (i < old_rows.size() && j < new_rows.size()) { + auto old_index = old_rows[i]; + auto new_index = new_rows[j]; + if (old_index.row_index == new_index.row_index) { + new_rows[j].prev_tv_index = old_rows[i].tv_index; + new_rows[j].shifted_tv_index = old_rows[i].shifted_tv_index; + ++i; + ++j; + } + else if (old_index.row_index < new_index.row_index) { + removed.add(old_index.tv_index); + ++i; + } + else { + ret.insertions.add(new_index.tv_index); + ++j; + } + } + + for (; i < old_rows.size(); ++i) + removed.add(old_rows[i].tv_index); + for (; j < new_rows.size(); ++j) + ret.insertions.add(new_rows[j].tv_index); + + // Filter out the new insertions since we don't need them for any of the + // further calculations + new_rows.erase(std::remove_if(begin(new_rows), end(new_rows), + [](auto& row) { return row.prev_tv_index == IndexSet::npos; }), + end(new_rows)); + std::sort(begin(new_rows), end(new_rows), + [](auto& lft, auto& rgt) { return lft.tv_index < rgt.tv_index; }); + + for (auto& row : new_rows) { + if (row_did_change(row.row_index)) { + ret.modifications.add(row.tv_index); + } + } + + if (move_candidates) { + calculate_moves_unsorted(new_rows, removed, *move_candidates, ret); + } + else { + calculate_moves_sorted(new_rows, ret); + } + ret.deletions.add(removed); + ret.verify(); + +#ifdef REALM_DEBUG + { // Verify that applying the calculated change to prev_rows actually produces next_rows + auto rows = prev_rows; + auto it = util::make_reverse_iterator(ret.deletions.end()); + auto end = util::make_reverse_iterator(ret.deletions.begin()); + for (; it != end; ++it) { + rows.erase(rows.begin() + it->first, rows.begin() + it->second); + } + + for (auto i : ret.insertions.as_indexes()) { + rows.insert(rows.begin() + i, next_rows[i]); + } + + REALM_ASSERT(rows == next_rows); + } +#endif + + return ret; +} + +CollectionChangeSet CollectionChangeBuilder::finalize() && +{ + // Calculate which indices in the old collection were modified + auto modifications_in_old = modifications; + modifications_in_old.erase_at(insertions); + modifications_in_old.shift_for_insert_at(deletions); + + // During changeset calculation we allow marking a row as both inserted and + // modified in case changeset merging results in it no longer being an insert, + // but we don't want inserts in the final modification set + modifications.remove(insertions); + + return { + std::move(deletions), + std::move(insertions), + std::move(modifications_in_old), + std::move(modifications), + std::move(moves), + std::move(columns) + }; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp new file mode 100644 index 0000000..a845d49 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp @@ -0,0 +1,486 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/collection_notifier.hpp" + +#include "impl/realm_coordinator.hpp" +#include "shared_realm.hpp" + +#include +#include + +using namespace realm; +using namespace realm::_impl; + +std::function +CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info, + Table const& root_table) +{ + // First check if any of the tables accessible from the root table were + // actually modified. This can be false if there were only insertions, or + // deletions which were not linked to by any row in the linking table + auto table_modified = [&](auto& tbl) { + return tbl.table_ndx < info.tables.size() + && !info.tables[tbl.table_ndx].modifications.empty(); + }; + if (!any_of(begin(m_related_tables), end(m_related_tables), table_modified)) { + return [](size_t) { return false; }; + } + + return DeepChangeChecker(info, root_table, m_related_tables); +} + +void DeepChangeChecker::find_related_tables(std::vector& out, Table const& table) +{ + auto table_ndx = table.get_index_in_group(); + if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_ndx == table_ndx; })) + return; + + // We need to add this table to `out` before recurring so that the check + // above works, but we can't store a pointer to the thing being populated + // because the recursive calls may resize `out`, so instead look it up by + // index every time + size_t out_index = out.size(); + out.push_back({table_ndx, {}}); + + for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { + auto type = table.get_column_type(i); + if (type == type_Link || type == type_LinkList) { + out[out_index].links.push_back({i, type == type_LinkList}); + find_related_tables(out, *table.get_link_target(i)); + } + } +} + +DeepChangeChecker::DeepChangeChecker(TransactionChangeInfo const& info, + Table const& root_table, + std::vector const& related_tables) +: m_info(info) +, m_root_table(root_table) +, m_root_table_ndx(root_table.get_index_in_group()) +, m_root_modifications(m_root_table_ndx < info.tables.size() ? &info.tables[m_root_table_ndx].modifications : nullptr) +, m_related_tables(related_tables) +{ +} + +bool DeepChangeChecker::check_outgoing_links(size_t table_ndx, + Table const& table, + size_t row_ndx, size_t depth) +{ + auto it = find_if(begin(m_related_tables), end(m_related_tables), + [&](auto&& tbl) { return tbl.table_ndx == table_ndx; }); + if (it == m_related_tables.end()) + return false; + + // Check if we're already checking if the destination of the link is + // modified, and if not add it to the stack + auto already_checking = [&](size_t col) { + auto end = m_current_path.begin() + depth; + auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) { + return p.table == table_ndx && p.row == row_ndx && p.col == col; + }); + if (match != end) { + for (; match < end; ++match) match->depth_exceeded = true; + return true; + } + m_current_path[depth] = {table_ndx, row_ndx, col, false}; + return false; + }; + + auto linked_object_changed = [&](OutgoingLink const& link) { + if (already_checking(link.col_ndx)) + return false; + if (!link.is_list) { + if (table.is_null_link(link.col_ndx, row_ndx)) + return false; + auto dst = table.get_link(link.col_ndx, row_ndx); + return check_row(*table.get_link_target(link.col_ndx), dst, depth + 1); + } + + auto& target = *table.get_link_target(link.col_ndx); + auto lvr = table.get_linklist(link.col_ndx, row_ndx); + for (size_t j = 0, size = lvr->size(); j < size; ++j) { + size_t dst = lvr->get(j).get_index(); + if (check_row(target, dst, depth + 1)) + return true; + } + return false; + }; + + return std::any_of(begin(it->links), end(it->links), linked_object_changed); +} + +bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth) +{ + // Arbitrary upper limit on the maximum depth to search + if (depth >= m_current_path.size()) { + // Don't mark any of the intermediate rows checked along the path as + // not modified, as a search starting from them might hit a modification + for (size_t i = 1; i < m_current_path.size(); ++i) + m_current_path[i].depth_exceeded = true; + return false; + } + + size_t table_ndx = table.get_index_in_group(); + if (depth > 0 && table_ndx < m_info.tables.size() && m_info.tables[table_ndx].modifications.contains(idx)) + return true; + + if (m_not_modified.size() <= table_ndx) + m_not_modified.resize(table_ndx + 1); + if (m_not_modified[table_ndx].contains(idx)) + return false; + + bool ret = check_outgoing_links(table_ndx, table, idx, depth); + if (!ret && (depth == 0 || !m_current_path[depth - 1].depth_exceeded)) + m_not_modified[table_ndx].add(idx); + return ret; +} + +bool DeepChangeChecker::operator()(size_t ndx) +{ + if (m_root_modifications && m_root_modifications->contains(ndx)) + return true; + return check_row(m_root_table, ndx, 0); +} + +CollectionNotifier::CollectionNotifier(std::shared_ptr realm) +: m_realm(std::move(realm)) +, m_sg_version(Realm::Internal::get_shared_group(*m_realm)->get_version_of_current_transaction()) +{ +} + +CollectionNotifier::~CollectionNotifier() +{ + // Need to do this explicitly to ensure m_realm is destroyed with the mutex + // held to avoid potential double-deletion + unregister(); +} + +size_t CollectionNotifier::add_callback(CollectionChangeCallback callback) +{ + m_realm->verify_thread(); + + auto next_token = [=] { + size_t token = 0; + for (auto& callback : m_callbacks) { + if (token <= callback.token) { + token = callback.token + 1; + } + } + return token; + }; + + std::lock_guard lock(m_callback_mutex); + auto token = next_token(); + m_callbacks.push_back({std::move(callback), {}, {}, token, false, false}); + if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications + Realm::Internal::get_coordinator(*m_realm).wake_up_notifier_worker(); + m_have_callbacks = true; + } + return token; +} + +void CollectionNotifier::remove_callback(size_t token) +{ + // the callback needs to be destroyed after releasing the lock as destroying + // it could cause user code to be called + Callback old; + { + std::lock_guard lock(m_callback_mutex); + auto it = find_callback(token); + if (it == end(m_callbacks)) { + return; + } + + size_t idx = distance(begin(m_callbacks), it); + if (m_callback_index != npos && m_callback_index >= idx) { + --m_callback_index; + } + + old = std::move(*it); + m_callbacks.erase(it); + + m_have_callbacks = !m_callbacks.empty(); + } +} + +void CollectionNotifier::suppress_next_notification(size_t token) +{ + { + std::lock_guard lock(m_realm_mutex); + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + m_realm->verify_in_write(); + } + + std::lock_guard lock(m_callback_mutex); + auto it = find_callback(token); + if (it != end(m_callbacks)) { + it->skip_next = true; + } +} + +std::vector::iterator CollectionNotifier::find_callback(size_t token) +{ + REALM_ASSERT(m_error || m_callbacks.size() > 0); + + auto it = find_if(begin(m_callbacks), end(m_callbacks), + [=](const auto& c) { return c.token == token; }); + // We should only fail to find the callback if it was removed due to an error + REALM_ASSERT(m_error || it != end(m_callbacks)); + return it; +} + +void CollectionNotifier::unregister() noexcept +{ + std::lock_guard lock(m_realm_mutex); + m_realm = nullptr; +} + +bool CollectionNotifier::is_alive() const noexcept +{ + std::lock_guard lock(m_realm_mutex); + return m_realm != nullptr; +} + +std::unique_lock CollectionNotifier::lock_target() +{ + return std::unique_lock{m_realm_mutex}; +} + +void CollectionNotifier::set_table(Table const& table) +{ + m_related_tables.clear(); + DeepChangeChecker::find_related_tables(m_related_tables, table); +} + +void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info) +{ + if (!do_add_required_change_info(info) || m_related_tables.empty()) { + return; + } + + auto max = max_element(begin(m_related_tables), end(m_related_tables), + [](auto&& a, auto&& b) { return a.table_ndx < b.table_ndx; }); + + if (max->table_ndx >= info.table_modifications_needed.size()) + info.table_modifications_needed.resize(max->table_ndx + 1, false); + for (auto& tbl : m_related_tables) { + info.table_modifications_needed[tbl.table_ndx] = true; + } +} + +void CollectionNotifier::prepare_handover() +{ + REALM_ASSERT(m_sg); + m_sg_version = m_sg->get_version_of_current_transaction(); + do_prepare_handover(*m_sg); + m_has_run = true; + +#ifdef REALM_DEBUG + std::lock_guard lock(m_callback_mutex); + for (auto& callback : m_callbacks) + REALM_ASSERT(!callback.skip_next); +#endif +} + +void CollectionNotifier::before_advance() +{ + for_each_callback([&](auto& lock, auto& callback) { + if (callback.changes_to_deliver.empty()) { + return; + } + + auto changes = callback.changes_to_deliver; + // acquire a local reference to the callback so that removing the + // callback from within it can't result in a dangling pointer + auto cb = callback.fn; + lock.unlock(); + cb.before(changes); + }); +} + +void CollectionNotifier::after_advance() +{ + for_each_callback([&](auto& lock, auto& callback) { + if (callback.initial_delivered && callback.changes_to_deliver.empty()) { + return; + } + callback.initial_delivered = true; + + auto changes = std::move(callback.changes_to_deliver); + // acquire a local reference to the callback so that removing the + // callback from within it can't result in a dangling pointer + auto cb = callback.fn; + lock.unlock(); + cb.after(changes); + }); +} + +void CollectionNotifier::deliver_error(std::exception_ptr error) +{ + for_each_callback([&](auto& lock, auto& callback) { + // acquire a local reference to the callback so that removing the + // callback from within it can't result in a dangling pointer + auto cb = callback.fn; + lock.unlock(); + cb.error(error); + }); + + // Remove all the callbacks as we never need to call anything ever again + // after delivering an error + m_callbacks.clear(); + m_error = true; +} + +bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept +{ + std::lock_guard lock(m_realm_mutex); + return m_realm.get() == &realm; +} + +bool CollectionNotifier::package_for_delivery() +{ + if (!prepare_to_deliver()) + return false; + std::lock_guard l(m_callback_mutex); + for (auto& callback : m_callbacks) + callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize(); + return true; +} + +template +void CollectionNotifier::for_each_callback(Fn&& fn) +{ + std::unique_lock callback_lock(m_callback_mutex); + for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) { + fn(callback_lock, m_callbacks[m_callback_index]); + if (!callback_lock.owns_lock()) + callback_lock.lock(); + } + + m_callback_index = npos; +} + +void CollectionNotifier::attach_to(SharedGroup& sg) +{ + REALM_ASSERT(!m_sg); + + m_sg = &sg; + do_attach_to(sg); +} + +void CollectionNotifier::detach() +{ + REALM_ASSERT(m_sg); + do_detach_from(*m_sg); + m_sg = nullptr; +} + +void CollectionNotifier::add_changes(CollectionChangeBuilder change) +{ + std::lock_guard lock(m_callback_mutex); + for (auto& callback : m_callbacks) { + if (callback.skip_next) { + REALM_ASSERT_DEBUG(callback.accumulated_changes.empty()); + callback.skip_next = false; + } + else { + if (&callback == &m_callbacks.back()) + callback.accumulated_changes.merge(std::move(change)); + else + callback.accumulated_changes.merge(CollectionChangeBuilder(change)); + } + } +} + +NotifierPackage::NotifierPackage(std::exception_ptr error, + std::vector> notifiers, + RealmCoordinator* coordinator) +: m_notifiers(std::move(notifiers)) +, m_coordinator(coordinator) +, m_error(std::move(error)) +{ +} + +void NotifierPackage::package_and_wait(util::Optional target_version) +{ + if (!m_coordinator || m_error || !*this) + return; + + auto lock = m_coordinator->wait_for_notifiers([&] { + if (!target_version) + return true; + return std::all_of(begin(m_notifiers), end(m_notifiers), [&](auto const& n) { + return !n->have_callbacks() || (n->has_run() && n->version().version >= *target_version); + }); + }); + + // Package the notifiers for delivery and remove any which don't have anything to deliver + auto package = [&](auto& notifier) { + if (notifier->has_run() && notifier->package_for_delivery()) { + m_version = notifier->version(); + return false; + } + return true; + }; + m_notifiers.erase(std::remove_if(begin(m_notifiers), end(m_notifiers), package), end(m_notifiers)); + if (m_version && target_version && m_version->version < *target_version) { + m_notifiers.clear(); + m_version = util::none; + } + REALM_ASSERT(m_version || m_notifiers.empty()); + + m_coordinator = nullptr; +} + +void NotifierPackage::before_advance() +{ + if (m_error) + return; + for (auto& notifier : m_notifiers) + notifier->before_advance(); +} + +void NotifierPackage::deliver(SharedGroup& sg) +{ + if (m_error) { + for (auto& notifier : m_notifiers) + notifier->deliver_error(m_error); + return; + } + // Can't deliver while in a write transaction + if (sg.get_transact_stage() != SharedGroup::transact_Reading) + return; + for (auto& notifier : m_notifiers) + notifier->deliver(sg); +} + +void NotifierPackage::after_advance() +{ + if (m_error) + return; + for (auto& notifier : m_notifiers) + notifier->after_advance(); +} + +void NotifierPackage::add_notifier(std::shared_ptr notifier) +{ + m_notifiers.push_back(notifier); + m_coordinator->register_notifier(notifier); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp new file mode 100644 index 0000000..60a7e3a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/list_notifier.hpp" + +#include "shared_realm.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +ListNotifier::ListNotifier(LinkViewRef lv, std::shared_ptr realm) +: CollectionNotifier(std::move(realm)) +, m_prev_size(lv->size()) +{ + set_table(lv->get_target_table()); + + auto& sg = Realm::Internal::get_shared_group(*get_realm()); + m_lv_handover = sg->export_linkview_for_handover(lv); +} + +void ListNotifier::release_data() noexcept +{ + m_lv.reset(); +} + +void ListNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_lv_handover); + REALM_ASSERT(!m_lv); + m_lv = sg.import_linkview_from_handover(std::move(m_lv_handover)); +} + +void ListNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(!m_lv_handover); + if (m_lv) { + m_lv_handover = sg.export_linkview_for_handover(m_lv); + m_lv = {}; + } +} + +bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(!m_lv_handover); + if (!m_lv || !m_lv->is_attached()) { + return false; // origin row was deleted after the notification was added + } + + // Find the lv's column, since that isn't tracked directly + auto& table = m_lv->get_origin_table(); + size_t row_ndx = m_lv->get_origin_row_index(); + size_t col_ndx = not_found; + for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { + if (table.get_column_type(i) == type_LinkList && table.get_linklist(i, row_ndx) == m_lv) { + col_ndx = i; + break; + } + } + REALM_ASSERT(col_ndx != not_found); + info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change}); + + m_info = &info; + return true; +} + +void ListNotifier::run() +{ + if (!m_lv || !m_lv->is_attached()) { + // LV was deleted, so report all of the rows being removed if this is + // the first run after that + if (m_prev_size) { + m_change.deletions.set(m_prev_size); + m_prev_size = 0; + } + else { + m_change = {}; + } + return; + } + + auto row_did_change = get_modification_checker(*m_info, m_lv->get_target_table()); + for (size_t i = 0; i < m_lv->size(); ++i) { + if (m_change.modifications.contains(i)) + continue; + if (row_did_change(m_lv->get(i).get_index())) + m_change.modifications.add(i); + } + + for (auto const& move : m_change.moves) { + if (m_change.modifications.contains(move.to)) + continue; + if (row_did_change(m_lv->get(move.to).get_index())) + m_change.modifications.add(move.to); + } + + m_prev_size = m_lv->size(); +} + +void ListNotifier::do_prepare_handover(SharedGroup&) +{ + add_changes(std::move(m_change)); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp new file mode 100644 index 0000000..0e43db5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/object_notifier.hpp" + +#include "shared_realm.hpp" + +using namespace realm; +using namespace realm::_impl; + +ObjectNotifier::ObjectNotifier(Row const& row, std::shared_ptr realm) +: CollectionNotifier(std::move(realm)) +{ + REALM_ASSERT(row.get_table()); + set_table(*row.get_table()); + + auto& sg = Realm::Internal::get_shared_group(*get_realm()); + m_handover = sg->export_for_handover(row); +} + +void ObjectNotifier::release_data() noexcept +{ + m_row = nullptr; +} + +void ObjectNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_handover); + REALM_ASSERT(!m_row); + m_row = sg.import_from_handover(std::move(m_handover)); +} + +void ObjectNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(!m_handover); + if (m_row) { + m_handover = sg.export_for_handover(*m_row); + m_row = nullptr; + } +} + +bool ObjectNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(!m_handover); + m_info = &info; + if (m_row && m_row->is_attached()) { + size_t table_ndx = m_row->get_table()->get_index_in_group(); + if (table_ndx >= info.table_modifications_needed.size()) + info.table_modifications_needed.resize(table_ndx + 1); + info.table_modifications_needed[table_ndx] = true; + } + return false; +} + +void ObjectNotifier::run() +{ + if (!m_row) + return; + if (!m_row->is_attached()) { + m_change.deletions.add(0); + m_row = nullptr; + return; + } + + size_t table_ndx = m_row->get_table()->get_index_in_group(); + if (table_ndx >= m_info->tables.size()) + return; + auto& change = m_info->tables[table_ndx]; + if (!change.modifications.contains(m_row->get_index())) + return; + m_change.modifications.add(0); + m_change.columns.reserve(change.columns.size()); + for (auto& col : change.columns) { + m_change.columns.emplace_back(); + if (col.contains(m_row->get_index())) + m_change.columns.back().add(0); + } +} + +void ObjectNotifier::do_prepare_handover(SharedGroup&) +{ + add_changes(std::move(m_change)); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp new file mode 100644 index 0000000..4aa0132 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp @@ -0,0 +1,886 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/realm_coordinator.hpp" + +#include "impl/collection_notifier.hpp" +#include "impl/external_commit_helper.hpp" +#include "impl/transact_log_handler.hpp" +#include "impl/weak_realm_notifier.hpp" +#include "binding_context.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "schema.hpp" + +#if REALM_ENABLE_SYNC +#include "sync/sync_config.hpp" +#include "sync/sync_manager.hpp" +#include "sync/sync_session.hpp" +#endif + +#include +#include +#include + +#include +#include + +using namespace realm; +using namespace realm::_impl; + +static auto& s_coordinator_mutex = *new std::mutex; +static auto& s_coordinators_per_path = *new std::unordered_map>; + +std::shared_ptr RealmCoordinator::get_coordinator(StringData path) +{ + std::lock_guard lock(s_coordinator_mutex); + + auto& weak_coordinator = s_coordinators_per_path[path]; + if (auto coordinator = weak_coordinator.lock()) { + return coordinator; + } + + auto coordinator = std::make_shared(); + weak_coordinator = coordinator; + return coordinator; +} + +std::shared_ptr RealmCoordinator::get_coordinator(const Realm::Config& config) +{ + auto coordinator = get_coordinator(config.path); + std::lock_guard lock(coordinator->m_realm_mutex); + coordinator->set_config(config); + return coordinator; +} + +std::shared_ptr RealmCoordinator::get_existing_coordinator(StringData path) +{ + std::lock_guard lock(s_coordinator_mutex); + auto it = s_coordinators_per_path.find(path); + return it == s_coordinators_per_path.end() ? nullptr : it->second.lock(); +} + +void RealmCoordinator::create_sync_session() +{ +#if REALM_ENABLE_SYNC + if (m_sync_session) + return; + + if (!m_config.encryption_key.empty() && !m_config.sync_config->realm_encryption_key) { + throw std::logic_error("A realm encryption key was specified in Realm::Config but not in SyncConfig"); + } else if (m_config.sync_config->realm_encryption_key && m_config.encryption_key.empty()) { + throw std::logic_error("A realm encryption key was specified in SyncConfig but not in Realm::Config"); + } else if (m_config.sync_config->realm_encryption_key && + !std::equal(m_config.sync_config->realm_encryption_key->begin(), m_config.sync_config->realm_encryption_key->end(), + m_config.encryption_key.begin(), m_config.encryption_key.end())) { + throw std::logic_error("The realm encryption key specified in SyncConfig does not match the one in Realm::Config"); + } + + m_sync_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config); + + std::weak_ptr weak_self = shared_from_this(); + SyncSession::Internal::set_sync_transact_callback(*m_sync_session, + [weak_self](VersionID old_version, VersionID new_version) { + if (auto self = weak_self.lock()) { + if (self->m_transaction_callback) + self->m_transaction_callback(old_version, new_version); + if (self->m_notifier) + self->m_notifier->notify_others(); + } + }); + if (m_config.sync_config->error_handler) { + SyncSession::Internal::set_error_handler(*m_sync_session, m_config.sync_config->error_handler); + } +#endif +} + +void RealmCoordinator::set_config(const Realm::Config& config) +{ + if (config.encryption_key.data() && config.encryption_key.size() != 64) + throw InvalidEncryptionKeyException(); + if (config.schema_mode == SchemaMode::ReadOnly && config.sync_config) + throw std::logic_error("Synchronized Realms cannot be opened in read-only mode"); + if (config.schema_mode == SchemaMode::Additive && config.migration_function) + throw std::logic_error("Realms opened in Additive-only schema mode do not use a migration function"); + if (config.schema_mode == SchemaMode::ReadOnly && config.migration_function) + throw std::logic_error("Realms opened in read-only mode do not use a migration function"); + if (config.schema && config.schema_version == ObjectStore::NotVersioned) + throw std::logic_error("A schema version must be specified when the schema is specified"); + if (!config.realm_data.is_null() && (!config.read_only() || !config.in_memory)) + throw std::logic_error("In-memory realms initialized from memory buffers can only be opened in read-only mode"); + if (!config.realm_data.is_null() && !config.path.empty()) + throw std::logic_error("Specifying both memory buffer and path is invalid"); + if (!config.realm_data.is_null() && !config.encryption_key.empty()) + throw std::logic_error("Memory buffers do not support encryption"); + // ResetFile also won't use the migration function, but specifying one is + // allowed to simplify temporarily switching modes during development + + bool no_existing_realm = std::all_of(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers), + [](auto& notifier) { return notifier.expired(); }); + if (no_existing_realm) { + m_config = config; + } + else { + if (m_config.read_only() != config.read_only()) { + throw MismatchedConfigException("Realm at path '%1' already opened with different read permissions.", config.path); + } + if (m_config.in_memory != config.in_memory) { + throw MismatchedConfigException("Realm at path '%1' already opened with different inMemory settings.", config.path); + } + if (m_config.encryption_key != config.encryption_key) { + throw MismatchedConfigException("Realm at path '%1' already opened with a different encryption key.", config.path); + } + if (m_config.schema_mode != config.schema_mode) { + throw MismatchedConfigException("Realm at path '%1' already opened with a different schema mode.", config.path); + } + if (config.schema && m_schema_version != ObjectStore::NotVersioned && m_schema_version != config.schema_version) { + throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path); + } + +#if REALM_ENABLE_SYNC + if (bool(m_config.sync_config) != bool(config.sync_config)) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync configurations.", config.path); + } + + if (config.sync_config) { + if (m_config.sync_config->user != config.sync_config->user) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync user.", config.path); + } + if (m_config.sync_config->realm_url != config.sync_config->realm_url) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync server URL.", config.path); + } + if (m_config.sync_config->transformer != config.sync_config->transformer) { + throw MismatchedConfigException("Realm at path '%1' already opened with different transformer.", config.path); + } + if (m_config.sync_config->realm_encryption_key != config.sync_config->realm_encryption_key) { + throw MismatchedConfigException("Realm at path '%1' already opened with sync session encryption key.", config.path); + } + } +#endif + + // Realm::update_schema() handles complaining about schema mismatches + } + +#if REALM_ENABLE_SYNC + if (config.sync_config) { + create_sync_session(); + } +#endif +} + +std::shared_ptr RealmCoordinator::get_realm(Realm::Config config) +{ + // realm must be declared before lock so that the mutex is released before + // we release the strong reference to realm, as Realm's destructor may want + // to acquire the same lock + std::shared_ptr realm; + std::unique_lock lock(m_realm_mutex); + + set_config(config); + + auto schema = std::move(config.schema); + auto migration_function = std::move(config.migration_function); + config.schema = {}; + + if (config.cache) { + AnyExecutionContextID execution_context(config.execution_context); + for (auto& cached_realm : m_weak_realm_notifiers) { + if (!cached_realm.is_cached_for_execution_context(execution_context)) + continue; + // can be null if we jumped in between ref count hitting zero and + // unregister_realm() getting the lock + if ((realm = cached_realm.realm())) { + // If the file is uninitialized and was opened without a schema, + // do the normal schema init + if (realm->schema_version() == ObjectStore::NotVersioned) + break; + + // Otherwise if we have a realm schema it needs to be an exact + // match (even having the same properties but in different + // orders isn't good enough) + if (schema && realm->schema() != *schema) + throw MismatchedConfigException("Realm at path '%1' already opened on current thread with different schema.", config.path); + + return realm; + } + } + } + + if (!realm) { + realm = Realm::make_shared_realm(std::move(config), shared_from_this()); + if (!config.read_only() && !m_notifier && config.automatic_change_notifications) { + try { + m_notifier = std::make_unique(*this); + } + catch (std::system_error const& ex) { + throw RealmFileException(RealmFileException::Kind::AccessError, get_path(), ex.code().message(), ""); + } + } + m_weak_realm_notifiers.emplace_back(realm, m_config.cache); + } + + if (schema) { + lock.unlock(); + realm->update_schema(std::move(*schema), config.schema_version, std::move(migration_function)); + } + + return realm; +} + +std::shared_ptr RealmCoordinator::get_realm() +{ + return get_realm(m_config); +} + +bool RealmCoordinator::get_cached_schema(Schema& schema, uint64_t& schema_version, + uint64_t& transaction) const noexcept +{ + std::lock_guard lock(m_schema_cache_mutex); + if (!m_cached_schema) + return false; + schema = *m_cached_schema; + schema_version = m_schema_version; + transaction = m_schema_transaction_version_max; + return true; +} + +void RealmCoordinator::cache_schema(Schema const& new_schema, uint64_t new_schema_version, + uint64_t transaction_version) +{ + std::lock_guard lock(m_schema_cache_mutex); + if (transaction_version < m_schema_transaction_version_max) + return; + if (new_schema.empty() || new_schema_version == ObjectStore::NotVersioned) + return; + + m_cached_schema = new_schema; + m_schema_version = new_schema_version; + m_schema_transaction_version_min = transaction_version; + m_schema_transaction_version_max = transaction_version; +} + +void RealmCoordinator::clear_schema_cache_and_set_schema_version(uint64_t new_schema_version) +{ + std::lock_guard lock(m_schema_cache_mutex); + m_cached_schema = util::none; + m_schema_version = new_schema_version; +} + +void RealmCoordinator::advance_schema_cache(uint64_t previous, uint64_t next) +{ + std::lock_guard lock(m_schema_cache_mutex); + if (!m_cached_schema) + return; + REALM_ASSERT(previous <= m_schema_transaction_version_max); + if (next < m_schema_transaction_version_min) + return; + m_schema_transaction_version_min = std::min(previous, m_schema_transaction_version_min); + m_schema_transaction_version_max = std::max(next, m_schema_transaction_version_max); +} + +RealmCoordinator::RealmCoordinator() = default; + +RealmCoordinator::~RealmCoordinator() +{ + std::lock_guard coordinator_lock(s_coordinator_mutex); + for (auto it = s_coordinators_per_path.begin(); it != s_coordinators_per_path.end(); ) { + if (it->second.expired()) { + it = s_coordinators_per_path.erase(it); + } + else { + ++it; + } + } +} + +void RealmCoordinator::unregister_realm(Realm* realm) +{ + std::lock_guard lock(m_realm_mutex); + auto new_end = remove_if(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers), + [=](auto& notifier) { return notifier.expired() || notifier.is_for_realm(realm); }); + m_weak_realm_notifiers.erase(new_end, end(m_weak_realm_notifiers)); +} + +void RealmCoordinator::clear_cache() +{ + std::vector realms_to_close; + { + std::lock_guard lock(s_coordinator_mutex); + + for (auto& weak_coordinator : s_coordinators_per_path) { + auto coordinator = weak_coordinator.second.lock(); + if (!coordinator) { + continue; + } + + coordinator->m_notifier = nullptr; + + // Gather a list of all of the realms which will be removed + for (auto& weak_realm_notifier : coordinator->m_weak_realm_notifiers) { + if (auto realm = weak_realm_notifier.realm()) { + realms_to_close.push_back(realm); + } + } + } + + s_coordinators_per_path.clear(); + } + + // Close all of the previously cached Realms. This can't be done while + // s_coordinator_mutex is held as it may try to re-lock it. + for (auto& weak_realm : realms_to_close) { + if (auto realm = weak_realm.lock()) { + realm->close(); + } + } +} + +void RealmCoordinator::clear_all_caches() +{ + std::vector> to_clear; + { + std::lock_guard lock(s_coordinator_mutex); + for (auto iter : s_coordinators_per_path) { + to_clear.push_back(iter.second); + } + } + for (auto weak_coordinator : to_clear) { + if (auto coordinator = weak_coordinator.lock()) { + coordinator->clear_cache(); + } + } +} + +void RealmCoordinator::assert_no_open_realms() noexcept +{ +#ifdef REALM_DEBUG + std::lock_guard lock(s_coordinator_mutex); + REALM_ASSERT(s_coordinators_per_path.empty()); +#endif +} + +void RealmCoordinator::wake_up_notifier_worker() +{ + if (m_notifier) { + // FIXME: this wakes up the notification workers for all processes and + // not just us. This might be worth optimizing in the future. + m_notifier->notify_others(); + } +} + +void RealmCoordinator::commit_write(Realm& realm) +{ + REALM_ASSERT(!m_config.read_only()); + REALM_ASSERT(realm.is_in_transaction()); + + { + // Need to acquire this lock before committing or another process could + // perform a write and notify us before we get the chance to set the + // skip version + std::lock_guard l(m_notifier_mutex); + + transaction::commit(*Realm::Internal::get_shared_group(realm)); + + // Don't need to check m_new_notifiers because those don't skip versions + bool have_notifiers = std::any_of(m_notifiers.begin(), m_notifiers.end(), + [&](auto&& notifier) { return notifier->is_for_realm(realm); }); + if (have_notifiers) { + m_notifier_skip_version = Realm::Internal::get_shared_group(realm)->get_version_of_current_transaction(); + } + } + +#if REALM_ENABLE_SYNC + // Realm could be closed in did_change. So send sync notification first before did_change. + if (m_sync_session) { + auto& sg = Realm::Internal::get_shared_group(realm); + auto version = LangBindHelper::get_version_of_latest_snapshot(*sg); + SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version); + } +#endif + if (realm.m_binding_context) { + realm.m_binding_context->did_change({}, {}); + } + + if (m_notifier) { + m_notifier->notify_others(); + } +} + +void RealmCoordinator::pin_version(VersionID versionid) +{ + REALM_ASSERT_DEBUG(!m_notifier_mutex.try_lock()); + if (m_async_error) { + return; + } + + if (!m_advancer_sg) { + try { + std::unique_ptr read_only_group; + Realm::open_with_config(m_config, m_advancer_history, m_advancer_sg, read_only_group, nullptr); + REALM_ASSERT(!read_only_group); + m_advancer_sg->begin_read(versionid); + } + catch (...) { + m_async_error = std::current_exception(); + m_advancer_sg = nullptr; + m_advancer_history = nullptr; + } + } + else if (m_new_notifiers.empty()) { + // If this is the first notifier then we don't already have a read transaction + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Ready); + m_advancer_sg->begin_read(versionid); + } + else { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + if (versionid < m_advancer_sg->get_version_of_current_transaction()) { + // Ensure we're holding a readlock on the oldest version we have a + // handover object for, as handover objects don't + m_advancer_sg->end_read(); + m_advancer_sg->begin_read(versionid); + } + } +} + +void RealmCoordinator::register_notifier(std::shared_ptr notifier) +{ + auto version = notifier->version(); + auto& self = Realm::Internal::get_coordinator(*notifier->get_realm()); + { + std::lock_guard lock(self.m_notifier_mutex); + self.pin_version(version); + self.m_new_notifiers.push_back(std::move(notifier)); + } +} + +void RealmCoordinator::clean_up_dead_notifiers() +{ + auto swap_remove = [&](auto& container) { + bool did_remove = false; + for (size_t i = 0; i < container.size(); ++i) { + if (container[i]->is_alive()) + continue; + + // Ensure the notifier is destroyed here even if there's lingering refs + // to the async notifier elsewhere + container[i]->release_data(); + + if (container.size() > i + 1) + container[i] = std::move(container.back()); + container.pop_back(); + --i; + did_remove = true; + } + return did_remove; + }; + + if (swap_remove(m_notifiers)) { + // Make sure we aren't holding on to read versions needlessly if there + // are no notifiers left, but don't close them entirely as opening shared + // groups is expensive + if (m_notifiers.empty() && m_notifier_sg) { + REALM_ASSERT_3(m_notifier_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + m_notifier_sg->end_read(); + m_notifier_skip_version = {0, 0}; + } + } + if (swap_remove(m_new_notifiers) && m_advancer_sg) { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + if (m_new_notifiers.empty()) { + m_advancer_sg->end_read(); + } + } +} + +void RealmCoordinator::on_change() +{ + run_async_notifiers(); + + std::lock_guard lock(m_realm_mutex); + for (auto& realm : m_weak_realm_notifiers) { + realm.notify(); + } +} + +namespace { +class IncrementalChangeInfo { +public: + IncrementalChangeInfo(SharedGroup& sg, + std::vector>& notifiers) + : m_sg(sg) + { + if (notifiers.empty()) + return; + + auto cmp = [&](auto&& lft, auto&& rgt) { + return lft->version() < rgt->version(); + }; + + // Sort the notifiers by their source version so that we can pull them + // all forward to the latest version in a single pass over the transaction log + std::sort(notifiers.begin(), notifiers.end(), cmp); + + // Preallocate the required amount of space in the vector so that we can + // safely give out pointers to within the vector + size_t count = 1; + for (auto it = notifiers.begin(), next = it + 1; next != notifiers.end(); ++it, ++next) { + if (cmp(*it, *next)) + ++count; + } + m_info.reserve(count); + m_info.resize(1); + m_current = &m_info[0]; + } + + TransactionChangeInfo& current() const { return *m_current; } + + bool advance_incremental(VersionID version) + { + if (version != m_sg.get_version_of_current_transaction()) { + transaction::advance(m_sg, *m_current, version); + m_info.push_back({ + m_current->table_modifications_needed, + m_current->table_moves_needed, + std::move(m_current->lists)}); + m_current = &m_info.back(); + return true; + } + return false; + } + + void advance_to_final(VersionID version) + { + if (!m_current) { + transaction::advance(m_sg, nullptr, version); + return; + } + + transaction::advance(m_sg, *m_current, version); + + // We now need to combine the transaction change info objects so that all of + // the notifiers see the complete set of changes from their first version to + // the most recent one + for (size_t i = m_info.size() - 1; i > 0; --i) { + auto& cur = m_info[i]; + if (cur.tables.empty()) + continue; + auto& prev = m_info[i - 1]; + if (prev.tables.empty()) { + prev.tables = cur.tables; + continue; + } + + for (size_t j = 0; j < prev.tables.size() && j < cur.tables.size(); ++j) { + prev.tables[j].merge(CollectionChangeBuilder{cur.tables[j]}); + } + prev.tables.reserve(cur.tables.size()); + while (prev.tables.size() < cur.tables.size()) { + prev.tables.push_back(cur.tables[prev.tables.size()]); + } + } + + // Copy the list change info if there are multiple LinkViews for the same LinkList + auto id = [](auto const& list) { return std::tie(list.table_ndx, list.col_ndx, list.row_ndx); }; + for (size_t i = 1; i < m_current->lists.size(); ++i) { + for (size_t j = i; j > 0; --j) { + if (id(m_current->lists[i]) == id(m_current->lists[j - 1])) { + m_current->lists[j - 1].changes->merge(CollectionChangeBuilder{*m_current->lists[i].changes}); + } + } + } + } + +private: + std::vector m_info; + TransactionChangeInfo* m_current = nullptr; + SharedGroup& m_sg; +}; +} // anonymous namespace + +void RealmCoordinator::run_async_notifiers() +{ + std::unique_lock lock(m_notifier_mutex); + + clean_up_dead_notifiers(); + + if (m_notifiers.empty() && m_new_notifiers.empty()) { + return; + } + + if (!m_async_error) { + open_helper_shared_group(); + } + + if (m_async_error) { + std::move(m_new_notifiers.begin(), m_new_notifiers.end(), std::back_inserter(m_notifiers)); + m_new_notifiers.clear(); + return; + } + + VersionID version; + + // Advance all of the new notifiers to the most recent version, if any + auto new_notifiers = std::move(m_new_notifiers); + IncrementalChangeInfo new_notifier_change_info(*m_advancer_sg, new_notifiers); + + if (!new_notifiers.empty()) { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + REALM_ASSERT_3(m_advancer_sg->get_version_of_current_transaction().version, + <=, new_notifiers.front()->version().version); + + // The advancer SG can be at an older version than the oldest new notifier + // if a notifier was added and then removed before it ever got the chance + // to run, as we don't move the pin forward when removing dead notifiers + transaction::advance(*m_advancer_sg, nullptr, new_notifiers.front()->version()); + + // Advance each of the new notifiers to the latest version, attaching them + // to the SG at their handover version. This requires a unique + // TransactionChangeInfo for each source version, so that things don't + // see changes from before the version they were handed over from. + // Each Info has all of the changes between that source version and the + // next source version, and they'll be merged together later after + // releasing the lock + for (auto& notifier : new_notifiers) { + new_notifier_change_info.advance_incremental(notifier->version()); + notifier->attach_to(*m_advancer_sg); + notifier->add_required_change_info(new_notifier_change_info.current()); + } + new_notifier_change_info.advance_to_final(VersionID{}); + + for (auto& notifier : new_notifiers) { + notifier->detach(); + } + + // We want to advance the non-new notifiers to the same version as the + // new notifiers to avoid having to merge changes from any new + // transaction that happen immediately after this into the new notifier + // changes + version = m_advancer_sg->get_version_of_current_transaction(); + m_advancer_sg->end_read(); + } + else { + // If we have no new notifiers we want to just advance to the latest + // version, but we have to pick a "latest" version while holding the + // notifier lock to avoid advancing over a transaction which should be + // skipped + m_advancer_sg->begin_read(); + version = m_advancer_sg->get_version_of_current_transaction(); + m_advancer_sg->end_read(); + } + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Ready); + + auto skip_version = m_notifier_skip_version; + m_notifier_skip_version = {0, 0}; + + // Make a copy of the notifiers vector and then release the lock to avoid + // blocking other threads trying to register or unregister notifiers while we run them + auto notifiers = m_notifiers; + m_notifiers.insert(m_notifiers.end(), new_notifiers.begin(), new_notifiers.end()); + lock.unlock(); + + if (skip_version.version) { + REALM_ASSERT(!notifiers.empty()); + REALM_ASSERT(version >= skip_version); + IncrementalChangeInfo change_info(*m_notifier_sg, notifiers); + for (auto& notifier : notifiers) + notifier->add_required_change_info(change_info.current()); + change_info.advance_to_final(skip_version); + + for (auto& notifier : notifiers) + notifier->run(); + + lock.lock(); + for (auto& notifier : notifiers) + notifier->prepare_handover(); + lock.unlock(); + } + + // Advance the non-new notifiers to the same version as we advanced the new + // ones to (or the latest if there were no new ones) + IncrementalChangeInfo change_info(*m_notifier_sg, notifiers); + for (auto& notifier : notifiers) { + notifier->add_required_change_info(change_info.current()); + } + change_info.advance_to_final(version); + + // Attach the new notifiers to the main SG and move them to the main list + for (auto& notifier : new_notifiers) { + notifier->attach_to(*m_notifier_sg); + notifier->run(); + } + + // Change info is now all ready, so the notifiers can now perform their + // background work + for (auto& notifier : notifiers) { + notifier->run(); + } + + // Reacquire the lock while updating the fields that are actually read on + // other threads + lock.lock(); + for (auto& notifier : new_notifiers) { + notifier->prepare_handover(); + } + for (auto& notifier : notifiers) { + notifier->prepare_handover(); + } + clean_up_dead_notifiers(); + m_notifier_cv.notify_all(); +} + +void RealmCoordinator::open_helper_shared_group() +{ + if (!m_notifier_sg) { + try { + std::unique_ptr read_only_group; + Realm::open_with_config(m_config, m_notifier_history, m_notifier_sg, read_only_group, nullptr); + REALM_ASSERT(!read_only_group); + m_notifier_sg->begin_read(); + } + catch (...) { + // Store the error to be passed to the async notifiers + m_async_error = std::current_exception(); + m_notifier_sg = nullptr; + m_notifier_history = nullptr; + } + } + else if (m_notifiers.empty()) { + m_notifier_sg->begin_read(); + } +} + +void RealmCoordinator::advance_to_ready(Realm& realm) +{ + std::unique_lock lock(m_notifier_mutex); + _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this); + lock.unlock(); + notifiers.package_and_wait(util::none); + + auto& sg = Realm::Internal::get_shared_group(realm); + if (notifiers) { + auto version = notifiers.version(); + if (version) { + auto current_version = sg->get_version_of_current_transaction(); + // Notifications are out of date, so just discard + // This should only happen if begin_read() was used to change the + // read version outside of our control + if (*version < current_version) + return; + // While there is a newer version, notifications are for the current + // version so just deliver them without advancing + if (*version == current_version) { + notifiers.deliver(*sg); + notifiers.after_advance(); + return; + } + } + } + + transaction::advance(sg, realm.m_binding_context.get(), notifiers); +} + +std::vector> RealmCoordinator::notifiers_for_realm(Realm& realm) +{ + std::vector> ret; + for (auto& notifier : m_new_notifiers) { + if (notifier->is_for_realm(realm)) + ret.push_back(notifier); + } + for (auto& notifier : m_notifiers) { + if (notifier->is_for_realm(realm)) + ret.push_back(notifier); + } + return ret; +} + +bool RealmCoordinator::advance_to_latest(Realm& realm) +{ + using sgf = SharedGroupFriend; + + auto& sg = Realm::Internal::get_shared_group(realm); + std::unique_lock lock(m_notifier_mutex); + _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this); + lock.unlock(); + notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(*sg)); + + auto version = sg->get_version_of_current_transaction(); + transaction::advance(sg, realm.m_binding_context.get(), notifiers); + + // Realm could be closed in the callbacks. + if (realm.is_closed()) + return false; + + return version != sg->get_version_of_current_transaction(); +} + +void RealmCoordinator::promote_to_write(Realm& realm) +{ + REALM_ASSERT(!realm.is_in_transaction()); + + std::unique_lock lock(m_notifier_mutex); + _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this); + lock.unlock(); + + auto& sg = Realm::Internal::get_shared_group(realm); + transaction::begin(sg, realm.m_binding_context.get(), notifiers); +} + +void RealmCoordinator::process_available_async(Realm& realm) +{ + REALM_ASSERT(!realm.is_in_transaction()); + + std::unique_lock lock(m_notifier_mutex); + auto notifiers = notifiers_for_realm(realm); + if (notifiers.empty()) + return; + + if (auto error = m_async_error) { + lock.unlock(); + for (auto& notifier : notifiers) + notifier->deliver_error(m_async_error); + return; + } + + bool in_read = realm.is_in_read_transaction(); + auto& sg = Realm::Internal::get_shared_group(realm); + auto version = sg->get_version_of_current_transaction(); + auto package = [&](auto& notifier) { + return !(notifier->has_run() && (!in_read || notifier->version() == version) && notifier->package_for_delivery()); + }; + notifiers.erase(std::remove_if(begin(notifiers), end(notifiers), package), end(notifiers)); + lock.unlock(); + + // no before advance because the Realm is already at the given version, + // because we're either sending initial notifications or the write was + // done on this Realm instance + + // Skip delivering if the Realm isn't in a read transaction + if (in_read) { + for (auto& notifier : notifiers) + notifier->deliver(*sg); + } + + // but still call the change callbacks + for (auto& notifier : notifiers) + notifier->after_advance(); +} + +void RealmCoordinator::set_transaction_callback(std::function fn) +{ + m_transaction_callback = std::move(fn); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp new file mode 100644 index 0000000..04e098e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp @@ -0,0 +1,234 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/results_notifier.hpp" + +using namespace realm; +using namespace realm::_impl; + +ResultsNotifier::ResultsNotifier(Results& target) +: CollectionNotifier(target.get_realm()) +, m_target_results(&target) +, m_target_is_in_table_order(target.is_in_table_order()) +{ + Query q = target.get_query(); + set_table(*q.get_table()); + m_query_handover = Realm::Internal::get_shared_group(*get_realm())->export_for_handover(q, MutableSourcePayload::Move); + SortDescriptor::generate_patch(target.get_sort(), m_sort_handover); + SortDescriptor::generate_patch(target.get_distinct(), m_distinct_handover); +} + +void ResultsNotifier::target_results_moved(Results& old_target, Results& new_target) +{ + auto lock = lock_target(); + + REALM_ASSERT(m_target_results == &old_target); + m_target_results = &new_target; +} + +void ResultsNotifier::release_data() noexcept +{ + m_query = nullptr; +} + +// Most of the inter-thread synchronization for run(), prepare_handover(), +// attach_to(), detach(), release_data() and deliver() is done by +// RealmCoordinator external to this code, which has some potentially +// non-obvious results on which members are and are not safe to use without +// holding a lock. +// +// add_required_change_info(), attach_to(), detach(), run(), +// prepare_handover(), and release_data() are all only ever called on a single +// background worker thread. call_callbacks() and deliver() are called on the +// target thread. Calls to prepare_handover() and deliver() are guarded by a +// lock. +// +// In total, this means that the safe data flow is as follows: +// - add_Required_change_info(), prepare_handover(), attach_to(), detach() and +// release_data() can read members written by each other +// - deliver() can read members written to in prepare_handover(), deliver(), +// and call_callbacks() +// - call_callbacks() and read members written to in deliver() +// +// Separately from the handover data flow, m_target_results is guarded by the target lock + +bool ResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(m_query); + m_info = &info; + + auto table_ndx = m_query->get_table()->get_index_in_group(); + if (info.table_moves_needed.size() <= table_ndx) + info.table_moves_needed.resize(table_ndx + 1); + info.table_moves_needed[table_ndx] = true; + + return has_run() && have_callbacks(); +} + +bool ResultsNotifier::need_to_run() +{ + REALM_ASSERT(m_info); + REALM_ASSERT(!m_tv.is_attached()); + + { + auto lock = lock_target(); + // Don't run the query if the results aren't actually going to be used + if (!get_realm() || (!have_callbacks() && !m_target_results->wants_background_updates())) { + return false; + } + } + + // If we've run previously, check if we need to rerun + if (has_run() && m_query->sync_view_if_needed() == m_last_seen_version) { + return false; + } + + return true; +} + +void ResultsNotifier::calculate_changes() +{ + size_t table_ndx = m_query->get_table()->get_index_in_group(); + if (has_run()) { + auto changes = table_ndx < m_info->tables.size() ? &m_info->tables[table_ndx] : nullptr; + + std::vector next_rows; + next_rows.reserve(m_tv.size()); + for (size_t i = 0; i < m_tv.size(); ++i) + next_rows.push_back(m_tv[i].get_index()); + + util::Optional move_candidates; + if (changes) { + auto const& moves = changes->moves; + for (auto& idx : m_previous_rows) { + auto it = lower_bound(begin(moves), end(moves), idx, + [](auto const& a, auto b) { return a.from < b; }); + if (it != moves.end() && it->from == idx) + idx = it->to; + else if (changes->deletions.contains(idx)) + idx = npos; + else + REALM_ASSERT_DEBUG(!changes->insertions.contains(idx)); + } + if (m_target_is_in_table_order && !m_sort) + move_candidates = changes->insertions; + } + + m_changes = CollectionChangeBuilder::calculate(m_previous_rows, next_rows, + get_modification_checker(*m_info, *m_query->get_table()), + move_candidates); + + m_previous_rows = std::move(next_rows); + } + else { + m_previous_rows.resize(m_tv.size()); + for (size_t i = 0; i < m_tv.size(); ++i) + m_previous_rows[i] = m_tv[i].get_index(); + } +} + +void ResultsNotifier::run() +{ + if (!need_to_run()) + return; + + m_query->sync_view_if_needed(); + m_tv = m_query->find_all(); + if (m_sort) { + m_tv.sort(m_sort); + } + if (m_distinct) { + m_tv.distinct(m_distinct); + } + m_last_seen_version = m_tv.sync_if_needed(); + + calculate_changes(); +} + +void ResultsNotifier::do_prepare_handover(SharedGroup& sg) +{ + if (!m_tv.is_attached()) { + // if the table version didn't change we can just reuse the same handover + // object and bump its version to the current SG version + if (m_tv_handover) + m_tv_handover->version = sg.get_version_of_current_transaction(); + + // add_changes() needs to be called even if there are no changes to + // clear the skip flag on the callbacks + add_changes({}); + return; + } + + REALM_ASSERT(m_tv.is_in_sync()); + + m_tv_handover = sg.export_for_handover(m_tv, MutableSourcePayload::Move); + + add_changes(std::move(m_changes)); + REALM_ASSERT(m_changes.empty()); + + // detach the TableView as we won't need it again and keeping it around + // makes advance_read() much more expensive + m_tv = {}; +} + +void ResultsNotifier::deliver(SharedGroup& sg) +{ + auto lock = lock_target(); + + // Target realm being null here indicates that we were unregistered while we + // were in the process of advancing the Realm version and preparing for + // delivery, i.e. the results was destroyed from the "wrong" thread + if (!get_realm()) { + return; + } + + REALM_ASSERT(!m_query_handover); + if (m_tv_to_deliver) { + Results::Internal::set_table_view(*m_target_results, + std::move(*sg.import_from_handover(std::move(m_tv_to_deliver)))); + } + REALM_ASSERT(!m_tv_to_deliver); +} + +bool ResultsNotifier::prepare_to_deliver() +{ + auto lock = lock_target(); + if (!get_realm()) + return false; + m_tv_to_deliver = std::move(m_tv_handover); + return true; +} + +void ResultsNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_query_handover); + m_query = sg.import_from_handover(std::move(m_query_handover)); + m_sort = SortDescriptor::create_from_and_consume_patch(m_sort_handover, *m_query->get_table()); + m_distinct = SortDescriptor::create_from_and_consume_patch(m_distinct_handover, *m_query->get_table()); +} + +void ResultsNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(m_query); + REALM_ASSERT(!m_tv.is_attached()); + + SortDescriptor::generate_patch(m_sort, m_sort_handover); + SortDescriptor::generate_patch(m_distinct, m_distinct_handover); + m_query_handover = sg.export_for_handover(*m_query, MutableSourcePayload::Move); + m_query = nullptr; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp new file mode 100644 index 0000000..7490633 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp @@ -0,0 +1,748 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/transact_log_handler.hpp" + +#include "binding_context.hpp" +#include "impl/collection_notifier.hpp" +#include "index_set.hpp" +#include "shared_realm.hpp" + +#include +#include + +#include +#include + +using namespace realm; + +namespace { + +class KVOAdapter : public _impl::TransactionChangeInfo { +public: + KVOAdapter(std::vector& observers, BindingContext* context); + + void before(SharedGroup& sg); + void after(SharedGroup& sg); + +private: + BindingContext* m_context; + std::vector& m_observers; + std::vector m_invalidated; + + struct ListInfo { + BindingContext::ObserverState* observer; + size_t col; + _impl::CollectionChangeBuilder builder; + }; + std::vector m_lists; + VersionID m_version; + + size_t new_table_ndx(size_t ndx) const { return ndx < table_indices.size() ? table_indices[ndx] : ndx; } +}; + +KVOAdapter::KVOAdapter(std::vector& observers, BindingContext* context) +: _impl::TransactionChangeInfo{} +, m_context(context) +, m_observers(observers) +{ + if (m_observers.empty()) + return; + + std::vector tables_needed; + for (auto& observer : observers) { + tables_needed.push_back(observer.table_ndx); + } + std::sort(begin(tables_needed), end(tables_needed)); + tables_needed.erase(std::unique(begin(tables_needed), end(tables_needed)), end(tables_needed)); + + auto realm = context->realm.lock(); + auto& group = realm->read_group(); + for (auto& observer : observers) { + auto table = group.get_table(observer.table_ndx); + for (size_t i = 0, count = table->get_column_count(); i < count; ++i) { + if (table->get_column_type(i) != type_LinkList) + continue; + m_lists.push_back({&observer, i, {}}); + } + } + + auto max = std::max_element(begin(tables_needed), end(tables_needed)); + if (*max >= table_modifications_needed.size()) + table_modifications_needed.resize(*max + 1, false); + if (*max >= table_moves_needed.size()) + table_moves_needed.resize(*max + 1, false); + for (auto& tbl : tables_needed) { + table_modifications_needed[tbl] = true; + table_moves_needed[tbl] = true; + } + for (auto& list : m_lists) + lists.push_back({list.observer->table_ndx, list.observer->row_ndx, list.col, &list.builder}); +} + +void KVOAdapter::before(SharedGroup& sg) +{ + if (!m_context) + return; + + m_version = sg.get_version_of_current_transaction(); + if (tables.empty()) + return; + + for (auto& observer : m_observers) { + size_t table_ndx = new_table_ndx(observer.table_ndx); + if (table_ndx >= tables.size()) + continue; + + auto const& table = tables[table_ndx]; + auto const& moves = table.moves; + auto idx = observer.row_ndx; + auto it = lower_bound(begin(moves), end(moves), idx, + [](auto const& a, auto b) { return a.from < b; }); + if (it != moves.end() && it->from == idx) + idx = it->to; + else if (table.deletions.contains(idx)) { + m_invalidated.push_back(observer.info); + continue; + } + else + idx = table.insertions.shift(table.deletions.unshift(idx)); + if (table.modifications.contains(idx)) { + observer.changes.resize(table.columns.size()); + size_t i = 0; + for (auto& c : table.columns) { + auto& change = observer.changes[i]; + if (table_ndx >= column_indices.size() || column_indices[table_ndx].empty()) + change.initial_column_index = i; + else if (i >= column_indices[table_ndx].size()) + change.initial_column_index = i - column_indices[table_ndx].size() + column_indices[table_ndx].back() + 1; + else + change.initial_column_index = column_indices[table_ndx][i]; + if (change.initial_column_index != npos && c.contains(idx)) + change.kind = BindingContext::ColumnInfo::Kind::Set; + ++i; + } + } + } + + for (auto& list : m_lists) { + if (list.builder.empty()) + continue; + // If the containing row was deleted then changes will be empty + if (list.observer->changes.empty()) { + REALM_ASSERT_DEBUG(tables[new_table_ndx(list.observer->table_ndx)].deletions.contains(list.observer->row_ndx)); + continue; + } + // otherwise the column should have been marked as modified + REALM_ASSERT(list.col < list.observer->changes.size()); + auto& builder = list.builder; + auto& changes = list.observer->changes[list.col]; + + // KVO can't express moves (becaue NSArray doesn't have them), so + // transform them into a series of sets on each affected index when possible + if (!builder.insertions.empty() && builder.insertions.count() == builder.moves.size()) { + changes.kind = BindingContext::ColumnInfo::Kind::Set; + changes.indices = builder.modifications; + + size_t start = std::min(builder.insertions.begin()->first, builder.deletions.begin()->first); + size_t end = std::max(std::prev(builder.insertions.end())->second, + std::prev(builder.deletions.end())->second); + for (size_t i = start; i < end; ++i) + changes.indices.add(i); + } + // KVO can't express multiple types of changes at once + else if (builder.insertions.empty() + builder.modifications.empty() + builder.deletions.empty() < 2) { + changes.kind = BindingContext::ColumnInfo::Kind::SetAll; + } + else if (!builder.insertions.empty()) { + changes.kind = BindingContext::ColumnInfo::Kind::Insert; + changes.indices = builder.insertions; + } + else if (!builder.modifications.empty()) { + changes.kind = BindingContext::ColumnInfo::Kind::Set; + changes.indices = builder.modifications; + } + else { + REALM_ASSERT(!builder.deletions.empty()); + changes.kind = BindingContext::ColumnInfo::Kind::Remove; + changes.indices = builder.deletions; + } + } + m_context->will_change(m_observers, m_invalidated); +} + +void KVOAdapter::after(SharedGroup& sg) +{ + if (!m_context) + return; + m_context->did_change(m_observers, m_invalidated, + m_version != VersionID{} && + m_version != sg.get_version_of_current_transaction()); +} + +template +struct MarkDirtyMixin { + bool mark_dirty(size_t row, size_t col, _impl::Instruction instr=_impl::instr_Set) + { + // Ignore SetDefault and SetUnique as those conceptually cannot be + // changes to existing rows + if (instr == _impl::instr_Set) + static_cast(this)->mark_dirty(row, col); + return true; + } + + bool set_int(size_t c, size_t r, int_fast64_t, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + bool set_bool(size_t c, size_t r, bool, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_float(size_t c, size_t r, float, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_double(size_t c, size_t r, double, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_string(size_t c, size_t r, StringData, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + bool set_binary(size_t c, size_t r, BinaryData, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_olddatetime(size_t c, size_t r, OldDateTime, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_timestamp(size_t c, size_t r, Timestamp, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_table(size_t c, size_t r, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_mixed(size_t c, size_t r, const Mixed&, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_link(size_t c, size_t r, size_t, size_t, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_null(size_t c, size_t r, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + + bool add_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } + bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } + bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); } + bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); } + + bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; } + bool set_string_unique(size_t, size_t, size_t, StringData) { return true; } +}; + +class TransactLogValidationMixin { + // Index of currently selected table + size_t m_current_table = 0; + + REALM_NORETURN + REALM_NOINLINE + void schema_error() + { + throw std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way"); + } + +protected: + size_t current_table() const noexcept { return m_current_table; } + +public: + bool select_descriptor(int levels, const size_t*) + { + // subtables not supported + return levels == 0; + } + + bool select_table(size_t group_level_ndx, int, const size_t*) noexcept + { + m_current_table = group_level_ndx; + return true; + } + + // Removing or renaming things while a Realm is open is never supported + bool erase_group_level_table(size_t, size_t) { schema_error(); } + bool rename_group_level_table(size_t, StringData) { schema_error(); } + bool erase_column(size_t) { schema_error(); } + bool erase_link_column(size_t, size_t, size_t) { schema_error(); } + bool rename_column(size_t, StringData) { schema_error(); } + + // Schema changes which don't involve a change in the schema version are + // allowed + bool add_search_index(size_t) { return true; } + bool remove_search_index(size_t) { return true; } + + // Additive changes and reorderings are supported + bool insert_group_level_table(size_t, size_t, StringData) { return true; } + bool insert_column(size_t, DataType, StringData, bool) { return true; } + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return true; } + bool set_link_type(size_t, LinkType) { return true; } + bool move_column(size_t, size_t) { return true; } + bool move_group_level_table(size_t, size_t) { return true; } + + // Non-schema changes are all allowed + void parse_complete() { } + bool select_link_list(size_t, size_t, size_t) { return true; } + bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; } + bool erase_rows(size_t, size_t, size_t, bool) { return true; } + bool swap_rows(size_t, size_t) { return true; } + bool clear_table() noexcept { return true; } + bool link_list_set(size_t, size_t, size_t) { return true; } + bool link_list_insert(size_t, size_t, size_t) { return true; } + bool link_list_erase(size_t, size_t) { return true; } + bool link_list_nullify(size_t, size_t) { return true; } + bool link_list_clear(size_t) { return true; } + bool link_list_move(size_t, size_t) { return true; } + bool link_list_swap(size_t, size_t) { return true; } + bool merge_rows(size_t, size_t) { return true; } + bool optimize_table() { return true; } +}; + + +// A transaction log handler that just validates that all operations made are +// ones supported by the object store +struct TransactLogValidator : public TransactLogValidationMixin, public MarkDirtyMixin { + void mark_dirty(size_t, size_t) { } +}; + +// Move the value at container[from] to container[to], shifting everything in +// between, or do nothing if either are out of bounds +template +void rotate(Container& container, size_t from, size_t to) +{ + REALM_ASSERT(from != to); + if (from >= container.size() && to >= container.size()) + return; + if (from >= container.size() || to >= container.size()) + container.resize(std::max(from, to) + 1); + if (from < to) + std::rotate(begin(container) + from, begin(container) + from + 1, begin(container) + to + 1); + else + std::rotate(begin(container) + to, begin(container) + from, begin(container) + from + 1); +} + +// Insert a default-initialized value at pos if there is anything after pos in the container. +template +void insert_empty_at(Container& container, size_t pos) +{ + if (pos < container.size()) + container.emplace(container.begin() + pos); +} + +// Shift `value` to reflect a move from `from` to `to` +void adjust_for_move(size_t& value, size_t from, size_t to) +{ + if (value == from) + value = to; + else if (value > from && value <= to) + --value; + else if (value < from && value >= to) + ++value; +} + +void adjust_for_move(std::vector& values, size_t from, size_t to) +{ + for (auto& value : values) + adjust_for_move(value, from, to); +} + +void expand_to(std::vector& cols, size_t i) +{ + auto old_size = cols.size(); + if (old_size > i) + return; + + cols.resize(std::max(old_size * 2, i + 1)); + std::iota(begin(cols) + old_size, end(cols), old_size == 0 ? 0 : cols[old_size - 1] + 1); +} + +void adjust_ge(std::vector& values, size_t i) +{ + for (auto& value : values) { + if (value >= i) + ++value; + } +} + +// Extends TransactLogValidator to track changes made to LinkViews +class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin { + _impl::TransactionChangeInfo& m_info; + _impl::CollectionChangeBuilder* m_active = nullptr; + + _impl::CollectionChangeBuilder* get_change() + { + auto tbl_ndx = current_table(); + if (!m_info.track_all && (tbl_ndx >= m_info.table_modifications_needed.size() || !m_info.table_modifications_needed[tbl_ndx])) + return nullptr; + if (m_info.tables.size() <= tbl_ndx) { + m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1)); + } + return &m_info.tables[tbl_ndx]; + } + + bool need_move_info() const + { + auto tbl_ndx = current_table(); + return m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() && m_info.table_moves_needed[tbl_ndx]); + } + + +public: + TransactLogObserver(_impl::TransactionChangeInfo& info) + : m_info(info) { } + + void mark_dirty(size_t row, size_t col) + { + if (auto change = get_change()) + change->modify(row, col); + } + + void parse_complete() + { + for (auto& table : m_info.tables) { + table.parse_complete(); + } + for (auto& list : m_info.lists) { + list.changes->clean_up_stale_moves(); + } + } + + bool select_link_list(size_t col, size_t row, size_t) + { + mark_dirty(row, col); + + m_active = nullptr; + // When there are multiple source versions there could be multiple + // change objects for a single LinkView, in which case we need to use + // the last one + for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) { + if (it->table_ndx == current_table() && it->row_ndx == row && it->col_ndx == col) { + m_active = it->changes; + break; + } + } + return true; + } + + bool link_list_set(size_t index, size_t, size_t) + { + if (m_active) + m_active->modify(index); + return true; + } + + bool link_list_insert(size_t index, size_t, size_t) + { + if (m_active) + m_active->insert(index); + return true; + } + + bool link_list_erase(size_t index, size_t) + { + if (m_active) + m_active->erase(index); + return true; + } + + bool link_list_nullify(size_t index, size_t prior_size) + { + return link_list_erase(index, prior_size); + } + + bool link_list_swap(size_t index1, size_t index2) + { + link_list_set(index1, 0, npos); + link_list_set(index2, 0, npos); + return true; + } + + bool link_list_clear(size_t old_size) + { + if (m_active) + m_active->clear(old_size); + return true; + } + + bool link_list_move(size_t from, size_t to) + { + if (m_active) + m_active->move(from, to); + return true; + } + + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t, bool) + { + if (auto change = get_change()) + change->insert(row_ndx, num_rows_to_insert, need_move_info()); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.row_ndx >= row_ndx) + list.row_ndx += num_rows_to_insert; + } + return true; + } + + bool erase_rows(size_t row_ndx, size_t, size_t prior_num_rows, bool unordered) + { + if (!unordered) { + if (auto change = get_change()) + change->deletions.add(row_ndx); + return true; + } + REALM_ASSERT(unordered); + size_t last_row = prior_num_rows - 1; + + for (size_t i = 0; i < m_info.lists.size(); ++i) { + auto& list = m_info.lists[i]; + if (list.table_ndx != current_table()) + continue; + if (list.row_ndx == row_ndx) { + if (i + 1 < m_info.lists.size()) + m_info.lists[i] = std::move(m_info.lists.back()); + m_info.lists.pop_back(); + continue; + } + if (list.row_ndx == last_row) + list.row_ndx = row_ndx; + } + + if (auto change = get_change()) + change->move_over(row_ndx, last_row, need_move_info()); + return true; + } + + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) { + REALM_ASSERT(row_ndx_1 < row_ndx_2); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table()) { + if (list.row_ndx == row_ndx_1) + list.row_ndx = row_ndx_2; + else if (list.row_ndx == row_ndx_2) + list.row_ndx = row_ndx_1; + } + } + if (auto change = get_change()) + change->swap(row_ndx_1, row_ndx_2, need_move_info()); + return true; + } + + bool merge_rows(size_t from, size_t to) + { + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.row_ndx == from) + list.row_ndx = to; + } + if (auto change = get_change()) + change->subsume(from, to, need_move_info()); + return true; + } + + bool clear_table() + { + auto tbl_ndx = current_table(); + auto it = remove_if(begin(m_info.lists), end(m_info.lists), + [&](auto const& lv) { return lv.table_ndx == tbl_ndx; }); + m_info.lists.erase(it, end(m_info.lists)); + if (auto change = get_change()) + change->clear(std::numeric_limits::max()); + return true; + } + + bool insert_column(size_t ndx, DataType, StringData, bool) + { + if (auto change = get_change()) + change->insert_column(ndx); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.col_ndx >= ndx) + ++list.col_ndx; + } + if (m_info.column_indices.size() <= current_table()) + m_info.column_indices.resize(current_table() + 1); + auto& indices = m_info.column_indices[current_table()]; + expand_to(indices, ndx); + insert_empty_at(indices, ndx); + indices[ndx] = npos; + return true; + } + + void prepare_table_indices() + { + if (m_info.table_indices.empty() && !m_info.table_modifications_needed.empty()) { + m_info.table_indices.resize(m_info.table_modifications_needed.size()); + std::iota(begin(m_info.table_indices), end(m_info.table_indices), 0); + } + } + + bool insert_group_level_table(size_t ndx, size_t, StringData) + { + for (auto& list : m_info.lists) { + if (list.table_ndx >= ndx) + ++list.table_ndx; + } + prepare_table_indices(); + adjust_ge(m_info.table_indices, ndx); + insert_empty_at(m_info.tables, ndx); + insert_empty_at(m_info.table_moves_needed, ndx); + insert_empty_at(m_info.table_modifications_needed, ndx); + return true; + } + + bool move_column(size_t from, size_t to) + { + if (auto change = get_change()) + change->move_column(from, to); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table()) + adjust_for_move(list.col_ndx, from, to); + } + if (m_info.column_indices.size() <= current_table()) + m_info.column_indices.resize(current_table() + 1); + expand_to(m_info.column_indices[current_table()], std::max(from, to) + 1); + rotate(m_info.column_indices[current_table()], from, to); + return true; + } + + bool move_group_level_table(size_t from, size_t to) + { + for (auto& list : m_info.lists) + adjust_for_move(list.table_ndx, from, to); + + prepare_table_indices(); + adjust_for_move(m_info.table_indices, from, to); + rotate(m_info.tables, from, to); + rotate(m_info.table_modifications_needed, from, to); + rotate(m_info.table_moves_needed, from, to); + return true; + } + + bool insert_link_column(size_t ndx, DataType type, StringData name, size_t, size_t) { return insert_column(ndx, type, name, false); } +}; + +class KVOTransactLogObserver : public TransactLogObserver { + KVOAdapter m_adapter; + _impl::NotifierPackage& m_notifiers; + SharedGroup& m_sg; + +public: + KVOTransactLogObserver(std::vector& observers, + BindingContext* context, + _impl::NotifierPackage& notifiers, + SharedGroup& sg) + : TransactLogObserver(m_adapter) + , m_adapter(observers, context) + , m_notifiers(notifiers) + , m_sg(sg) + { + } + + ~KVOTransactLogObserver() + { + m_adapter.after(m_sg); + } + + void parse_complete() + { + TransactLogObserver::parse_complete(); + m_adapter.before(m_sg); + + using sgf = _impl::SharedGroupFriend; + m_notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(m_sg)); + m_notifiers.before_advance(); + } +}; + +template +void advance_with_notifications(BindingContext* context, const std::unique_ptr& sg, Func&& func, + _impl::NotifierPackage& notifiers) +{ + auto old_version = sg->get_version_of_current_transaction(); + std::vector observers; + if (context) { + observers = context->get_observed_rows(); + } + + // Advancing to the latest version with notifiers requires using the full + // transaction log observer so that we have a point where we know what + // version we're going to before we actually advance to that version + if (observers.empty() && (!notifiers || notifiers.version())) { + notifiers.before_advance(); + func(TransactLogValidator()); + auto new_version = sg->get_version_of_current_transaction(); + if (context && old_version != new_version) + context->did_change({}, {}); + // did_change() could close the Realm. Just return if it does. + if (!sg) + return; + // did_change() can change the read version, and if it does we can't + // deliver notifiers + if (new_version == sg->get_version_of_current_transaction()) + notifiers.deliver(*sg); + notifiers.after_advance(); + return; + } + + func(KVOTransactLogObserver(observers, context, notifiers, *sg)); + notifiers.package_and_wait(sg->get_version_of_current_transaction().version); // is a no-op if parse_complete() was called + notifiers.deliver(*sg); + notifiers.after_advance(); +} + +} // anonymous namespace + +namespace realm { +namespace _impl { + +namespace transaction { +void advance(SharedGroup& sg, BindingContext*, VersionID version) +{ + LangBindHelper::advance_read(sg, TransactLogValidator(), version); +} + +void advance(const std::unique_ptr& sg, BindingContext* context, NotifierPackage& notifiers) +{ + advance_with_notifications(context, sg, [&](auto&&... args) { + LangBindHelper::advance_read(*sg, std::move(args)..., notifiers.version().value_or(VersionID{})); + }, notifiers); +} + +void begin_without_validation(SharedGroup& sg) +{ + LangBindHelper::promote_to_write(sg); +} + +void begin(const std::unique_ptr& sg, BindingContext* context, NotifierPackage& notifiers) +{ + advance_with_notifications(context, sg, [&](auto&&... args) { + LangBindHelper::promote_to_write(*sg, std::move(args)...); + }, notifiers); +} + +void commit(SharedGroup& sg) +{ + LangBindHelper::commit_and_continue_as_read(sg); +} + +void cancel(SharedGroup& sg, BindingContext* context) +{ + std::vector observers; + if (context) { + observers = context->get_observed_rows(); + } + if (observers.empty()) { + LangBindHelper::rollback_and_continue_as_read(sg); + return; + } + + _impl::NotifierPackage notifiers; + LangBindHelper::rollback_and_continue_as_read(sg, KVOTransactLogObserver(observers, context, notifiers, sg)); +} + +void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version) +{ + if (!info.track_all && info.table_modifications_needed.empty() && info.lists.empty()) { + LangBindHelper::advance_read(sg, version); + } + else { + LangBindHelper::advance_read(sg, TransactLogObserver(info), version); + } + +} + +} // namespace transaction +} // namespace _impl +} // namespace realm diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp new file mode 100644 index 0000000..1620eea --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/weak_realm_notifier.hpp" + +#include "shared_realm.hpp" +#include "util/event_loop_signal.hpp" + +using namespace realm; +using namespace realm::_impl; + + +WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr& realm, bool cache) +: m_realm(realm) +, m_execution_context(realm->config().execution_context) +, m_realm_key(realm.get()) +, m_cache(cache) +, m_signal(std::make_shared>(Callback{realm})) +{ +} + +WeakRealmNotifier::~WeakRealmNotifier() = default; + +void WeakRealmNotifier::Callback::operator()() const +{ + if (auto realm = weak_realm.lock()) { + realm->notify(); + } +} + +void WeakRealmNotifier::notify() +{ + m_signal->notify(); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp new file mode 100644 index 0000000..683f886 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp @@ -0,0 +1,707 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "index_set.hpp" + +#include + +#include + +using namespace realm; +using namespace realm::_impl; + +const size_t IndexSet::npos; + +template +void MutableChunkedRangeVectorIterator::set(size_t front, size_t back) +{ + this->m_outer->count -= this->m_inner->second - this->m_inner->first; + if (this->offset() == 0) { + this->m_outer->begin = front; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end = back; + } + this->m_outer->count += back - front; + this->m_inner->first = front; + this->m_inner->second = back; +} + +template +void MutableChunkedRangeVectorIterator::adjust(ptrdiff_t front, ptrdiff_t back) +{ + if (this->offset() == 0) { + this->m_outer->begin += front; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end += back; + } + this->m_outer->count += -front + back; + this->m_inner->first += front; + this->m_inner->second += back; +} + +template +void MutableChunkedRangeVectorIterator::shift(ptrdiff_t distance) +{ + if (this->offset() == 0) { + this->m_outer->begin += distance; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end += distance; + } + this->m_inner->first += distance; + this->m_inner->second += distance; +} + +void ChunkedRangeVector::push_back(value_type value) +{ + if (!empty() && m_data.back().data.size() < max_size) { + auto& range = m_data.back(); + REALM_ASSERT(range.end <= value.first); + + range.data.push_back(value); + range.count += value.second - value.first; + range.end = value.second; + } + else { + m_data.push_back({{std::move(value)}, value.first, value.second, value.second - value.first}); + } + verify(); +} + +ChunkedRangeVector::iterator ChunkedRangeVector::insert(iterator pos, value_type value) +{ + if (pos.m_outer == m_data.end()) { + push_back(std::move(value)); + return std::prev(end()); + } + + pos = ensure_space(pos); + auto& chunk = *pos.m_outer; + pos.m_inner = &*chunk.data.insert(pos.m_outer->data.begin() + pos.offset(), value); + chunk.count += value.second - value.first; + chunk.begin = std::min(chunk.begin, value.first); + chunk.end = std::max(chunk.end, value.second); + + verify(); + return pos; +} + +ChunkedRangeVector::iterator ChunkedRangeVector::ensure_space(iterator pos) +{ + if (pos.m_outer->data.size() + 1 <= max_size) + return pos; + + auto offset = pos.offset(); + + // Split the chunk in half to make space for the new insertion + auto new_pos = m_data.insert(pos.m_outer + 1, Chunk{}); + auto prev = new_pos - 1; + auto to_move = max_size / 2; + new_pos->data.reserve(to_move); + new_pos->data.assign(prev->data.end() - to_move, prev->data.end()); + prev->data.resize(prev->data.size() - to_move); + + size_t moved_count = 0; + for (auto range : new_pos->data) + moved_count += range.second - range.first; + + prev->end = prev->data.back().second; + prev->count -= moved_count; + new_pos->begin = new_pos->data.front().first; + new_pos->end = new_pos->data.back().second; + new_pos->count = moved_count; + + if (offset >= to_move) { + pos.m_outer = new_pos; + offset -= to_move; + } + else { + pos.m_outer = prev; + } + pos.m_end = m_data.end(); + pos.m_inner = &pos.m_outer->data[offset]; + verify(); + return pos; +} + +ChunkedRangeVector::iterator ChunkedRangeVector::erase(iterator pos) noexcept +{ + auto offset = pos.offset(); + auto& chunk = *pos.m_outer; + chunk.count -= pos->second - pos->first; + chunk.data.erase(chunk.data.begin() + offset); + + if (chunk.data.size() == 0) { + pos.m_outer = m_data.erase(pos.m_outer); + pos.m_end = m_data.end(); + pos.m_inner = pos.m_outer == m_data.end() ? nullptr : &pos.m_outer->data.front(); + verify(); + return pos; + } + + chunk.begin = chunk.data.front().first; + chunk.end = chunk.data.back().second; + if (offset < chunk.data.size()) + pos.m_inner = &chunk.data[offset]; + else { + ++pos.m_outer; + pos.m_inner = pos.m_outer == pos.m_end ? nullptr : &pos.m_outer->data.front(); + } + + verify(); + return pos; +} + +void ChunkedRangeVector::verify() const noexcept +{ +#ifdef REALM_DEBUG + size_t prev_end = -1; + for (auto range : *this) { + REALM_ASSERT(range.first < range.second); + REALM_ASSERT(prev_end == size_t(-1) || range.first > prev_end); + prev_end = range.second; + } + + for (auto& chunk : m_data) { + REALM_ASSERT(!chunk.data.empty()); + REALM_ASSERT(chunk.data.front().first == chunk.begin); + REALM_ASSERT(chunk.data.back().second == chunk.end); + REALM_ASSERT(chunk.count <= chunk.end - chunk.begin); + size_t count = 0; + for (auto range : chunk.data) + count += range.second - range.first; + REALM_ASSERT(count == chunk.count); + } +#endif +} + +namespace { +class ChunkedRangeVectorBuilder { +public: + using value_type = std::pair; + + ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected); + void push_back(size_t index); + void push_back(std::pair range); + std::vector finalize(); +private: + std::vector m_data; + size_t m_outer_pos = 0; +}; + +ChunkedRangeVectorBuilder::ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected) +{ + size_t size = 0; + for (auto const& chunk : expected.m_data) + size += chunk.data.size(); + m_data.resize(size / ChunkedRangeVector::max_size + 1); + for (size_t i = 0; i < m_data.size() - 1; ++i) + m_data[i].data.reserve(ChunkedRangeVector::max_size); +} + +void ChunkedRangeVectorBuilder::push_back(size_t index) +{ + push_back({index, index + 1}); +} + +void ChunkedRangeVectorBuilder::push_back(std::pair range) +{ + auto& chunk = m_data[m_outer_pos]; + if (chunk.data.empty()) { + chunk.data.push_back(range); + chunk.count = range.second - range.first; + chunk.begin = range.first; + } + else if (range.first == chunk.data.back().second) { + chunk.data.back().second = range.second; + chunk.count += range.second - range.first; + } + else if (chunk.data.size() < ChunkedRangeVector::max_size) { + chunk.data.push_back(range); + chunk.count += range.second - range.first; + } + else { + chunk.end = chunk.data.back().second; + ++m_outer_pos; + if (m_outer_pos >= m_data.size()) + m_data.push_back({{range}, range.first, 0, 1}); + else { + auto& chunk = m_data[m_outer_pos]; + chunk.data.push_back(range); + chunk.begin = range.first; + chunk.count = range.second - range.first; + } + } +} + +std::vector ChunkedRangeVectorBuilder::finalize() +{ + if (!m_data.empty()) { + m_data.resize(m_outer_pos + 1); + if (m_data.back().data.empty()) + m_data.pop_back(); + else + m_data.back().end = m_data.back().data.back().second; + } + return std::move(m_data); +} +} + +IndexSet::IndexSet(std::initializer_list values) +{ + for (size_t v : values) + add(v); +} + +bool IndexSet::contains(size_t index) const noexcept +{ + auto it = const_cast(this)->find(index); + return it != end() && it->first <= index; +} + +size_t IndexSet::count(size_t start_index, size_t end_index) const noexcept +{ + auto it = const_cast(this)->find(start_index); + const auto end = this->end(); + if (it == end || it->first >= end_index) { + return 0; + } + if (it->second >= end_index) + return std::min(it->second, end_index) - std::max(it->first, start_index); + + size_t ret = 0; + + if (start_index > it->first || it.offset() != 0) { + // Start index is in the middle of a chunk, so start by counting the + // rest of that chunk + ret = it->second - std::max(it->first, start_index); + for (++it; it != end && it->second < end_index && it.offset() != 0; ++it) { + ret += it->second - it->first; + } + if (it != end && it->first < end_index && it.offset() != 0) + ret += end_index - it->first; + if (it == end || it->second >= end_index) + return ret; + } + + // Now count all complete chunks that fall within the range + while (it != end && it.outer()->end <= end_index) { + REALM_ASSERT_DEBUG(it.offset() == 0); + ret += it.outer()->count; + it.next_chunk(); + } + + // Cound all complete ranges within the last chunk + while (it != end && it->second <= end_index) { + ret += it->second - it->first; + ++it; + } + + // And finally add in the partial last range + if (it != end && it->first < end_index) + ret += end_index - it->first; + return ret; +} + +IndexSet::iterator IndexSet::find(size_t index) noexcept +{ + return find(index, begin()); +} + +IndexSet::iterator IndexSet::find(size_t index, iterator begin) noexcept +{ + auto it = std::find_if(begin.outer(), m_data.end(), + [&](auto const& lft) { return lft.end > index; }); + if (it == m_data.end()) + return end(); + if (index < it->begin) + return iterator(it, m_data.end(), &it->data[0]); + auto inner_begin = it->data.begin(); + if (it == begin.outer()) + inner_begin += begin.offset(); + auto inner = std::lower_bound(inner_begin, it->data.end(), index, + [&](auto const& lft, auto) { return lft.second <= index; }); + REALM_ASSERT_DEBUG(inner != it->data.end()); + + return iterator(it, m_data.end(), &*inner); +} + +void IndexSet::add(size_t index) +{ + do_add(find(index), index); +} + +void IndexSet::add(IndexSet const& other) +{ + auto it = begin(); + for (size_t index : other.as_indexes()) { + it = do_add(find(index, it), index); + } +} + +size_t IndexSet::add_shifted(size_t index) +{ + iterator it = begin(), end = this->end(); + + // Shift for any complete chunks before the target + for (; it != end && it.outer()->end <= index; it.next_chunk()) + index += it.outer()->count; + + // And any ranges within the last partial chunk + for (; it != end && it->first <= index; ++it) + index += it->second - it->first; + + do_add(it, index); + return index; +} + +void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values) +{ + if (values.empty()) + return; + +#ifdef REALM_DEBUG + size_t expected = std::distance(as_indexes().begin(), as_indexes().end()); + for (auto index : values.as_indexes()) { + if (!shifted_by.contains(index)) + ++expected; + } +#endif + + ChunkedRangeVectorBuilder builder(*this); + + auto old_it = cbegin(), old_end = cend(); + auto shift_it = shifted_by.cbegin(), shift_end = shifted_by.cend(); + + size_t skip_until = 0; + size_t old_shift = 0; + size_t new_shift = 0; + for (size_t index : values.as_indexes()) { + for (; shift_it != shift_end && shift_it->first <= index; ++shift_it) { + new_shift += shift_it->second - shift_it->first; + skip_until = shift_it->second; + } + if (index < skip_until) + continue; + + for (; old_it != old_end && old_it->first <= index - new_shift + old_shift; ++old_it) { + for (size_t i = old_it->first; i < old_it->second; ++i) + builder.push_back(i); + old_shift += old_it->second - old_it->first; + } + + REALM_ASSERT(index >= new_shift); + builder.push_back(index - new_shift + old_shift); + } + + copy(old_it, old_end, std::back_inserter(builder)); + m_data = builder.finalize(); + +#ifdef REALM_DEBUG + REALM_ASSERT((size_t)std::distance(as_indexes().begin(), as_indexes().end()) == expected); +#endif +} + +void IndexSet::set(size_t len) +{ + clear(); + if (len) { + push_back({0, len}); + } +} + +void IndexSet::insert_at(size_t index, size_t count) +{ + REALM_ASSERT(count > 0); + + auto pos = find(index); + auto end = this->end(); + bool in_existing = false; + if (pos != end) { + if (pos->first <= index) { + in_existing = true; + pos.adjust(0, count); + } + else { + pos.shift(count); + } + for (auto it = std::next(pos); it != end; ++it) + it.shift(count); + } + if (!in_existing) { + for (size_t i = 0; i < count; ++i) + pos = std::next(do_add(pos, index + i)); + } + + verify(); +} + +void IndexSet::insert_at(IndexSet const& positions) +{ + if (positions.empty()) + return; + if (empty()) { + *this = positions; + return; + } + + IndexIterator begin1 = cbegin(), begin2 = positions.cbegin(); + IndexIterator end1 = cend(), end2 = positions.cend(); + + ChunkedRangeVectorBuilder builder(*this); + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 + shift < *begin2) { + builder.push_back(*begin1++ + shift); + } + else { + ++shift; + builder.push_back(*begin2++); + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 + shift); + for (; begin2 != end2; ++begin2) + builder.push_back(*begin2); + + m_data = builder.finalize(); +} + +void IndexSet::shift_for_insert_at(size_t index, size_t count) +{ + REALM_ASSERT(count > 0); + + auto it = find(index); + if (it == end()) + return; + + for (auto pos = it, end = this->end(); pos != end; ++pos) + pos.shift(count); + + // If the range contained the insertion point, split the range and move + // the part of it before the insertion point back + if (it->first < index + count) { + auto old_second = it->second; + it.set(it->first - count, index); + insert(std::next(it), {index + count, old_second}); + } + verify(); +} + +void IndexSet::shift_for_insert_at(realm::IndexSet const& values) +{ + if (empty() || values.empty()) + return; + if (values.m_data.front().begin >= m_data.back().end) + return; + + IndexIterator begin1 = cbegin(), begin2 = values.cbegin(); + IndexIterator end1 = cend(), end2 = values.cend(); + + ChunkedRangeVectorBuilder builder(*this); + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 + shift < *begin2) { + builder.push_back(*begin1++ + shift); + } + else { + ++shift; + begin2++; + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 + shift); + + m_data = builder.finalize(); +} + +void IndexSet::erase_at(size_t index) +{ + auto it = find(index); + if (it != end()) + do_erase(it, index); +} + +void IndexSet::erase_at(IndexSet const& positions) +{ + if (empty() || positions.empty()) + return; + + ChunkedRangeVectorBuilder builder(*this); + + IndexIterator begin1 = cbegin(), begin2 = positions.cbegin(); + IndexIterator end1 = cend(), end2 = positions.cend(); + + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 < *begin2) { + builder.push_back(*begin1++ - shift); + } + else if (*begin1 == *begin2) { + ++shift; + ++begin1; + ++begin2; + } + else { + ++shift; + ++begin2; + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 - shift); + + m_data = builder.finalize(); +} + +size_t IndexSet::erase_or_unshift(size_t index) +{ + auto shifted = index; + iterator it = begin(), end = this->end(); + + // Shift for any complete chunks before the target + for (; it != end && it.outer()->end <= index; it.next_chunk()) + shifted -= it.outer()->count; + + // And any ranges within the last partial chunk + for (; it != end && it->second <= index; ++it) + shifted -= it->second - it->first; + + if (it == end) + return shifted; + + if (it->first <= index) + shifted = npos; + + do_erase(it, index); + + return shifted; +} + +void IndexSet::do_erase(iterator it, size_t index) +{ + if (it->first <= index) { + if (it->first + 1 == it->second) { + it = erase(it); + } + else { + it.adjust(0, -1); + ++it; + } + } + else if (it != begin() && std::prev(it)->second + 1 == it->first) { + std::prev(it).adjust(0, it->second - it->first); + it = erase(it); + } + + for (; it != end(); ++it) + it.shift(-1); +} + +IndexSet::iterator IndexSet::do_remove(iterator it, size_t begin, size_t end) +{ + for (it = find(begin, it); it != this->end() && it->first < end; it = find(begin, it)) { + // Trim off any part of the range to remove that's before the matching range + begin = std::max(it->first, begin); + + // If the matching range extends to both sides of the range to remove, + // split it on the range to remove + if (it->first < begin && it->second > end) { + auto old_second = it->second; + it.set(it->first, begin); + it = std::prev(insert(std::next(it), {end, old_second})); + } + // Range to delete now coverages (at least) one end of the matching range + else if (begin == it->first && end >= it->second) + it = erase(it); + else if (begin == it->first) + it.set(end, it->second); + else + it.set(it->first, begin); + } + return it; +} + +void IndexSet::remove(size_t index, size_t count) +{ + do_remove(find(index), index, index + count); +} + +void IndexSet::remove(realm::IndexSet const& values) +{ + auto it = begin(); + for (auto range : values) { + it = do_remove(it, range.first, range.second); + if (it == end()) + return; + } +} + +size_t IndexSet::shift(size_t index) const noexcept +{ + // FIXME: optimize + for (auto range : *this) { + if (range.first > index) + break; + index += range.second - range.first; + } + return index; +} + +size_t IndexSet::unshift(size_t index) const noexcept +{ + REALM_ASSERT_DEBUG(!contains(index)); + return index - count(0, index); +} + +void IndexSet::clear() noexcept +{ + m_data.clear(); +} + +IndexSet::iterator IndexSet::do_add(iterator it, size_t index) +{ + verify(); + bool more_before = it != begin(), valid = it != end(); + REALM_ASSERT(!more_before || index >= std::prev(it)->second); + if (valid && it->first <= index && it->second > index) { + // index is already in set + return it; + } + if (more_before && std::prev(it)->second == index) { + auto prev = std::prev(it); + // index is immediately after an existing range + prev.adjust(0, 1); + + if (valid && prev->second == it->first) { + // index joins two existing ranges + prev.adjust(0, it->second - it->first); + return std::prev(erase(it)); + } + return prev; + } + if (valid && it->first == index + 1) { + // index is immediately before an existing range + it.adjust(-1, 0); + return it; + } + + // index is not next to an existing range + return insert(it, {index, index + 1}); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp new file mode 100644 index 0000000..a21e733 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp @@ -0,0 +1,249 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "list.hpp" + +#include "impl/list_notifier.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_store.hpp" +#include "results.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" +#include "util/format.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +List::List() noexcept = default; +List::~List() = default; + +List::List(const List&) = default; +List& List::operator=(const List&) = default; +List::List(List&&) = default; +List& List::operator=(List&&) = default; + +List::List(std::shared_ptr r, LinkViewRef l) noexcept +: m_realm(std::move(r)) +, m_link_view(std::move(l)) +{ +} + +const ObjectSchema& List::get_object_schema() const +{ + verify_attached(); + + if (!m_object_schema) { + auto object_type = ObjectStore::object_type_for_table_name(m_link_view->get_target_table().get_name()); + auto it = m_realm->schema().find(object_type); + REALM_ASSERT(it != m_realm->schema().end()); + m_object_schema = &*it; + } + return *m_object_schema; +} + +Query List::get_query() const +{ + verify_attached(); + return m_link_view->get_target_table().where(m_link_view); +} + +size_t List::get_origin_row_index() const +{ + verify_attached(); + return m_link_view->get_origin_row_index(); +} + +void List::verify_valid_row(size_t row_ndx, bool insertion) const +{ + size_t size = m_link_view->size(); + if (row_ndx > size || (!insertion && row_ndx == size)) { + throw OutOfBoundsIndexException{row_ndx, size + insertion}; + } +} + +bool List::is_valid() const +{ + m_realm->verify_thread(); + return m_link_view && m_link_view->is_attached(); +} + +void List::verify_attached() const +{ + if (!is_valid()) { + throw InvalidatedException(); + } +} + +void List::verify_in_transaction() const +{ + verify_attached(); + if (!m_realm->is_in_transaction()) { + throw InvalidTransactionException("Must be in a write transaction"); + } +} + +size_t List::size() const +{ + verify_attached(); + return m_link_view->size(); +} + +RowExpr List::get(size_t row_ndx) const +{ + verify_attached(); + verify_valid_row(row_ndx); + return m_link_view->get(row_ndx); +} + +size_t List::get_unchecked(size_t row_ndx) const noexcept +{ + return m_link_view->get(row_ndx).get_index(); +} + +size_t List::find(ConstRow const& row) const +{ + verify_attached(); + + if (!row.is_attached() || row.get_table() != &m_link_view->get_target_table()) { + return not_found; + } + + return m_link_view->find(row.get_index()); +} + +void List::add(size_t target_row_ndx) +{ + verify_in_transaction(); + m_link_view->add(target_row_ndx); +} + +void List::insert(size_t row_ndx, size_t target_row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx, true); + m_link_view->insert(row_ndx, target_row_ndx); +} + +void List::move(size_t source_ndx, size_t dest_ndx) +{ + verify_in_transaction(); + verify_valid_row(source_ndx); + verify_valid_row(dest_ndx); // Can't be one past end due to removing one earlier + m_link_view->move(source_ndx, dest_ndx); +} + +void List::remove(size_t row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + m_link_view->remove(row_ndx); +} + +void List::remove_all() +{ + verify_in_transaction(); + m_link_view->clear(); +} + +void List::set(size_t row_ndx, size_t target_row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + m_link_view->set(row_ndx, target_row_ndx); +} + +void List::swap(size_t ndx1, size_t ndx2) +{ + verify_in_transaction(); + verify_valid_row(ndx1); + verify_valid_row(ndx2); + m_link_view->swap(ndx1, ndx2); +} + +void List::delete_all() +{ + verify_in_transaction(); + m_link_view->remove_all_target_rows(); +} + +Results List::sort(SortDescriptor order) +{ + verify_attached(); + return Results(m_realm, m_link_view, util::none, std::move(order)); +} + +Results List::filter(Query q) +{ + verify_attached(); + return Results(m_realm, m_link_view, get_query().and_query(std::move(q))); +} + +Results List::snapshot() const +{ + verify_attached(); + return Results(m_realm, m_link_view).snapshot(); +} + +util::Optional List::max(size_t column) +{ + return Results(m_realm, m_link_view).max(column); +} + +util::Optional List::min(size_t column) +{ + return Results(m_realm, m_link_view).min(column); +} + +util::Optional List::sum(size_t column) +{ + return Results(m_realm, m_link_view).sum(column); +} + +util::Optional List::average(size_t column) +{ + return Results(m_realm, m_link_view).average(column); +} + +// These definitions rely on that LinkViews are interned by core +bool List::operator==(List const& rgt) const noexcept +{ + return m_link_view.get() == rgt.m_link_view.get(); +} + +namespace std { +size_t hash::operator()(realm::List const& list) const +{ + return std::hash()(list.m_link_view.get()); +} +} + +NotificationToken List::add_notification_callback(CollectionChangeCallback cb) & +{ + verify_attached(); + if (!m_notifier) { + m_notifier = std::make_shared(m_link_view, m_realm); + RealmCoordinator::register_notifier(m_notifier); + } + return {m_notifier, m_notifier->add_callback(std::move(cb))}; +} + +List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +, requested(r), valid_count(c) {} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp new file mode 100644 index 0000000..d51ce81 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "object.hpp" + +#include "impl/object_notifier.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_schema.hpp" +#include "util/format.hpp" + +using namespace realm; + +InvalidatedObjectException::InvalidatedObjectException(const std::string& object_type) +: std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted") +, object_type(object_type) +{} + +InvalidPropertyException::InvalidPropertyException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name)) +, object_type(object_type), property_name(property_name) +{} + +MissingPropertyValueException::MissingPropertyValueException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Missing value for property '%1.%2'", object_type, property_name)) +, object_type(object_type), property_name(property_name) +{} + +MissingPrimaryKeyException::MissingPrimaryKeyException(const std::string& object_type) +: std::logic_error(util::format("'%1' does not have a primary key defined", object_type)) +, object_type(object_type) +{} + +ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Cannot modify read-only property '%1.%2'", object_type, property_name)) +, object_type(object_type), property_name(property_name) {} + +Object::Object(SharedRealm r, ObjectSchema const& s, BasicRowExpr const& o) +: m_realm(std::move(r)), m_object_schema(&s), m_row(o) { } + +Object::Object(SharedRealm r, ObjectSchema const& s, Row const& o) +: m_realm(std::move(r)), m_object_schema(&s), m_row(o) { } + +Object::Object() = default; +Object::~Object() = default; +Object::Object(Object const&) = default; +Object::Object(Object&&) = default; +Object& Object::operator=(Object const&) = default; +Object& Object::operator=(Object&&) = default; + +NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) & +{ + if (!m_notifier) { + m_notifier = std::make_shared<_impl::ObjectNotifier>(m_row, m_realm); + _impl::RealmCoordinator::register_notifier(m_notifier); + } + return {m_notifier, m_notifier->add_callback(std::move(callback))}; +} + +void Object::verify_attached() const +{ + m_realm->verify_thread(); + if (!m_row.is_attached()) { + throw InvalidatedObjectException(m_object_schema->name); + } +} + +Property const& Object::property_for_name(std::string const& prop_name) const +{ + auto prop = m_object_schema->property_for_name(prop_name); + if (!prop) { + throw InvalidPropertyException(m_object_schema->name, prop_name); + } + return *prop; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp new file mode 100644 index 0000000..b71922f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp @@ -0,0 +1,234 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "object_schema.hpp" + +#include "object_store.hpp" +#include "property.hpp" +#include "schema.hpp" + +#include "util/format.hpp" + +#include +#include +#include + +using namespace realm; + +#define ASSERT_PROPERTY_TYPE_VALUE(property, type) \ + static_assert(static_cast(PropertyType::property) == type_##type, \ + "PropertyType and DataType must have the same values") + +ASSERT_PROPERTY_TYPE_VALUE(Int, Int); +ASSERT_PROPERTY_TYPE_VALUE(Bool, Bool); +ASSERT_PROPERTY_TYPE_VALUE(Float, Float); +ASSERT_PROPERTY_TYPE_VALUE(Double, Double); +ASSERT_PROPERTY_TYPE_VALUE(Data, Binary); +ASSERT_PROPERTY_TYPE_VALUE(Date, Timestamp); +ASSERT_PROPERTY_TYPE_VALUE(Any, Mixed); +ASSERT_PROPERTY_TYPE_VALUE(Object, Link); +ASSERT_PROPERTY_TYPE_VALUE(Array, LinkList); + +ObjectSchema::ObjectSchema() = default; +ObjectSchema::~ObjectSchema() = default; + +ObjectSchema::ObjectSchema(std::string name, std::initializer_list persisted_properties) +: ObjectSchema(std::move(name), persisted_properties, {}) +{ +} + +ObjectSchema::ObjectSchema(std::string name, std::initializer_list persisted_properties, + std::initializer_list computed_properties) +: name(std::move(name)) +, persisted_properties(persisted_properties) +, computed_properties(computed_properties) +{ + for (auto const& prop : persisted_properties) { + if (prop.is_primary) { + primary_key = prop.name; + } + } +} + +ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : name(name) { + ConstTableRef table; + if (index < group.size()) { + table = group.get_table(index); + } + else { + table = ObjectStore::table_for_object_type(group, name); + } + + size_t count = table->get_column_count(); + persisted_properties.reserve(count); + for (size_t col = 0; col < count; col++) { + Property property; + property.name = table->get_column_name(col).data(); + property.type = (PropertyType)table->get_column_type(col); + property.is_indexed = table->has_search_index(col); + property.is_nullable = table->is_nullable(col) || property.type == PropertyType::Object; + property.table_column = col; + if (property.type == PropertyType::Object || property.type == PropertyType::Array) { + // set link type for objects and arrays + ConstTableRef linkTable = table->get_link_target(col); + property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); + } + persisted_properties.push_back(std::move(property)); + } + + primary_key = realm::ObjectStore::get_primary_key_for_object(group, name); + set_primary_key_property(); +} + +Property *ObjectSchema::property_for_name(StringData name) { + for (auto& prop : persisted_properties) { + if (StringData(prop.name) == name) { + return ∝ + } + } + for (auto& prop : computed_properties) { + if (StringData(prop.name) == name) { + return ∝ + } + } + return nullptr; +} + +const Property *ObjectSchema::property_for_name(StringData name) const { + return const_cast(this)->property_for_name(name); +} + +bool ObjectSchema::property_is_computed(Property const& property) const { + auto end = computed_properties.end(); + return std::find(computed_properties.begin(), end, property) != end; +} + +void ObjectSchema::set_primary_key_property() +{ + if (primary_key.length()) { + if (auto primary_key_prop = primary_key_property()) { + primary_key_prop->is_primary = true; + } + } +} + +static void validate_property(Schema const& schema, + std::string const& object_name, + Property const& prop, + Property const** primary, + std::vector& exceptions) +{ + // check nullablity + if (prop.is_nullable && !prop.type_is_nullable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be nullable.", + object_name, prop.name, string_for_property_type(prop.type)); + } + else if (prop.type == PropertyType::Object && !prop.is_nullable) { + exceptions.emplace_back("Property '%1.%2' of type 'Object' must be nullable.", object_name, prop.name); + } + + // check primary keys + if (prop.is_primary) { + if (!prop.is_indexable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be made the primary key.", + object_name, prop.name, string_for_property_type(prop.type)); + } + if (*primary) { + exceptions.emplace_back("Properties'%1' and '%2' are both marked as the primary key of '%3'.", + prop.name, (*primary)->name, object_name); + } + *primary = ∝ + } + + // check indexable + if (prop.is_indexed && !prop.is_indexable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be indexed.", + object_name, prop.name, string_for_property_type(prop.type)); + } + + // check that only link properties have object types + if (prop.type != PropertyType::LinkingObjects && !prop.link_origin_property_name.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an origin property name.", + object_name, prop.name, string_for_property_type(prop.type)); + } + else if (prop.type == PropertyType::LinkingObjects && prop.link_origin_property_name.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' must have an origin property name.", + object_name, prop.name, string_for_property_type(prop.type)); + } + + if (prop.type != PropertyType::Object && prop.type != PropertyType::Array && prop.type != PropertyType::LinkingObjects) { + if (!prop.object_type.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.", + object_name, prop.name, string_for_property_type(prop.type)); + } + return; + } + + + // check that the object_type is valid for link properties + auto it = schema.find(prop.object_type); + if (it == schema.end()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' has unknown object type '%4'", + object_name, prop.name, string_for_property_type(prop.type), prop.object_type); + return; + } + if (prop.type != PropertyType::LinkingObjects) { + return; + } + + const Property *origin_property = it->property_for_name(prop.link_origin_property_name); + if (!origin_property) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name); + } + else if (origin_property->type != PropertyType::Object && origin_property->type != PropertyType::Array) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name); + } + else if (origin_property->object_type != object_name) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to type '%5'", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name, origin_property->object_type); + } +} + +void ObjectSchema::validate(Schema const& schema, std::vector& exceptions) const +{ + const Property *primary = nullptr; + for (auto const& prop : persisted_properties) { + validate_property(schema, name, prop, &primary, exceptions); + } + for (auto const& prop : computed_properties) { + validate_property(schema, name, prop, &primary, exceptions); + } + + if (!primary_key.empty() && !primary && !primary_key_property()) { + exceptions.emplace_back("Specified primary key '%1.%2' does not exist.", name, primary_key); + } +} + +namespace realm { +bool operator==(ObjectSchema const& a, ObjectSchema const& b) +{ + return std::tie(a.name, a.primary_key, a.persisted_properties, a.computed_properties) + == std::tie(b.name, b.primary_key, b.persisted_properties, b.computed_properties); + +} +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp new file mode 100644 index 0000000..ee0c3cb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp @@ -0,0 +1,825 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "object_store.hpp" + +#include "object_schema.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" +#include "util/format.hpp" + +#include +#include +#include +#include + +#include + +using namespace realm; + +const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); + +namespace { +const char * const c_metadataTableName = "metadata"; +const char * const c_versionColumnName = "version"; +const size_t c_versionColumnIndex = 0; + +const char * const c_primaryKeyTableName = "pk"; +const char * const c_primaryKeyObjectClassColumnName = "pk_table"; +const size_t c_primaryKeyObjectClassColumnIndex = 0; +const char * const c_primaryKeyPropertyNameColumnName = "pk_property"; +const size_t c_primaryKeyPropertyNameColumnIndex = 1; + +const size_t c_zeroRowIndex = 0; + +const char c_object_table_prefix[] = "class_"; + +void create_metadata_tables(Group& group) { + // FIXME: the order of the creation of the two tables seems to + // matter for some Android devices. The reason is unclear, and + // further investigation is required. + // See https://github.com/realm/realm-java/issues/3651 + + TableRef table = group.get_or_add_table(c_metadataTableName); + if (table->get_column_count() == 0) { + table->add_column(type_Int, c_versionColumnName); + + // set initial version + table->add_empty_row(); + table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned); + } + + table = group.get_or_add_table(c_primaryKeyTableName); + if (table->get_column_count() == 0) { + table->add_column(type_String, c_primaryKeyObjectClassColumnName); + table->add_column(type_String, c_primaryKeyPropertyNameColumnName); + } + table->add_search_index(table->get_column_index(c_primaryKeyObjectClassColumnName)); +} + +void set_schema_version(Group& group, uint64_t version) { + TableRef table = group.get_or_add_table(c_metadataTableName); + table->set_int(c_versionColumnIndex, c_zeroRowIndex, version); +} + +template +auto table_for_object_schema(Group& group, ObjectSchema const& object_schema) +{ + return ObjectStore::table_for_object_type(group, object_schema.name); +} + +void add_index(Table& table, size_t col) +{ + try { + table.add_search_index(col); + } + catch (LogicError const&) { + throw std::logic_error(util::format("Cannot index property '%1.%2': indexing properties of type '%3' is not yet implemented.", + ObjectStore::object_type_for_table_name(table.get_name()), + table.get_column_name(col), + string_for_property_type((PropertyType)table.get_column_type(col)))); + } +} + +void insert_column(Group& group, Table& table, Property const& property, size_t col_ndx) +{ + // Cannot directly insert a LinkingObjects column (a computed property). + // LinkingObjects must be an artifact of an existing link column. + REALM_ASSERT(property.type != PropertyType::LinkingObjects); + + if (property.type == PropertyType::Object || property.type == PropertyType::Array) { + auto target_name = ObjectStore::table_name_for_object_type(property.object_type); + TableRef link_table = group.get_or_add_table(target_name); + table.insert_column_link(col_ndx, DataType(property.type), property.name, *link_table); + } + else { + table.insert_column(col_ndx, DataType(property.type), property.name, property.is_nullable); + if (property.requires_index()) + add_index(table, col_ndx); + } +} + +void add_column(Group& group, Table& table, Property const& property) +{ + insert_column(group, table, property, table.get_column_count()); +} + +void replace_column(Group& group, Table& table, Property const& old_property, Property const& new_property) +{ + insert_column(group, table, new_property, old_property.table_column); + table.remove_column(old_property.table_column + 1); +} + +TableRef create_table(Group& group, ObjectSchema const& object_schema) +{ + auto name = ObjectStore::table_name_for_object_type(object_schema.name); + auto table = group.get_or_add_table(name); + if (table->get_column_count() > 0) { + return table; + } + + for (auto const& prop : object_schema.persisted_properties) { + add_column(group, *table, prop); + } + + ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key); + + return table; +} + +void copy_property_values(Property const& prop, Table& table) +{ + auto copy_property_values = [&](auto getter, auto setter) { + for (size_t i = 0, count = table.size(); i < count; i++) { + bool is_default = false; + (table.*setter)(prop.table_column, i, (table.*getter)(prop.table_column + 1, i), + is_default); + } + }; + + switch (prop.type) { + case PropertyType::Int: + copy_property_values(&Table::get_int, &Table::set_int); + break; + case PropertyType::Bool: + copy_property_values(&Table::get_bool, &Table::set_bool); + break; + case PropertyType::Float: + copy_property_values(&Table::get_float, &Table::set_float); + break; + case PropertyType::Double: + copy_property_values(&Table::get_double, &Table::set_double); + break; + case PropertyType::String: + copy_property_values(&Table::get_string, &Table::set_string); + break; + case PropertyType::Data: + copy_property_values(&Table::get_binary, &Table::set_binary); + break; + case PropertyType::Date: + copy_property_values(&Table::get_timestamp, &Table::set_timestamp); + break; + default: + break; + } +} + +void make_property_optional(Group& group, Table& table, Property property) +{ + property.is_nullable = true; + insert_column(group, table, property, property.table_column); + copy_property_values(property, table); + table.remove_column(property.table_column + 1); +} + +void make_property_required(Group& group, Table& table, Property property) +{ + property.is_nullable = false; + insert_column(group, table, property, property.table_column); + table.remove_column(property.table_column + 1); +} + +void validate_primary_column_uniqueness(Group const& group, StringData object_type, StringData primary_property) +{ + auto table = ObjectStore::table_for_object_type(group, object_type); + if (table->get_distinct_view(table->get_column_index(primary_property)).size() != table->size()) { + throw DuplicatePrimaryKeyValueException(object_type, primary_property); + } +} + +void validate_primary_column_uniqueness(Group const& group) +{ + auto pk_table = group.get_table(c_primaryKeyTableName); + for (size_t i = 0, count = pk_table->size(); i < count; ++i) { + validate_primary_column_uniqueness(group, + pk_table->get_string(c_primaryKeyObjectClassColumnIndex, i), + pk_table->get_string(c_primaryKeyPropertyNameColumnIndex, i)); + } +} +} // anonymous namespace + +// FIXME remove this after integrating OS's migration related logic into Realm java +void ObjectStore::set_schema_version(Group& group, uint64_t version) { + ::create_metadata_tables(group); + ::set_schema_version(group, version); +} + +uint64_t ObjectStore::get_schema_version(Group const& group) { + ConstTableRef table = group.get_table(c_metadataTableName); + if (!table || table->get_column_count() == 0) { + return ObjectStore::NotVersioned; + } + return table->get_int(c_versionColumnIndex, c_zeroRowIndex); +} + +StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) { + ConstTableRef table = group.get_table(c_primaryKeyTableName); + if (!table) { + return ""; + } + size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); + if (row == not_found) { + return ""; + } + return table->get_string(c_primaryKeyPropertyNameColumnIndex, row); +} + +void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) { + TableRef table = group.get_table(c_primaryKeyTableName); + + // get row or create if new object and populate + size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); + if (row == not_found && primary_key.size()) { + row = table->add_empty_row(); + row = table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type); + } + + // set if changing, or remove if setting to nil + if (primary_key.size() == 0) { + if (row != not_found) { + table->move_last_over(row); + } + } + else { + table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); + } +} + +StringData ObjectStore::object_type_for_table_name(StringData table_name) { + if (table_name.begins_with(c_object_table_prefix)) { + return table_name.substr(sizeof(c_object_table_prefix) - 1); + } + return StringData(); +} + +std::string ObjectStore::table_name_for_object_type(StringData object_type) { + return std::string(c_object_table_prefix) + std::string(object_type); +} + +TableRef ObjectStore::table_for_object_type(Group& group, StringData object_type) { + auto name = table_name_for_object_type(object_type); + return group.get_table(name); +} + +ConstTableRef ObjectStore::table_for_object_type(Group const& group, StringData object_type) { + auto name = table_name_for_object_type(object_type); + return group.get_table(name); +} + +namespace { +struct SchemaDifferenceExplainer { + std::vector errors; + + void operator()(schema_change::AddTable op) + { + errors.emplace_back("Class '%1' has been added.", op.object->name); + } + + void operator()(schema_change::AddProperty op) + { + errors.emplace_back("Property '%1.%2' has been added.", op.object->name, op.property->name); + } + + void operator()(schema_change::RemoveProperty op) + { + errors.emplace_back("Property '%1.%2' has been removed.", op.object->name, op.property->name); + } + + void operator()(schema_change::ChangePropertyType op) + { + errors.emplace_back("Property '%1.%2' has been changed from '%3' to '%4'.", + op.object->name, op.new_property->name, + string_for_property_type(op.old_property->type), + string_for_property_type(op.new_property->type)); + } + + void operator()(schema_change::MakePropertyNullable op) + { + errors.emplace_back("Property '%1.%2' has been made optional.", op.object->name, op.property->name); + } + + void operator()(schema_change::MakePropertyRequired op) + { + errors.emplace_back("Property '%1.%2' has been made required.", op.object->name, op.property->name); + } + + void operator()(schema_change::ChangePrimaryKey op) + { + if (op.property && !op.object->primary_key.empty()) { + errors.emplace_back("Primary Key for class '%1 has changed from '%2' to '%3'.", + op.object->name, op.object->primary_key, op.property->name); + } + else if (op.property) { + errors.emplace_back("Primary Key for class '%1 has been added.", op.object->name); + } + else { + errors.emplace_back("Primary Key for class '%1 has been removed.", op.object->name); + } + } + + void operator()(schema_change::AddIndex op) + { + errors.emplace_back("Property '%1.%2' has been made indexed.", op.object->name, op.property->name); + } + + void operator()(schema_change::RemoveIndex op) + { + errors.emplace_back("Property '%1.%2' has been made unindexed.", op.object->name, op.property->name); + } +}; + +class TableHelper { +public: + TableHelper(Group& g) : m_group(g) { } + + Table& operator()(const ObjectSchema* object_schema) + { + if (object_schema != m_current_object_schema) { + m_current_table = table_for_object_schema(m_group, *object_schema); + m_current_object_schema = object_schema; + } + REALM_ASSERT(m_current_table); + return *m_current_table; + } + +private: + Group& m_group; + const ObjectSchema* m_current_object_schema = nullptr; + TableRef m_current_table; +}; + +template +void verify_no_errors(Verifier&& verifier, std::vector const& changes) +{ + for (auto& change : changes) { + change.visit(verifier); + } + + if (!verifier.errors.empty()) { + throw ErrorType(verifier.errors); + } +} +} // anonymous namespace + +bool ObjectStore::needs_migration(std::vector const& changes) +{ + using namespace schema_change; + struct Visitor { + bool operator()(AddIndex) { return false; } + bool operator()(AddProperty) { return true; } + bool operator()(AddTable) { return false; } + bool operator()(ChangePrimaryKey) { return true; } + bool operator()(ChangePropertyType) { return true; } + bool operator()(MakePropertyNullable) { return true; } + bool operator()(MakePropertyRequired) { return true; } + bool operator()(RemoveIndex) { return false; } + bool operator()(RemoveProperty) { return true; } + }; + + return std::any_of(begin(changes), end(changes), + [](auto&& change) { return change.visit(Visitor()); }); +} + +void ObjectStore::verify_no_changes_required(std::vector const& changes) +{ + verify_no_errors(SchemaDifferenceExplainer(), changes); +} + +void ObjectStore::verify_no_migration_required(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + // Adding a table or adding/removing indexes can be done automatically. + // All other changes require migrations. + void operator()(AddTable) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + } verifier; + verify_no_errors(verifier, changes); +} + +bool ObjectStore::verify_valid_additive_changes(std::vector const& changes, bool update_indexes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + bool index_changes = false; + bool other_changes = false; + + // Additive mode allows adding things, extra columns, and adding/removing indexes + void operator()(AddTable) { other_changes = true; } + void operator()(AddProperty) { other_changes = true; } + void operator()(RemoveProperty) { } + void operator()(AddIndex) { index_changes = true; } + void operator()(RemoveIndex) { index_changes = true; } + } verifier; + verify_no_errors(verifier, changes); + return verifier.other_changes || (verifier.index_changes && update_indexes); +} + +void ObjectStore::verify_valid_external_changes(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + // Adding new things is fine + void operator()(AddTable) { } + void operator()(AddProperty) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + } verifier; + verify_no_errors(verifier, changes); +} + +void ObjectStore::verify_compatible_for_read_only(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + void operator()(AddTable) { } + void operator()(RemoveProperty) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + } verifier; + verify_no_errors(verifier, changes); +} + +static void apply_non_migration_changes(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier : SchemaDifferenceExplainer { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + // Produce an exception listing the unsupported schema changes for + // everything but the explicitly supported ones + using SchemaDifferenceExplainer::operator(); + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + } applier{group}; + verify_no_errors(applier, changes); +} + +static void create_initial_tables(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + void operator()(AddTable op) { create_table(group, *op.object); } + + // Note that in normal operation none of these will be hit, as if we're + // creating the initial tables there shouldn't be anything to update. + // Implementing these makes us better able to handle weird + // not-quite-correct files produced by other things and has no obvious + // downside. + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(RemoveProperty op) { table(op.object).remove_column(op.property->table_column); } + void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } + void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } + void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + + void operator()(ChangePropertyType op) + { + replace_column(group, table(op.object), *op.old_property, *op.new_property); + } + } applier{group}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_additive_changes(Group& group, std::vector const& changes, bool update_indexes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group, bool update_indexes) + : group{group}, table{group}, update_indexes{update_indexes} { } + Group& group; + TableHelper table; + bool update_indexes; + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(AddIndex op) { if (update_indexes) add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->table_column); } + void operator()(RemoveProperty) { } + + // No need for errors for these, as we've already verified that they aren't present + void operator()(ChangePrimaryKey) { } + void operator()(ChangePropertyType) { } + void operator()(MakePropertyNullable) { } + void operator()(MakePropertyRequired) { } + } applier{group, update_indexes}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_pre_migration_changes(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(RemoveProperty) { /* delayed until after the migration */ } + void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); } + void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } + void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } + void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name.c_str(), op.property ? op.property->name.c_str() : ""); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + } applier{group}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_post_migration_changes(Group& group, std::vector const& changes, Schema const& initial_schema) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group, Schema const& initial_schema) : group{group}, initial_schema(initial_schema), table(group) { } + Group& group; + Schema const& initial_schema; + TableHelper table; + + void operator()(RemoveProperty op) + { + if (!initial_schema.empty() && !initial_schema.find(op.object->name)->property_for_name(op.property->name)) + throw std::logic_error(util::format("Renamed property '%1.%2' does not exist.", op.object->name, op.property->name)); + auto table = table_for_object_schema(group, *op.object); + table->remove_column(op.property->table_column); + } + + void operator()(ChangePrimaryKey op) + { + if (op.property) { + validate_primary_column_uniqueness(group, op.object->name, op.property->name); + } + } + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + + void operator()(ChangePropertyType) { } + void operator()(MakePropertyNullable) { } + void operator()(MakePropertyRequired) { } + void operator()(AddProperty) { } + } applier{group, initial_schema}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version, + Schema& target_schema, uint64_t target_schema_version, + SchemaMode mode, std::vector const& changes, + std::function migration_function) +{ + create_metadata_tables(group); + + if (schema_version == ObjectStore::NotVersioned) { + create_initial_tables(group, changes); + set_schema_version(group, target_schema_version); + set_schema_columns(group, target_schema); + return; + } + + if (mode == SchemaMode::Additive) { + apply_additive_changes(group, changes, schema_version < target_schema_version); + + if (schema_version < target_schema_version) { + schema_version = target_schema_version; + set_schema_version(group, target_schema_version); + } + + set_schema_columns(group, target_schema); + return; + } + + if (mode == SchemaMode::Manual) { + set_schema_columns(group, target_schema); + migration_function(); + + verify_no_changes_required(schema_from_group(group).compare(target_schema)); + validate_primary_column_uniqueness(group); + set_schema_columns(group, target_schema); + set_schema_version(group, target_schema_version); + return; + } + + if (schema_version == target_schema_version) { + apply_non_migration_changes(group, changes); + set_schema_columns(group, target_schema); + return; + } + + auto old_schema = schema_from_group(group); + apply_pre_migration_changes(group, changes); + if (migration_function) { + set_schema_columns(group, target_schema); + migration_function(); + + // Migration function may have changed the schema, so we need to re-read it + auto schema = schema_from_group(group); + apply_post_migration_changes(group, schema.compare(target_schema), old_schema); + validate_primary_column_uniqueness(group); + } + else { + apply_post_migration_changes(group, changes, {}); + } + + set_schema_version(group, target_schema_version); + set_schema_columns(group, target_schema); +} + +Schema ObjectStore::schema_from_group(Group const& group) { + std::vector schema; + schema.reserve(group.size()); + for (size_t i = 0; i < group.size(); i++) { + auto object_type = object_type_for_table_name(group.get_table_name(i)); + if (object_type.size()) { + schema.emplace_back(group, object_type, i); + } + } + return schema; +} + +void ObjectStore::set_schema_columns(Group const& group, Schema& schema) +{ + for (auto& object_schema : schema) { + auto table = table_for_object_schema(group, object_schema); + if (!table) { + continue; + } + for (auto& property : object_schema.persisted_properties) { + property.table_column = table->get_column_index(property.name); + } + } +} + +void ObjectStore::delete_data_for_object(Group& group, StringData object_type) { + if (TableRef table = table_for_object_type(group, object_type)) { + group.remove_table(table->get_index_in_group()); + ObjectStore::set_primary_key_for_object(group, object_type, ""); + } +} + +bool ObjectStore::is_empty(Group const& group) { + for (size_t i = 0; i < group.size(); i++) { + ConstTableRef table = group.get_table(i); + std::string object_type = object_type_for_table_name(table->get_name()); + if (!object_type.length()) { + continue; + } + if (!table->is_empty()) { + return false; + } + } + return true; +} + +void ObjectStore::rename_property(Group& group, Schema& target_schema, StringData object_type, StringData old_name, StringData new_name) +{ + TableRef table = table_for_object_type(group, object_type); + if (!table) { + throw std::logic_error(util::format("Cannot rename properties for type '%1' because it does not exist.", object_type)); + } + + auto target_object_schema = target_schema.find(object_type); + if (target_object_schema == target_schema.end()) { + throw std::logic_error(util::format("Cannot rename properties for type '%1' because it has been removed from the Realm.", object_type)); + } + + if (target_object_schema->property_for_name(old_name)) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because the source property still exists.", + object_type, old_name, new_name)); + } + + ObjectSchema table_object_schema(group, object_type); + Property *old_property = table_object_schema.property_for_name(old_name); + if (!old_property) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name)); + } + + Property *new_property = table_object_schema.property_for_name(new_name); + if (!new_property) { + // New property doesn't exist in the table, which means we're probably + // renaming to an intermediate property in a multi-version migration. + // This is safe because the migration will fail schema validation unless + // this property is renamed again to a valid name before the end. + table->rename_column(old_property->table_column, new_name); + return; + } + + if (old_property->type != new_property->type || old_property->object_type != new_property->object_type) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from type '%4' to '%5'.", + object_type, old_name, new_name, old_property->type_string(), new_property->type_string())); + } + + if (old_property->is_nullable && !new_property->is_nullable) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from optional to required.", + object_type, old_name, new_name)); + } + + size_t column_to_remove = new_property->table_column; + table->rename_column(old_property->table_column, new_name); + table->remove_column(column_to_remove); + + // update table_column for each property since it may have shifted + for (auto& current_prop : target_object_schema->persisted_properties) { + if (current_prop.table_column == column_to_remove) + current_prop.table_column = old_property->table_column; + else if (current_prop.table_column > column_to_remove) + --current_prop.table_column; + } + + // update nullability for column + if (new_property->is_nullable && !old_property->is_nullable) { + auto prop = *new_property; + prop.table_column = old_property->table_column; + make_property_optional(group, *table, prop); + } +} + +InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) +: logic_error(util::format("Provided schema version %1 is less than last set version %2.", new_version, old_version)) +, m_old_version(old_version), m_new_version(new_version) +{ +} + +DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, std::string property) +: logic_error(util::format("Primary key property '%1.%2' has duplicate values after migration.", object_type, property)) +, m_object_type(object_type), m_property(property) +{ +} + +SchemaValidationException::SchemaValidationException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "Schema validation failed due to the following errors:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} + +SchemaMismatchException::SchemaMismatchException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "Migration is required due to the following errors:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} + +InvalidSchemaChangeException::InvalidSchemaChangeException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "The following changes cannot be made in additive-only schema mode:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp new file mode 100644 index 0000000..8936534 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp @@ -0,0 +1 @@ +// This file is intentionally left blank. diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp new file mode 100644 index 0000000..b5e12fc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp @@ -0,0 +1,635 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "results.hpp" + +#include "impl/realm_coordinator.hpp" +#include "impl/results_notifier.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "schema.hpp" +#include "util/compiler.hpp" +#include "util/format.hpp" + +#include + +using namespace realm; + +Results::Results() = default; +Results::~Results() = default; + +Results::Results(SharedRealm r, Query q, SortDescriptor s, SortDescriptor d) +: m_realm(std::move(r)) +, m_query(std::move(q)) +, m_table(m_query.get_table()) +, m_sort(std::move(s)) +, m_distinct(std::move(d)) +, m_mode(Mode::Query) +{ +} + +Results::Results(SharedRealm r, Table& table) +: m_realm(std::move(r)) +, m_mode(Mode::Table) +{ + m_table.reset(&table); +} + +Results::Results(SharedRealm r, LinkViewRef lv, util::Optional q, SortDescriptor s) +: m_realm(std::move(r)) +, m_link_view(lv) +, m_sort(std::move(s)) +, m_mode(Mode::LinkView) +{ + m_table.reset(&lv->get_target_table()); + if (q) { + m_query = std::move(*q); + m_mode = Mode::Query; + } +} + +Results::Results(SharedRealm r, TableView tv, SortDescriptor s, SortDescriptor d) +: m_realm(std::move(r)) +, m_table_view(std::move(tv)) +, m_sort(std::move(s)) +, m_distinct(std::move(d)) +, m_mode(Mode::TableView) +{ + m_table.reset(&m_table_view.get_parent()); +} + +Results::Results(const Results&) = default; +Results& Results::operator=(const Results&) = default; + +Results::Results(Results&& other) +: m_realm(std::move(other.m_realm)) +, m_object_schema(std::move(other.m_object_schema)) +, m_query(std::move(other.m_query)) +, m_table_view(std::move(other.m_table_view)) +, m_link_view(std::move(other.m_link_view)) +, m_table(std::move(other.m_table)) +, m_sort(std::move(other.m_sort)) +, m_distinct(std::move(other.m_distinct)) +, m_notifier(std::move(other.m_notifier)) +, m_mode(other.m_mode) +, m_update_policy(other.m_update_policy) +, m_has_used_table_view(other.m_has_used_table_view) +, m_wants_background_updates(other.m_wants_background_updates) +{ + if (m_notifier) { + m_notifier->target_results_moved(other, *this); + } +} + +Results& Results::operator=(Results&& other) +{ + this->~Results(); + new (this) Results(std::move(other)); + return *this; +} + +bool Results::is_valid() const +{ + if (m_realm) + m_realm->verify_thread(); + + if (m_table && !m_table->is_attached()) + return false; + + return true; +} + +void Results::validate_read() const +{ + // is_valid ensures that we're on the correct thread. + if (!is_valid()) + throw InvalidatedException(); +} + +void Results::validate_write() const +{ + validate_read(); + if (!m_realm || !m_realm->is_in_transaction()) + throw InvalidTransactionException("Must be in a write transaction"); +} + +size_t Results::size() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: return 0; + case Mode::Table: return m_table->size(); + case Mode::LinkView: return m_link_view->size(); + case Mode::Query: + m_query.sync_view_if_needed(); + if (!m_distinct) + return m_query.count(); + REALM_FALLTHROUGH; + case Mode::TableView: + update_tableview(); + return m_table_view.size(); + } + REALM_UNREACHABLE(); +} + +const ObjectSchema& Results::get_object_schema() const +{ + validate_read(); + + if (!m_object_schema) { + REALM_ASSERT(m_realm); + auto it = m_realm->schema().find(get_object_type()); + REALM_ASSERT(it != m_realm->schema().end()); + m_object_schema = &*it; + } + + return *m_object_schema; +} + + +StringData Results::get_object_type() const noexcept +{ + if (!m_table) { + return StringData(); + } + + return ObjectStore::object_type_for_table_name(m_table->get_name()); +} + +RowExpr Results::get(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: break; + case Mode::Table: + if (row_ndx < m_table->size()) + return m_table->get(row_ndx); + break; + case Mode::LinkView: + if (update_linkview()) { + if (row_ndx < m_link_view->size()) + return m_link_view->get(row_ndx); + break; + } + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + if (row_ndx >= m_table_view.size()) + break; + if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx)) + return {}; + return m_table_view.get(row_ndx); + } + + throw OutOfBoundsIndexException{row_ndx, size()}; +} + +util::Optional Results::first() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->front()); + case Mode::LinkView: + if (update_linkview()) + return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(0)); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + if (m_table_view.size() == 0) + return util::none; + else if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(0)) + return RowExpr(); + return m_table_view.front(); + } + REALM_UNREACHABLE(); +} + +util::Optional Results::last() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->back()); + case Mode::LinkView: + if (update_linkview()) + return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(m_link_view->size() - 1)); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + auto s = m_table_view.size(); + if (s == 0) + return util::none; + else if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(s - 1)) + return RowExpr(); + return m_table_view.back(); + } + REALM_UNREACHABLE(); +} + +bool Results::update_linkview() +{ + REALM_ASSERT(m_update_policy == UpdatePolicy::Auto); + + if (m_sort || m_distinct) { + m_query = get_query(); + m_mode = Mode::Query; + update_tableview(); + return false; + } + return true; +} + +void Results::update_tableview(bool wants_notifications) +{ + if (m_update_policy == UpdatePolicy::Never) { + REALM_ASSERT(m_mode == Mode::TableView); + return; + } + + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + case Mode::LinkView: + return; + case Mode::Query: + m_query.sync_view_if_needed(); + m_table_view = m_query.find_all(); + if (m_sort) { + m_table_view.sort(m_sort); + } + if (m_distinct) { + m_table_view.distinct(m_distinct); + } + m_mode = Mode::TableView; + REALM_FALLTHROUGH; + case Mode::TableView: + if (wants_notifications && !m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) { + m_notifier = std::make_shared<_impl::ResultsNotifier>(*this); + _impl::RealmCoordinator::register_notifier(m_notifier); + } + m_has_used_table_view = true; + m_table_view.sync_if_needed(); + break; + } +} + +size_t Results::index_of(Row const& row) +{ + validate_read(); + if (!row) { + throw DetatchedAccessorException{}; + } + if (m_table && row.get_table() != m_table) { + throw IncorrectTableException( + ObjectStore::object_type_for_table_name(m_table->get_name()), + ObjectStore::object_type_for_table_name(row.get_table()->get_name()), + "Attempting to get the index of a Row of the wrong type" + ); + } + return index_of(row.get_index()); +} + +size_t Results::index_of(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return not_found; + case Mode::Table: + return row_ndx; + case Mode::LinkView: + if (update_linkview()) + return m_link_view->find(row_ndx); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view.find_by_source_ndx(row_ndx); + } + REALM_UNREACHABLE(); +} + +template +util::Optional Results::aggregate(size_t column, + const char* name, + Int agg_int, Float agg_float, + Double agg_double, Timestamp agg_timestamp) +{ + validate_read(); + if (!m_table) + return none; + if (column > m_table->get_column_count()) + throw OutOfBoundsIndexException{column, m_table->get_column_count()}; + + auto do_agg = [&](auto const& getter) -> util::Optional { + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return util::Optional(getter(*m_table)); + case Mode::LinkView: + m_query = this->get_query(); + m_mode = Mode::Query; + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + this->update_tableview(); + return util::Optional(getter(m_table_view)); + } + + REALM_UNREACHABLE(); + }; + + switch (m_table->get_column_type(column)) + { + case type_Timestamp: return do_agg(agg_timestamp); + case type_Double: return do_agg(agg_double); + case type_Float: return do_agg(agg_float); + case type_Int: return do_agg(agg_int); + default: + throw UnsupportedColumnTypeException{column, m_table.get(), name}; + } +} + +util::Optional Results::max(size_t column) +{ + size_t return_ndx = npos; + auto results = aggregate(column, "max", + [&](auto const& table) { return table.maximum_int(column, &return_ndx); }, + [&](auto const& table) { return table.maximum_float(column, &return_ndx); }, + [&](auto const& table) { return table.maximum_double(column, &return_ndx); }, + [&](auto const& table) { return table.maximum_timestamp(column, &return_ndx); }); + return return_ndx == npos ? none : results; +} + +util::Optional Results::min(size_t column) +{ + size_t return_ndx = npos; + auto results = aggregate(column, "min", + [&](auto const& table) { return table.minimum_int(column, &return_ndx); }, + [&](auto const& table) { return table.minimum_float(column, &return_ndx); }, + [&](auto const& table) { return table.minimum_double(column, &return_ndx); }, + [&](auto const& table) { return table.minimum_timestamp(column, &return_ndx); }); + return return_ndx == npos ? none : results; +} + +util::Optional Results::sum(size_t column) +{ + return aggregate(column, "sum", + [=](auto const& table) { return table.sum_int(column); }, + [=](auto const& table) { return table.sum_float(column); }, + [=](auto const& table) { return table.sum_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table.get(), "sum"}; }); +} + +util::Optional Results::average(size_t column) +{ + // Initial value to make gcc happy + size_t value_count = 0; + auto results = aggregate(column, "average", + [&](auto const& table) { return table.average_int(column, &value_count); }, + [&](auto const& table) { return table.average_float(column, &value_count); }, + [&](auto const& table) { return table.average_double(column, &value_count); }, + [&](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table.get(), "average"}; }); + return value_count == 0 ? none : results; +} + +void Results::clear() +{ + switch (m_mode) { + case Mode::Empty: + return; + case Mode::Table: + validate_write(); + m_table->clear(); + break; + case Mode::Query: + // Not using Query:remove() because building the tableview and + // clearing it is actually significantly faster + case Mode::TableView: + validate_write(); + update_tableview(); + + switch (m_update_policy) { + case UpdatePolicy::Auto: + m_table_view.clear(RemoveMode::unordered); + break; + case UpdatePolicy::Never: { + // Copy the TableView because a frozen Results shouldn't let its size() change. + TableView copy(m_table_view); + copy.clear(RemoveMode::unordered); + break; + } + } + break; + case Mode::LinkView: + validate_write(); + m_link_view->remove_all_target_rows(); + break; + } +} + +Query Results::get_query() const +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Query: + return m_query; + case Mode::TableView: { + // A TableView has an associated Query if it was produced by Query::find_all. This is indicated + // by TableView::get_query returning a Query with a non-null table. + Query query = m_table_view.get_query(); + if (query.get_table()) { + return query; + } + + // The TableView has no associated query so create one with no conditions that is restricted + // to the rows in the TableView. + if (m_update_policy == UpdatePolicy::Auto) { + m_table_view.sync_if_needed(); + } + return Query(*m_table, std::unique_ptr(new TableView(m_table_view))); + } + case Mode::LinkView: + return m_table->where(m_link_view); + case Mode::Table: + return m_table->where(); + } + REALM_UNREACHABLE(); +} + +TableView Results::get_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return {}; + case Mode::LinkView: + if (update_linkview()) + return m_table->where(m_link_view).find_all(); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view; + case Mode::Table: + return m_table->where().find_all(); + } + REALM_UNREACHABLE(); +} + +Results Results::sort(realm::SortDescriptor&& sort) const +{ + return Results(m_realm, get_query(), std::move(sort), m_distinct); +} + +Results Results::filter(Query&& q) const +{ + return Results(m_realm, get_query().and_query(std::move(q)), m_sort, m_distinct); +} + + +// FIXME: The current implementation of distinct() breaks the Results API. +// This is tracked by the following issues: +// - https://github.com/realm/realm-object-store/issues/266 +// - https://github.com/realm/realm-core/issues/2332 +Results Results::distinct(realm::SortDescriptor&& uniqueness) +{ + auto tv = get_tableview(); + tv.distinct(uniqueness); + return Results(m_realm, std::move(tv), m_sort, std::move(uniqueness)); +} + +Results Results::snapshot() const & +{ + validate_read(); + + return Results(*this).snapshot(); +} + +Results Results::snapshot() && +{ + validate_read(); + + switch (m_mode) { + case Mode::Empty: + return Results(); + + case Mode::Table: + case Mode::LinkView: + m_query = get_query(); + m_mode = Mode::Query; + + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(false); + m_notifier.reset(); + m_update_policy = UpdatePolicy::Never; + return std::move(*this); + } + REALM_UNREACHABLE(); +} + +void Results::prepare_async() +{ + if (m_notifier) { + return; + } + if (m_realm->config().read_only()) { + throw InvalidTransactionException("Cannot create asynchronous query for read-only Realms"); + } + if (m_realm->is_in_transaction()) { + throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction"); + } + if (m_update_policy == UpdatePolicy::Never) { + throw std::logic_error("Cannot create asynchronous query for snapshotted Results."); + } + + m_wants_background_updates = true; + m_notifier = std::make_shared<_impl::ResultsNotifier>(*this); + _impl::RealmCoordinator::register_notifier(m_notifier); +} + +NotificationToken Results::async(std::function target) +{ + prepare_async(); + auto wrap = [=](CollectionChangeSet, std::exception_ptr e) { target(e); }; + return {m_notifier, m_notifier->add_callback(wrap)}; +} + +NotificationToken Results::add_notification_callback(CollectionChangeCallback cb) & +{ + prepare_async(); + return {m_notifier, m_notifier->add_callback(std::move(cb))}; +} + +bool Results::is_in_table_order() const +{ + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + return true; + case Mode::LinkView: + return false; + case Mode::Query: + return m_query.produces_results_in_table_order() && !m_sort; + case Mode::TableView: + return m_table_view.is_in_table_order(); + } + REALM_UNREACHABLE(); // keep gcc happy +} + +void Results::Internal::set_table_view(Results& results, realm::TableView &&tv) +{ + REALM_ASSERT(results.m_update_policy != UpdatePolicy::Never); + // If the previous TableView was never actually used, then stop generating + // new ones until the user actually uses the Results object again + if (results.m_mode == Mode::TableView) { + results.m_wants_background_updates = results.m_has_used_table_view; + } + + results.m_table_view = std::move(tv); + results.m_mode = Mode::TableView; + results.m_has_used_table_view = false; + REALM_ASSERT(results.m_table_view.is_in_sync()); + REALM_ASSERT(results.m_table_view.is_attached()); +} + +Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +, requested(r), valid_count(c) {} + +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation) +: std::logic_error(util::format("Cannot %1 property '%2': operation not supported for '%3' properties", + operation, table->get_column_name(column), + string_for_property_type(static_cast(table->get_column_type(column))))) +, column_index(column) +, column_name(table->get_column_name(column)) +, column_type(table->get_column_type(column)) +{ +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp new file mode 100644 index 0000000..a204eb0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp @@ -0,0 +1,242 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "schema.hpp" + +#include "object_schema.hpp" +#include "object_store.hpp" +#include "object_schema.hpp" +#include "property.hpp" + +#include + +using namespace realm; + +namespace realm { +bool operator==(Schema const& a, Schema const& b) +{ + return static_cast(a) == static_cast(b); +} +} + +Schema::Schema() = default; +Schema::~Schema() = default; +Schema::Schema(Schema const&) = default; +Schema::Schema(Schema &&) = default; +Schema& Schema::operator=(Schema const&) = default; +Schema& Schema::operator=(Schema&&) = default; + +Schema::Schema(std::initializer_list types) : Schema(base(types)) { } + +Schema::Schema(base types) : base(std::move(types)) +{ + std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) { + return lft.name < rgt.name; + }); +} + +Schema::iterator Schema::find(StringData name) +{ + auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) { + return lft.name < rgt; + }); + if (it != end() && it->name != name) { + it = end(); + } + return it; +} + +Schema::const_iterator Schema::find(StringData name) const +{ + return const_cast(this)->find(name); +} + +Schema::iterator Schema::find(ObjectSchema const& object) noexcept +{ + return find(object.name); +} + +Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept +{ + return const_cast(this)->find(object); +} + +void Schema::validate() const +{ + std::vector exceptions; + for (auto const& object : *this) { + object.validate(*this, exceptions); + } + + if (exceptions.size()) { + throw SchemaValidationException(exceptions); + } +} + +namespace { +struct IsNotRemoveProperty { + bool operator()(SchemaChange sc) const { return sc.visit(*this); } + bool operator()(schema_change::RemoveProperty) const { return false; } + template bool operator()(T) const { return true; } +}; +struct GetRemovedColumn { + size_t operator()(SchemaChange sc) const { return sc.visit(*this); } + size_t operator()(schema_change::RemoveProperty p) const { return p.property->table_column; } + template size_t operator()(T) const { REALM_COMPILER_HINT_UNREACHABLE(); } +}; +} + +static void compare(ObjectSchema const& existing_schema, + ObjectSchema const& target_schema, + std::vector& changes) +{ + for (auto& current_prop : existing_schema.persisted_properties) { + auto target_prop = target_schema.property_for_name(current_prop.name); + + if (!target_prop) { + changes.emplace_back(schema_change::RemoveProperty{&existing_schema, ¤t_prop}); + continue; + } + if (target_schema.property_is_computed(*target_prop)) { + changes.emplace_back(schema_change::RemoveProperty{&existing_schema, ¤t_prop}); + continue; + } + if (current_prop.type != target_prop->type || current_prop.object_type != target_prop->object_type) { + changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, ¤t_prop, target_prop}); + continue; + } + if (current_prop.is_nullable != target_prop->is_nullable) { + if (current_prop.is_nullable) + changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, ¤t_prop}); + else + changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, ¤t_prop}); + } + if (target_prop->requires_index()) { + if (!current_prop.is_indexed) + changes.emplace_back(schema_change::AddIndex{&existing_schema, ¤t_prop}); + } + else if (current_prop.requires_index()) { + changes.emplace_back(schema_change::RemoveIndex{&existing_schema, ¤t_prop}); + } + } + + if (existing_schema.primary_key != target_schema.primary_key) { + changes.emplace_back(schema_change::ChangePrimaryKey{&existing_schema, target_schema.primary_key_property()}); + } + + for (auto& target_prop : target_schema.persisted_properties) { + if (!existing_schema.property_for_name(target_prop.name)) { + changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop}); + } + } + + // Move all RemovePropertys to the end and sort in descending order of + // column index, as removing a column will shift all columns after that one + auto it = std::partition(begin(changes), end(changes), IsNotRemoveProperty{}); + std::sort(it, end(changes), + [](auto a, auto b) { return GetRemovedColumn()(a) > GetRemovedColumn()(b); }); +} + +template +void Schema::zip_matching(T&& a, U&& b, Func&& func) +{ + size_t i = 0, j = 0; + while (i < a.size() && j < b.size()) { + auto& object_schema = a[i]; + auto& matching_schema = b[j]; + int cmp = object_schema.name.compare(matching_schema.name); + if (cmp == 0) { + func(&object_schema, &matching_schema); + ++i, ++j; + } + else if (cmp < 0) { + func(&object_schema, nullptr); + ++i; + } + else { + func(nullptr, &matching_schema); + ++j; + } + } + for (; i < a.size(); ++i) + func(&a[i], nullptr); + for (; j < b.size(); ++j) + func(nullptr, &b[j]); + +} + +std::vector Schema::compare(Schema const& target_schema) const +{ + std::vector changes; + zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) { + if (target && existing) + ::compare(*existing, *target, changes); + else if (target) + changes.emplace_back(schema_change::AddTable{target}); + // nothing for tables in existing but not target + }); + return changes; +} + +void Schema::copy_table_columns_from(realm::Schema const& other) +{ + zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) { + if (!existing || !other) + return; + + for (auto& current_prop : other->persisted_properties) { + auto target_prop = existing->property_for_name(current_prop.name); + if (target_prop) { + target_prop->table_column = current_prop.table_column; + } + } + }); +} + +namespace realm { +bool operator==(SchemaChange const& lft, SchemaChange const& rgt) +{ + if (lft.m_kind != rgt.m_kind) + return false; + + using namespace schema_change; + struct Visitor { + SchemaChange const& value; + + #define REALM_SC_COMPARE(type, ...) \ + bool operator()(type rgt) const \ + { \ + auto cmp = [](auto&& v) { return std::tie(__VA_ARGS__); }; \ + return cmp(value.type) == cmp(rgt); \ + } + + REALM_SC_COMPARE(AddIndex, v.object, v.property) + REALM_SC_COMPARE(AddProperty, v.object, v.property) + REALM_SC_COMPARE(AddTable, v.object) + REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property) + REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property) + REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property) + REALM_SC_COMPARE(MakePropertyRequired, v.object, v.property) + REALM_SC_COMPARE(RemoveIndex, v.object, v.property) + REALM_SC_COMPARE(RemoveProperty, v.object, v.property) + + #undef REALM_SC_COMPARE + } visitor{lft}; + return rgt.visit(visitor); +} +} // namespace realm diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp new file mode 100644 index 0000000..66e4cef --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp @@ -0,0 +1,871 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "shared_realm.hpp" + +#include "impl/collection_notifier.hpp" +#include "impl/realm_coordinator.hpp" +#include "impl/transact_log_handler.hpp" + +#include "binding_context.hpp" +#include "list.hpp" +#include "object.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "results.hpp" +#include "schema.hpp" +#include "thread_safe_reference.hpp" + +#include "util/compiler.hpp" +#include "util/format.hpp" + +#include +#include + +#if REALM_ENABLE_SYNC +#include +#endif + +using namespace realm; +using namespace realm::_impl; + +Realm::Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator) +: m_config(std::move(config)) +, m_execution_context(m_config.execution_context) +{ + open_with_config(m_config, m_history, m_shared_group, m_read_only_group, this); + + if (m_read_only_group) { + m_group = m_read_only_group.get(); + m_schema_version = ObjectStore::get_schema_version(*m_group); + m_schema = ObjectStore::schema_from_group(*m_group); + } + else if (!coordinator || !coordinator->get_cached_schema(m_schema, m_schema_version, m_schema_transaction_version)) { + read_group(); + if (coordinator) + coordinator->cache_schema(m_schema, m_schema_version, m_schema_transaction_version); + m_shared_group->end_read(); + m_group = nullptr; + } + + m_coordinator = std::move(coordinator); +} + +REALM_NOINLINE static void translate_file_exception(StringData path, bool read_only=false) +{ + try { + throw; + } + catch (util::File::PermissionDenied const& ex) { + throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), + util::format("Unable to open a realm at path '%1'. Please use a path where your app has %2 permissions.", + ex.get_path(), read_only ? "read" : "read-write"), + ex.what()); + } + catch (util::File::Exists const& ex) { + throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(), + util::format("File at path '%1' already exists.", ex.get_path()), + ex.what()); + } + catch (util::File::NotFound const& ex) { + throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(), + util::format("Directory at path '%1' does not exist.", ex.get_path()), ex.what()); + } + catch (util::File::AccessError const& ex) { + // Errors for `open()` include the path, but other errors don't. We + // don't want two copies of the path in the error, so strip it out if it + // appears, and then include it in our prefix. + std::string underlying = ex.what(); + RealmFileException::Kind error_kind = RealmFileException::Kind::AccessError; + // FIXME: Replace this with a proper specific exception type once Core adds support for it. + if (underlying == "Bad or incompatible history type") + error_kind = RealmFileException::Kind::BadHistoryError; + auto pos = underlying.find(ex.get_path()); + if (pos != std::string::npos && pos > 0) { + // One extra char at each end for the quotes + underlying.replace(pos - 1, ex.get_path().size() + 2, ""); + } + throw RealmFileException(error_kind, ex.get_path(), + util::format("Unable to open a realm at path '%1': %2.", ex.get_path(), underlying), ex.what()); + } + catch (IncompatibleLockFile const& ex) { + throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, path, + "Realm file is currently open in another process " + "which cannot share access with this process. " + "All processes sharing a single file must be the same architecture.", + ex.what()); + } + catch (FileFormatUpgradeRequired const& ex) { + throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, path, + "The Realm file format must be allowed to be upgraded " + "in order to proceed.", + ex.what()); + } +} + +void Realm::open_with_config(const Config& config, + std::unique_ptr& history, + std::unique_ptr& shared_group, + std::unique_ptr& read_only_group, + Realm* realm) +{ + try { + if (config.read_only()) { + if (config.realm_data.is_null()) { + read_only_group = std::make_unique(config.path, config.encryption_key.data(), Group::mode_ReadOnly); + } + else { + // Create in-memory read-only realm from existing buffer (without taking ownership of the buffer) + read_only_group = std::make_unique(config.realm_data, false); + } + } + else { + bool server_synchronization_mode = bool(config.sync_config) || config.force_sync_history; + if (server_synchronization_mode) { +#if REALM_ENABLE_SYNC + history = realm::sync::make_client_history(config.path); +#else + REALM_TERMINATE("Realm was not built with sync enabled"); +#endif + } + else { + history = realm::make_in_realm_history(config.path); + } + + SharedGroupOptions options; + options.durability = config.in_memory ? SharedGroupOptions::Durability::MemOnly : + SharedGroupOptions::Durability::Full; + options.encryption_key = config.encryption_key.data(); + options.allow_file_format_upgrade = !config.disable_format_upgrade && + config.schema_mode != SchemaMode::ResetFile; + options.upgrade_callback = [&](int from_version, int to_version) { + if (realm) { + realm->upgrade_initial_version = from_version; + realm->upgrade_final_version = to_version; + } + }; + shared_group = std::make_unique(*history, options); + +#if !WIN32 + if (config.should_compact_on_launch_function) { + size_t free_space = -1; + size_t used_space = -1; + // getting stats requires committing a write transaction beforehand. + Group* group = nullptr; + if (shared_group->try_begin_write(group)) { + shared_group->commit(); + shared_group->get_stats(free_space, used_space); + if (config.should_compact_on_launch_function(free_space + used_space, used_space)) + realm->compact(); + } + } +#endif + } + } + catch (realm::FileFormatUpgradeRequired const&) { + if (config.schema_mode != SchemaMode::ResetFile) { + translate_file_exception(config.path, config.read_only()); + } + util::File::remove(config.path); + open_with_config(config, history, shared_group, read_only_group, realm); + } + catch (...) { + translate_file_exception(config.path, config.read_only()); + } +} + +Realm::~Realm() +{ + if (m_coordinator) { + m_coordinator->unregister_realm(this); + } +} + +Group& Realm::read_group() +{ + verify_open(); + + if (!m_group) + begin_read(VersionID{}); + return *m_group; +} + +void Realm::Internal::begin_read(Realm& realm, VersionID version_id) +{ + realm.begin_read(version_id); +} + +void Realm::begin_read(VersionID version_id) +{ + REALM_ASSERT(!m_group); + m_group = &const_cast(m_shared_group->begin_read(version_id)); + add_schema_change_handler(); + read_schema_from_group_if_needed(); +} + +SharedRealm Realm::get_shared_realm(Config config) +{ + auto coordinator = RealmCoordinator::get_coordinator(config.path); + return coordinator->get_realm(std::move(config)); +} + +void Realm::set_schema(Schema const& reference, Schema schema) +{ + m_dynamic_schema = false; + schema.copy_table_columns_from(reference); + m_schema = std::move(schema); +} + +void Realm::read_schema_from_group_if_needed() +{ + REALM_ASSERT(!m_read_only_group); + + Group& group = read_group(); + auto current_version = m_shared_group->get_version_of_current_transaction().version; + if (m_schema_transaction_version == current_version) + return; + + m_schema_transaction_version = current_version; + m_schema_version = ObjectStore::get_schema_version(group); + auto schema = ObjectStore::schema_from_group(group); + if (m_coordinator) + m_coordinator->cache_schema(schema, m_schema_version, + m_schema_transaction_version); + + if (m_dynamic_schema) { + m_schema = std::move(schema); + } + else { + ObjectStore::verify_valid_external_changes(m_schema.compare(schema)); + m_schema.copy_table_columns_from(schema); + } +} + +bool Realm::reset_file(Schema& schema, std::vector& required_changes) +{ + // FIXME: this does not work if multiple processes try to open the file at + // the same time, or even multiple threads if there is not any external + // synchronization. The latter is probably fixable, but making it + // multi-process-safe requires some sort of multi-process exclusive lock + m_group = nullptr; + m_shared_group = nullptr; + m_history = nullptr; + util::File::remove(m_config.path); + + open_with_config(m_config, m_history, m_shared_group, m_read_only_group, this); + m_schema = ObjectStore::schema_from_group(read_group()); + m_schema_version = ObjectStore::get_schema_version(read_group()); + required_changes = m_schema.compare(schema); + m_coordinator->clear_schema_cache_and_set_schema_version(m_schema_version); + return false; +} + +bool Realm::schema_change_needs_write_transaction(Schema& schema, + std::vector& changes, + uint64_t version) +{ + if (version == m_schema_version && changes.empty()) + return false; + + switch (m_config.schema_mode) { + case SchemaMode::Automatic: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) + throw InvalidSchemaVersionException(m_schema_version, version); + return true; + + case SchemaMode::ReadOnly: + if (version != m_schema_version) + throw InvalidSchemaVersionException(m_schema_version, version); + ObjectStore::verify_compatible_for_read_only(changes); + return false; + + case SchemaMode::ResetFile: + if (m_schema_version == ObjectStore::NotVersioned) + return true; + if (m_schema_version == version && !ObjectStore::needs_migration(changes)) + return true; + reset_file(schema, changes); + return true; + + case SchemaMode::Additive: { + bool will_apply_index_changes = version > m_schema_version; + if (ObjectStore::verify_valid_additive_changes(changes, will_apply_index_changes)) + return true; + return version != m_schema_version; + } + + case SchemaMode::Manual: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) + throw InvalidSchemaVersionException(m_schema_version, version); + if (version == m_schema_version) { + ObjectStore::verify_no_changes_required(changes); + REALM_UNREACHABLE(); // changes is non-empty so above line always throws + } + return true; + } + REALM_COMPILER_HINT_UNREACHABLE(); +} + +Schema Realm::get_full_schema() +{ + if (!m_read_only_group) + refresh(); + + // If the user hasn't specified a schema previously then m_schema is always + // the full schema + if (m_dynamic_schema) + return m_schema; + + // Otherwise we may have a subset of the file's schema, so we need to get + // the complete thing to calculate what changes to make + if (m_read_only_group) + return ObjectStore::schema_from_group(read_group()); + + Schema actual_schema; + uint64_t actual_version; + uint64_t transaction = -1; + bool got_cached = m_coordinator->get_cached_schema(actual_schema, actual_version, transaction); + if (!got_cached || transaction != m_shared_group->get_version_of_current_transaction().version) + return ObjectStore::schema_from_group(read_group()); + return actual_schema; +} + +void Realm::set_schema_subset(Schema schema) +{ + REALM_ASSERT(m_dynamic_schema); + REALM_ASSERT(m_schema_version != ObjectStore::NotVersioned); + + std::vector changes = m_schema.compare(schema); + switch (m_config.schema_mode) { + case SchemaMode::Automatic: + case SchemaMode::ResetFile: + ObjectStore::verify_no_migration_required(changes); + break; + + case SchemaMode::ReadOnly: + ObjectStore::verify_compatible_for_read_only(changes); + break; + + case SchemaMode::Additive: + ObjectStore::verify_valid_additive_changes(changes); + break; + + case SchemaMode::Manual: + ObjectStore::verify_no_changes_required(changes); + break; + } + + set_schema(m_schema, std::move(schema)); +} + +void Realm::update_schema(Schema schema, uint64_t version, + MigrationFunction migration_function, bool in_transaction) +{ + schema.validate(); + + Schema actual_schema = get_full_schema(); + std::vector required_changes = actual_schema.compare(schema); + + if (!schema_change_needs_write_transaction(schema, required_changes, version)) { + set_schema(actual_schema, std::move(schema)); + return; + } + // Either the schema version has changed or we need to do non-migration changes + + if (!in_transaction) { + transaction::begin_without_validation(*m_shared_group); + + // Beginning the write transaction may have advanced the version and left + // us with nothing to do if someone else initialized the schema on disk + if (m_new_schema) { + actual_schema = *m_new_schema; + required_changes = actual_schema.compare(schema); + if (!schema_change_needs_write_transaction(schema, required_changes, version)) { + cancel_transaction(); + cache_new_schema(); + set_schema(actual_schema, std::move(schema)); + return; + } + } + cache_new_schema(); + } + + // Cancel the write transaction if we exit this function before committing it + auto cleanup = util::make_scope_exit([&]() noexcept { + // When in_transaction is true, caller is responsible to cancel the transaction. + if (!in_transaction && is_in_transaction()) + cancel_transaction(); + }); + + bool additive = m_config.schema_mode == SchemaMode::Additive; + if (migration_function && !additive) { + auto wrapper = [&] { + SharedRealm old_realm(new Realm(m_config, nullptr)); + // Need to open in read-write mode so that it uses a SharedGroup, but + // users shouldn't actually be able to write via the old realm + old_realm->m_config.schema_mode = SchemaMode::ReadOnly; + migration_function(old_realm, shared_from_this(), m_schema); + }; + + // migration function needs to see the target schema on the "new" Realm + std::swap(m_schema, schema); + std::swap(m_schema_version, version); + auto restore = util::make_scope_exit([&]() noexcept { + std::swap(m_schema, schema); + std::swap(m_schema_version, version); + }); + + ObjectStore::apply_schema_changes(read_group(), version, m_schema, m_schema_version, + m_config.schema_mode, required_changes, wrapper); + } + else { + ObjectStore::apply_schema_changes(read_group(), m_schema_version, schema, version, + m_config.schema_mode, required_changes); + REALM_ASSERT_DEBUG(additive || (required_changes = ObjectStore::schema_from_group(read_group()).compare(schema)).empty()); + } + + if (!in_transaction) { + commit_transaction(); + } + + m_schema = std::move(schema); + m_schema_version = ObjectStore::get_schema_version(read_group()); + m_dynamic_schema = false; + m_coordinator->clear_schema_cache_and_set_schema_version(version); +} + +void Realm::add_schema_change_handler() +{ + if (m_config.read_only()) + return; + m_group->set_schema_change_notification_handler([&] { + m_new_schema = ObjectStore::schema_from_group(read_group()); + m_schema_version = ObjectStore::get_schema_version(read_group()); + if (m_dynamic_schema) + m_schema = *m_new_schema; + else + m_schema.copy_table_columns_from(*m_new_schema); + }); +} + +void Realm::cache_new_schema() +{ + if (!m_shared_group) + return; + + auto new_version = m_shared_group->get_version_of_current_transaction().version; + if (m_coordinator) { + if (m_new_schema) + m_coordinator->cache_schema(std::move(*m_new_schema), m_schema_version, new_version); + else + m_coordinator->advance_schema_cache(m_schema_transaction_version, new_version); + } + m_schema_transaction_version = new_version; + m_new_schema = util::none; +} + +static void check_read_write(Realm *realm) +{ + if (realm->config().read_only()) { + throw InvalidTransactionException("Can't perform transactions on read-only Realms."); + } +} + +void Realm::verify_thread() const +{ + if (!m_execution_context.contains()) + return; + + auto thread_id = m_execution_context.get(); + if (thread_id != std::this_thread::get_id()) + throw IncorrectThreadException(); +} + +void Realm::verify_in_write() const +{ + if (!is_in_transaction()) { + throw InvalidTransactionException("Cannot modify managed objects outside of a write transaction."); + } +} + +void Realm::verify_open() const +{ + if (is_closed()) { + throw ClosedRealmException(); + } +} + +bool Realm::is_in_transaction() const noexcept +{ + if (!m_shared_group) { + return false; + } + return m_shared_group->get_transact_stage() == SharedGroup::transact_Writing; +} + +void Realm::begin_transaction() +{ + check_read_write(this); + verify_thread(); + + if (is_in_transaction()) { + throw InvalidTransactionException("The Realm is already in a write transaction"); + } + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); + + // If we're already in the middle of sending notifications, just begin the + // write transaction without sending more notifications. If this actually + // advances the read version this could leave the user in an inconsistent + // state, but that's unavoidable. + if (m_is_sending_notifications) { + _impl::NotifierPackage notifiers; + transaction::begin(m_shared_group, m_binding_context.get(), notifiers); + return; + } + + // make sure we have a read transaction + read_group(); + + m_is_sending_notifications = true; + auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; }); + + m_coordinator->promote_to_write(*this); + cache_new_schema(); +} + +void Realm::commit_transaction() +{ + check_read_write(this); + verify_thread(); + + if (!is_in_transaction()) { + throw InvalidTransactionException("Can't commit a non-existing write transaction"); + } + + m_coordinator->commit_write(*this); + cache_new_schema(); +} + +void Realm::cancel_transaction() +{ + check_read_write(this); + verify_thread(); + + if (!is_in_transaction()) { + throw InvalidTransactionException("Can't cancel a non-existing write transaction"); + } + + transaction::cancel(*m_shared_group, m_binding_context.get()); +} + +void Realm::invalidate() +{ + verify_open(); + verify_thread(); + check_read_write(this); + + if (m_is_sending_notifications) { + return; + } + + if (is_in_transaction()) { + cancel_transaction(); + } + if (!m_group) { + return; + } + + m_shared_group->end_read(); + m_group = nullptr; +} + +bool Realm::compact() +{ + verify_thread(); + + if (m_config.read_only()) { + throw InvalidTransactionException("Can't compact a read-only Realm"); + } + if (is_in_transaction()) { + throw InvalidTransactionException("Can't compact a Realm within a write transaction"); + } + + Group& group = read_group(); + for (auto &object_schema : m_schema) { + ObjectStore::table_for_object_type(group, object_schema.name)->optimize(); + } + m_shared_group->end_read(); + m_group = nullptr; + + return m_shared_group->compact(); +} + +void Realm::write_copy(StringData path, BinaryData key) +{ + if (key.data() && key.size() != 64) { + throw InvalidEncryptionKeyException(); + } + verify_thread(); + try { + read_group().write(path, key.data()); + } + catch (...) { + translate_file_exception(path); + } +} + +OwnedBinaryData Realm::write_copy() +{ + verify_thread(); + BinaryData buffer = read_group().write_to_mem(); + + // Since OwnedBinaryData does not have a constructor directly taking + // ownership of BinaryData, we have to do this to avoid copying the buffer + return OwnedBinaryData(std::unique_ptr((char*)buffer.data()), buffer.size()); +} + +void Realm::notify() +{ + if (is_closed() || is_in_transaction()) { + return; + } + + verify_thread(); + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); + + if (m_binding_context) { + m_binding_context->before_notify(); + } + + auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; }); + if (!m_shared_group->has_changed()) { + m_is_sending_notifications = true; + m_coordinator->process_available_async(*this); + return; + } + + if (m_binding_context) { + m_binding_context->changes_available(); + + // changes_available() may have advanced the read version, and if + // so we don't need to do anything further + if (!m_shared_group->has_changed()) + return; + } + + m_is_sending_notifications = true; + if (m_auto_refresh) { + if (m_group) { + m_coordinator->advance_to_ready(*this); + cache_new_schema(); + } + else { + if (m_binding_context) { + m_binding_context->did_change({}, {}); + } + if (!is_closed()) { + m_coordinator->process_available_async(*this); + } + } + } +} + +bool Realm::refresh() +{ + verify_thread(); + check_read_write(this); + + // can't be any new changes if we're in a write transaction + if (is_in_transaction()) { + return false; + } + // don't advance if we're already in the process of advancing as that just + // makes things needlessly complicated + if (m_is_sending_notifications) { + return false; + } + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); + + m_is_sending_notifications = true; + auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; }); + + if (m_binding_context) { + m_binding_context->before_notify(); + } + if (m_group) { + bool version_changed = m_coordinator->advance_to_latest(*this); + cache_new_schema(); + return version_changed; + } + + // No current read transaction, so just create a new one + read_group(); + m_coordinator->process_available_async(*this); + return true; +} + +bool Realm::can_deliver_notifications() const noexcept +{ + if (m_config.read_only()) { + return false; + } + + if (m_binding_context && !m_binding_context->can_deliver_notifications()) { + return false; + } + + return true; +} + +uint64_t Realm::get_schema_version(const Realm::Config &config) +{ + auto coordinator = RealmCoordinator::get_existing_coordinator(config.path); + if (coordinator) { + return coordinator->get_schema_version(); + } + + return ObjectStore::get_schema_version(Realm(config, nullptr).read_group()); +} + +void Realm::close() +{ + if (m_coordinator) { + m_coordinator->unregister_realm(this); + } + + m_group = nullptr; + m_shared_group = nullptr; + m_history = nullptr; + m_read_only_group = nullptr; + m_binding_context = nullptr; + m_coordinator = nullptr; +} + +util::Optional Realm::file_format_upgraded_from_version() const +{ + if (upgrade_initial_version != upgrade_final_version) { + return upgrade_initial_version; + } + return util::none; +} + +template +realm::ThreadSafeReference Realm::obtain_thread_safe_reference(T const& value) +{ + verify_thread(); + if (is_in_transaction()) { + throw InvalidTransactionException("Cannot obtain thread safe reference during a write transaction."); + } + return ThreadSafeReference(value); +} + +template ThreadSafeReference Realm::obtain_thread_safe_reference(Object const& value); +template ThreadSafeReference Realm::obtain_thread_safe_reference(List const& value); +template ThreadSafeReference Realm::obtain_thread_safe_reference(Results const& value); + +template +T Realm::resolve_thread_safe_reference(ThreadSafeReference reference) +{ + verify_thread(); + if (is_in_transaction()) { + throw InvalidTransactionException("Cannot resolve thread safe reference during a write transaction."); + } + if (reference.is_invalidated()) { + throw std::logic_error("Cannot resolve thread safe reference more than once."); + } + if (!reference.has_same_config(*this)) { + throw MismatchedRealmException("Cannot resolve thread safe reference in Realm with different configuration " + "than the source Realm."); + } + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); + + // Ensure we're on the same version as the reference + if (!m_group) { + // A read transaction doesn't yet exist, so create at the reference's version + begin_read(reference.m_version_id); + } + else { + // A read transaction does exist, but let's make sure that its version matches the reference's + auto current_version = m_shared_group->get_version_of_current_transaction(); + VersionID reference_version(reference.m_version_id); + + if (reference_version == current_version) { + return std::move(reference).import_into_realm(shared_from_this()); + } + + refresh(); + + current_version = m_shared_group->get_version_of_current_transaction(); + + // If the reference's version is behind, advance it to our version + if (reference_version < current_version) { + // Duplicate config for uncached Realm so we don't advance the user's Realm + Realm::Config config = m_coordinator->get_config(); + config.cache = false; + config.schema = util::none; + SharedRealm temporary_realm = m_coordinator->get_realm(config); + temporary_realm->begin_read(reference_version); + + // With reference imported, advance temporary Realm to our version + T imported_value = std::move(reference).import_into_realm(temporary_realm); + transaction::advance(*temporary_realm->m_shared_group, nullptr, current_version); + reference = ThreadSafeReference(imported_value); + } + } + + return std::move(reference).import_into_realm(shared_from_this()); +} + +template Object Realm::resolve_thread_safe_reference(ThreadSafeReference reference); +template List Realm::resolve_thread_safe_reference(ThreadSafeReference reference); +template Results Realm::resolve_thread_safe_reference(ThreadSafeReference reference); + +MismatchedConfigException::MismatchedConfigException(StringData message, StringData path) +: std::logic_error(util::format(message.data(), path)) { } + +MismatchedRealmException::MismatchedRealmException(StringData message) +: std::logic_error(message.data()) { } + +// FIXME Those are exposed for Java async queries, mainly because of handover related methods. +SharedGroup& RealmFriend::get_shared_group(Realm& realm) +{ + return *realm.m_shared_group; +} + +Group& RealmFriend::read_group_to(Realm& realm, VersionID version) +{ + if (realm.m_group && realm.m_shared_group->get_version_of_current_transaction() == version) + return *realm.m_group; + + if (realm.m_group) + realm.m_shared_group->end_read(); + realm.begin_read(version); + return *realm.m_group; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp new file mode 100644 index 0000000..9c5027a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp @@ -0,0 +1,363 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/impl/sync_file.hpp" + +#include "util/time.hpp" + +#include +#include + +#include +#include +#include + +#if WIN32 +#include +#include + +inline static int mkstemp(char* _template) { return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE); } +#else +#include +#endif + + +using File = realm::util::File; + +namespace realm { + +namespace { + +uint8_t value_of_hex_digit(char hex_digit) +{ + if (hex_digit >= '0' && hex_digit <= '9') { + return hex_digit - '0'; + } else if (hex_digit >= 'A' && hex_digit <= 'F') { + return 10 + hex_digit - 'A'; + } else if (hex_digit >= 'a' && hex_digit <= 'f') { + return 10 + hex_digit - 'a'; + } else { + throw std::invalid_argument("Cannot get the value of a character that isn't a hex digit."); + } +} + +bool filename_is_reserved(const std::string& filename) { + return (filename == "." || filename == ".."); +} + +bool character_is_unreserved(char character) +{ + bool is_capital_letter = (character >= 'A' && character <= 'Z'); + bool is_lowercase_letter = (character >= 'a' && character <= 'z'); + bool is_number = (character >= '0' && character <= '9'); + bool is_allowed_symbol = (character == '-' || character == '_' || character == '.'); + return is_capital_letter || is_lowercase_letter || is_number || is_allowed_symbol; +} + +char decoded_char_for(const std::string& percent_encoding, size_t index) +{ + if (index+2 >= percent_encoding.length()) { + throw std::invalid_argument("Malformed string: not enough characters after '%' before end of string."); + } + REALM_ASSERT(percent_encoding[index] == '%'); + return (16*value_of_hex_digit(percent_encoding[index + 1])) + value_of_hex_digit(percent_encoding[index + 2]); +} + +} // (anonymous namespace) + +namespace util { + +void remove_nonempty_dir(const std::string& path) +{ + // Open the directory and list all the files. + DIR *dir_listing = opendir(path.c_str()); + if (!dir_listing) { + return; + } + auto cleanup = util::make_scope_exit([=]() noexcept { closedir(dir_listing); }); + while (struct dirent *file = readdir(dir_listing)) { + auto file_type = file->d_type; + std::string file_name = file->d_name; + if (file_name == "." || file_name == "..") { + continue; + } + if (file_type == DT_REG || file_type == DT_FIFO) { + File::try_remove(file_path_by_appending_component(path, file_name)); + } else if (file_type == DT_DIR) { + // Directory, recurse + remove_nonempty_dir(file_path_by_appending_component(path, file_name, FilePathType::Directory)); + } + } + // Delete the directory itself + try { + util::remove_dir(path); + } + catch (File::NotFound const&) { + } +} + +std::string make_percent_encoded_string(const std::string& raw_string) +{ + std::string buffer; + buffer.reserve(raw_string.size()); + for (size_t i=0; i 0); + if (filename_is_reserved(user_identity)) { + throw std::invalid_argument("A user can't have an identifier reserved by the filesystem."); + } + auto user_path = file_path_by_appending_component(get_base_sync_directory(), + user_identity, + util::FilePathType::Directory); + util::try_make_dir(user_path); + return user_path; +} + +void SyncFileManager::remove_user_directory(const std::string& user_identity) const +{ + REALM_ASSERT(user_identity.length() > 0); + if (filename_is_reserved(user_identity)) { + throw std::invalid_argument("A user can't have an identifier reserved by the filesystem."); + } + auto user_path = file_path_by_appending_component(get_base_sync_directory(), + user_identity, + util::FilePathType::Directory); + util::remove_nonempty_dir(user_path); +} + +bool SyncFileManager::remove_realm(const std::string& absolute_path) const +{ + REALM_ASSERT(absolute_path.length() > 0); + bool success = true; + // Remove the Realm file (e.g. "example.realm"). + success = File::try_remove(absolute_path); + // Remove the lock file (e.g. "example.realm.lock"). + auto lock_path = util::file_path_by_appending_extension(absolute_path, "lock"); + success = File::try_remove(lock_path); + // Remove the management directory (e.g. "example.realm.management"). + auto management_path = util::file_path_by_appending_extension(absolute_path, "management"); + try { + util::remove_nonempty_dir(management_path); + } + catch (File::NotFound const&) { + } + catch (File::AccessError const&) { + success = false; + } + return success; +} + +bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::string& new_path) const +{ + REALM_ASSERT(old_path.length() > 0); + try { + if (File::exists(new_path)) { + return false; + } + File::copy(old_path, new_path); + } + catch (File::NotFound const&) { + return false; + } + catch (File::AccessError const&) { + return false; + } + return true; +} + +bool SyncFileManager::remove_realm(const std::string& user_identity, const std::string& raw_realm_path) const +{ + REALM_ASSERT(user_identity.length() > 0); + REALM_ASSERT(raw_realm_path.length() > 0); + if (filename_is_reserved(user_identity) || filename_is_reserved(raw_realm_path)) { + throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem."); + } + auto escaped = util::make_percent_encoded_string(raw_realm_path); + auto realm_path = util::file_path_by_appending_component(user_directory(user_identity), escaped); + return remove_realm(realm_path); +} + +std::string SyncFileManager::path(const std::string& user_identity, const std::string& raw_realm_path) const +{ + REALM_ASSERT(user_identity.length() > 0); + REALM_ASSERT(raw_realm_path.length() > 0); + if (filename_is_reserved(user_identity) || filename_is_reserved(raw_realm_path)) { + throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem."); + } + auto escaped = util::make_percent_encoded_string(raw_realm_path); + auto realm_path = util::file_path_by_appending_component(user_directory(user_identity), escaped); + return realm_path; +} + +std::string SyncFileManager::metadata_path() const +{ + auto dir_path = file_path_by_appending_component(get_utility_directory(), + c_metadata_directory, + util::FilePathType::Directory); + util::try_make_dir(dir_path); + return util::file_path_by_appending_component(dir_path, c_metadata_realm); +} + +bool SyncFileManager::remove_metadata_realm() const +{ + auto dir_path = file_path_by_appending_component(get_utility_directory(), + c_metadata_directory, + util::FilePathType::Directory); + try { + util::remove_nonempty_dir(dir_path); + return true; + } + catch (File::AccessError const&) { + return false; + } +} + +} // realm diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp new file mode 100644 index 0000000..18b6bd2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp @@ -0,0 +1,403 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/impl/sync_metadata.hpp" + +#include "object_schema.hpp" +#include "object_store.hpp" +#include "property.hpp" +#include "results.hpp" +#include "schema.hpp" +#if REALM_PLATFORM_APPLE +#include "impl/apple/keychain_helper.hpp" +#endif + +#include +#include + +namespace realm { + +static const char * const c_sync_userMetadata = "UserMetadata"; +static const char * const c_sync_marked_for_removal = "marked_for_removal"; +static const char * const c_sync_identity = "identity"; +static const char * const c_sync_auth_server_url = "auth_server_url"; +static const char * const c_sync_user_token = "user_token"; +static const char * const c_sync_user_is_admin = "user_is_admin"; + +static const char * const c_sync_fileActionMetadata = "FileActionMetadata"; +static const char * const c_sync_original_name = "original_name"; +static const char * const c_sync_new_name = "new_name"; +static const char * const c_sync_action = "action"; +static const char * const c_sync_url = "url"; + +namespace { + +Property make_nullable_string_property(const char* name) +{ + Property p = {name, PropertyType::String}; + p.is_nullable = true; + return p; +} + +Property make_primary_key_property(const char* name) +{ + Property p = {name, PropertyType::String}; + p.is_indexed = true; + p.is_primary = true; + return p; +} + +Schema make_schema() +{ + return Schema{ + {c_sync_userMetadata, { + make_primary_key_property(c_sync_identity), + {c_sync_marked_for_removal, PropertyType::Bool}, + make_nullable_string_property(c_sync_auth_server_url), + make_nullable_string_property(c_sync_user_token), + {c_sync_user_is_admin, PropertyType::Bool}, + }}, + {c_sync_fileActionMetadata, { + make_primary_key_property(c_sync_original_name), + {c_sync_action, PropertyType::Int}, + make_nullable_string_property(c_sync_new_name), + {c_sync_url, PropertyType::String}, + {c_sync_identity, PropertyType::String}, + }}, + }; +} + +} + +// MARK: - Sync metadata manager + +SyncMetadataManager::SyncMetadataManager(std::string path, + bool should_encrypt, + util::Optional> encryption_key) +{ + constexpr uint64_t SCHEMA_VERSION = 1; + std::lock_guard lock(m_metadata_lock); + + Realm::Config config; + config.path = std::move(path); + config.schema = make_schema(); + config.schema_version = SCHEMA_VERSION; + config.schema_mode = SchemaMode::Automatic; +#if REALM_PLATFORM_APPLE + if (should_encrypt && !encryption_key) { + encryption_key = keychain::metadata_realm_encryption_key(); + } +#endif + if (should_encrypt) { + if (!encryption_key) { + throw std::invalid_argument("Metadata Realm encryption was specified, but no encryption key was provided."); + } + config.encryption_key = std::move(*encryption_key); + } + + // Open the Realm. + SharedRealm realm = Realm::get_shared_realm(config); + + // Get data about the (hardcoded) schemas. + DescriptorRef descriptor = ObjectStore::table_for_object_type(realm->read_group(), + c_sync_userMetadata)->get_descriptor(); + m_user_schema = { + descriptor->get_column_index(c_sync_identity), + descriptor->get_column_index(c_sync_marked_for_removal), + descriptor->get_column_index(c_sync_user_token), + descriptor->get_column_index(c_sync_auth_server_url), + descriptor->get_column_index(c_sync_user_is_admin), + }; + + descriptor = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata)->get_descriptor(); + m_file_action_schema = { + descriptor->get_column_index(c_sync_original_name), + descriptor->get_column_index(c_sync_new_name), + descriptor->get_column_index(c_sync_action), + descriptor->get_column_index(c_sync_url), + descriptor->get_column_index(c_sync_identity) + }; + + m_metadata_config = std::move(config); +} + +Realm::Config SyncMetadataManager::get_configuration() const +{ + std::lock_guard lock(m_metadata_lock); + return m_metadata_config; +} + +SyncUserMetadataResults SyncMetadataManager::all_unmarked_users() const +{ + return get_users(false); +} + +SyncUserMetadataResults SyncMetadataManager::all_users_marked_for_removal() const +{ + return get_users(true); +} + +SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const +{ + // Open the Realm. + SharedRealm realm = Realm::get_shared_realm(get_configuration()); + + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); + Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked); + + Results results(realm, std::move(query)); + return SyncUserMetadataResults(std::move(results), std::move(realm), m_user_schema); +} + +SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const +{ + SharedRealm realm = Realm::get_shared_realm(get_configuration()); + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); + Results results(realm, table->where()); + return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema); +} + +// MARK: - Sync user metadata + +SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row) +: m_invalid(row.get_bool(schema.idx_marked_for_removal)) +, m_schema(std::move(schema)) +, m_realm(std::move(realm)) +, m_row(row) +{ } + +SyncUserMetadata::SyncUserMetadata(const SyncMetadataManager& manager, std::string identity, bool make_if_absent) +: m_schema(manager.m_user_schema) +{ + // Open the Realm. + m_realm = Realm::get_shared_realm(manager.get_configuration()); + + // Retrieve or create the row for this object. + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); + size_t row_idx = table->find_first_string(m_schema.idx_identity, identity); + if (row_idx == not_found) { + if (!make_if_absent) { + m_invalid = true; + m_realm = nullptr; + return; + } + m_realm->begin_transaction(); + row_idx = table->find_first_string(m_schema.idx_identity, identity); + if (row_idx == not_found) { + row_idx = table->add_empty_row(); + table->set_string(m_schema.idx_identity, row_idx, identity); + table->set_bool(m_schema.idx_user_is_admin, row_idx, false); + m_realm->commit_transaction(); + } else { + // Someone beat us to adding this user. + m_realm->cancel_transaction(); + } + } + m_row = table->get(row_idx); + if (make_if_absent) { + // User existed in the table, but had been marked for deletion. Unmark it. + m_realm->begin_transaction(); + table->set_bool(m_schema.idx_marked_for_removal, row_idx, false); + m_realm->commit_transaction(); + m_invalid = false; + } else { + m_invalid = m_row.get_bool(m_schema.idx_marked_for_removal); + } +} + +bool SyncUserMetadata::is_valid() const +{ + return !m_invalid; +} + +std::string SyncUserMetadata::identity() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + StringData result = m_row.get_string(m_schema.idx_identity); + return result; +} + +bool SyncUserMetadata::is_admin() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_bool(m_schema.idx_user_is_admin); +} + +util::Optional SyncUserMetadata::get_optional_string_field(size_t col_idx) const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + StringData result = m_row.get_string(col_idx); + return result.is_null() ? util::none : util::make_optional(std::string(result)); +} + +util::Optional SyncUserMetadata::server_url() const +{ + return get_optional_string_field(m_schema.idx_auth_server_url); +} + +util::Optional SyncUserMetadata::user_token() const +{ + return get_optional_string_field(m_schema.idx_user_token); +} + +void SyncUserMetadata::set_state(util::Optional server_url, util::Optional user_token) +{ + if (m_invalid) { + return; + } + REALM_ASSERT_DEBUG(m_realm); + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_string(m_schema.idx_user_token, *user_token); + m_row.set_string(m_schema.idx_auth_server_url, *server_url); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::set_is_admin(bool is_admin) +{ + if (m_invalid) { + return; + } + REALM_ASSERT_DEBUG(m_realm); + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_bool(m_schema.idx_user_is_admin, is_admin); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::mark_for_removal() +{ + if (m_invalid) { + return; + } + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_bool(m_schema.idx_marked_for_removal, true); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::remove() +{ + m_invalid = true; + m_realm->begin_transaction(); + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); + table->move_last_over(m_row.get_index()); + m_realm->commit_transaction(); + m_realm = nullptr; +} + +// MARK: - File action metadata + +util::Optional SyncFileActionMetadata::metadata_for_path(const std::string& original_name, const SyncMetadataManager& manager) +{ + auto realm = Realm::get_shared_realm(manager.get_configuration()); + auto schema = manager.m_file_action_schema; + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); + size_t row_idx = table->find_first_string(schema.idx_original_name, original_name); + if (row_idx == not_found) { + return none; + } + return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx)); +} + +SyncFileActionMetadata::SyncFileActionMetadata(const SyncMetadataManager& manager, + Action action, + const std::string& original_name, + const std::string& url, + const std::string& user_identity, + util::Optional new_name) +: m_schema(manager.m_file_action_schema) +{ + size_t raw_action = static_cast(action); + + // Open the Realm. + m_realm = Realm::get_shared_realm(manager.get_configuration()); + + // Retrieve or create the row for this object. + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); + m_realm->begin_transaction(); + size_t row_idx = table->find_first_string(m_schema.idx_original_name, original_name); + if (row_idx == not_found) { + row_idx = table->add_empty_row(); + table->set_string(m_schema.idx_original_name, row_idx, original_name); + } + table->set_string(m_schema.idx_new_name, row_idx, new_name); + table->set_int(m_schema.idx_action, row_idx, raw_action); + table->set_string(m_schema.idx_url, row_idx, url); + table->set_string(m_schema.idx_user_identity, row_idx, user_identity); + m_realm->commit_transaction(); + m_row = table->get(row_idx); +} + +SyncFileActionMetadata::SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row) +: m_schema(std::move(schema)) +, m_realm(std::move(realm)) +, m_row(row) +{ } + +std::string SyncFileActionMetadata::original_name() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_string(m_schema.idx_original_name); +} + +util::Optional SyncFileActionMetadata::new_name() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + StringData result = m_row.get_string(m_schema.idx_new_name); + return result.is_null() ? util::none : util::make_optional(std::string(result)); +} + +SyncFileActionMetadata::Action SyncFileActionMetadata::action() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return static_cast(m_row.get_int(m_schema.idx_action)); +} + +std::string SyncFileActionMetadata::url() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_string(m_schema.idx_url); +} + +std::string SyncFileActionMetadata::user_identity() const +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_string(m_schema.idx_user_identity); +} + +void SyncFileActionMetadata::remove() +{ + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + m_realm->begin_transaction(); + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); + table->move_last_over(m_row.get_index()); + m_realm->commit_transaction(); + m_realm = nullptr; +} + +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp new file mode 100644 index 0000000..d909a13 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp @@ -0,0 +1,473 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_manager.hpp" + +#include "sync/impl/sync_client.hpp" +#include "sync/impl/sync_file.hpp" +#include "sync/impl/sync_metadata.hpp" +#include "sync/sync_session.hpp" +#include "sync/sync_user.hpp" + +using namespace realm; +using namespace realm::_impl; + +SyncManager& SyncManager::shared() +{ + // The singleton is heap-allocated in order to fix an issue when running unit tests where tests would crash after + // they were done running because the manager was destroyed too early. + static SyncManager& manager = *new SyncManager; + return manager; +} + +void SyncManager::configure_file_system(const std::string& base_file_path, + MetadataMode metadata_mode, + util::Optional> custom_encryption_key, + bool reset_metadata_on_error) +{ + struct UserCreationData { + std::string identity; + std::string user_token; + util::Optional server_url; + bool is_admin; + }; + + std::vector users_to_add; + { + std::lock_guard lock(m_file_system_mutex); + + // Set up the file manager. + if (m_file_manager) { + REALM_ASSERT(m_file_manager->base_path() == base_file_path); + } else { + m_file_manager = std::make_unique(base_file_path); + } + + // Set up the metadata manager, and perform initial loading/purging work. + if (m_metadata_manager) { + return; + } + switch (metadata_mode) { + case MetadataMode::NoEncryption: + m_metadata_manager = std::make_unique(m_file_manager->metadata_path(), + false); + break; + case MetadataMode::Encryption: + try { + m_metadata_manager = std::make_unique(m_file_manager->metadata_path(), + true, + std::move(custom_encryption_key)); + } catch (RealmFileException const& ex) { + if (reset_metadata_on_error && m_file_manager->remove_metadata_realm()) { + m_metadata_manager = std::make_unique(m_file_manager->metadata_path(), + true, + std::move(custom_encryption_key)); + } else { + throw; + } + } + break; + case MetadataMode::NoMetadata: + return; + } + + REALM_ASSERT(m_metadata_manager); + // Perform any necessary file actions. + std::vector completed_actions; + SyncFileActionMetadataResults file_actions = m_metadata_manager->all_pending_actions(); + for (size_t i = 0; i < file_actions.size(); i++) { + auto file_action = file_actions.get(i); + if (run_file_action(file_action)) { + completed_actions.emplace_back(std::move(file_action)); + } + } + for (auto& action : completed_actions) { + action.remove(); + } + // Load persisted users into the users map. + SyncUserMetadataResults users = m_metadata_manager->all_unmarked_users(); + for (size_t i = 0; i < users.size(); i++) { + // Note that 'admin' style users are not persisted. + auto user_data = users.get(i); + auto user_token = user_data.user_token(); + auto identity = user_data.identity(); + auto server_url = user_data.server_url(); + bool is_admin = user_data.is_admin(); + if (user_token) { + UserCreationData data = { + std::move(identity), + std::move(*user_token), + std::move(server_url), + is_admin, + }; + users_to_add.emplace_back(std::move(data)); + } + } + // Delete any users marked for death. + std::vector dead_users; + SyncUserMetadataResults users_to_remove = m_metadata_manager->all_users_marked_for_removal(); + dead_users.reserve(users_to_remove.size()); + for (size_t i = 0; i < users_to_remove.size(); i++) { + auto user = users_to_remove.get(i); + // FIXME: delete user data in a different way? (This deletes a logged-out user's data as soon as the app + // launches again, which might not be how some apps want to treat their data.) + try { + m_file_manager->remove_user_directory(user.identity()); + dead_users.emplace_back(std::move(user)); + } catch (util::File::AccessError const&) { + continue; + } + } + for (auto& user : dead_users) { + user.remove(); + } + } + { + std::lock_guard lock(m_user_mutex); + for (auto& user_data : users_to_add) { + auto user = std::make_shared(user_data.user_token, user_data.identity, user_data.server_url); + user->set_is_admin(user_data.is_admin); + m_users.insert({ user_data.identity, std::move(user) }); + } + } +} + +bool SyncManager::immediately_run_file_actions(const std::string& realm_path) +{ + if (!m_metadata_manager) { + return false; + } + auto metadata = SyncFileActionMetadata::metadata_for_path(realm_path, *m_metadata_manager); + if (!metadata) { + return false; + } + if (run_file_action(*metadata)) { + metadata->remove(); + return true; + } + return false; +} + +// Perform a file action. Returns whether or not the file action can be removed. +bool SyncManager::run_file_action(const SyncFileActionMetadata& md) +{ + switch (md.action()) { + case SyncFileActionMetadata::Action::DeleteRealm: + // Delete all the files for the given Realm. + m_file_manager->remove_realm(md.original_name()); + return true; + case SyncFileActionMetadata::Action::HandleRealmForClientReset: + // Copy the primary Realm file to the recovery dir, and then delete the Realm. + auto new_name = md.new_name(); + auto original_name = md.original_name(); + if (!util::File::exists(original_name)) { + // The Realm file doesn't exist anymore. + return true; + } + if (new_name && !util::File::exists(*new_name) && m_file_manager->copy_realm_file(original_name, *new_name)) { + // We successfully copied the Realm file to the recovery directory. + m_file_manager->remove_realm(original_name); + return true; + } + return false; + } + return false; +} + +void SyncManager::reset_for_testing() +{ + std::lock_guard lock(m_file_system_mutex); + m_file_manager = nullptr; + m_metadata_manager = nullptr; + { + // Destroy all the users. + std::lock_guard lock(m_user_mutex); + m_users.clear(); + } + { + std::lock_guard lock(m_mutex); + + // Stop the client. This will abort any uploads that inactive sessions are waiting for. + if (m_sync_client) + m_sync_client->stop(); + + { + std::lock_guard lock(m_session_mutex); + +#if REALM_ASSERTIONS_ENABLED + // Callers of `SyncManager::reset_for_testing` should ensure there are no active sessions + // prior to calling `reset_for_testing`. + auto no_active_sessions = std::none_of(m_sessions.begin(), m_sessions.end(), [](auto& element){ + return element.second->existing_external_reference(); + }); + REALM_ASSERT(no_active_sessions); +#endif + + // Destroy any inactive sessions. + // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to + // remain inactive until their final upload completes, at which point they are unregistered + // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions + // should have already been destroyed. + m_sessions.clear(); + } + + // Destroy the client now that we have no remaining sessions. + m_sync_client = nullptr; + + // Reset even more state. + // NOTE: these should always match the defaults. + m_log_level = util::Logger::Level::info; + m_logger_factory = nullptr; + m_client_reconnect_mode = ReconnectMode::normal; + m_client_validate_ssl = true; + } +} + +void SyncManager::set_log_level(util::Logger::Level level) noexcept +{ + std::lock_guard lock(m_mutex); + m_log_level = level; +} + +void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept +{ + std::lock_guard lock(m_mutex); + m_logger_factory = &factory; +} + +void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately) +{ + std::lock_guard lock(m_mutex); + m_client_reconnect_mode = reconnect_immediately ? ReconnectMode::immediate : ReconnectMode::normal; +} + +bool SyncManager::client_should_reconnect_immediately() const noexcept +{ + std::lock_guard lock(m_mutex); + return m_client_reconnect_mode == ReconnectMode::immediate; +} + +void SyncManager::set_client_should_validate_ssl(bool validate_ssl) +{ + std::lock_guard lock(m_mutex); + m_client_validate_ssl = validate_ssl; +} + +bool SyncManager::client_should_validate_ssl() const noexcept +{ + std::lock_guard lock(m_mutex); + return m_client_validate_ssl; +} + +void SyncManager::reconnect() +{ + std::lock_guard lock(m_mutex); + if (m_sync_client) { + m_sync_client->cancel_reconnect_delay(); + } +} + +util::Logger::Level SyncManager::log_level() const noexcept +{ + std::lock_guard lock(m_mutex); + return m_log_level; +} + +bool SyncManager::perform_metadata_update(std::function update_function) const +{ + std::lock_guard lock(m_file_system_mutex); + if (!m_metadata_manager) { + return false; + } + update_function(*m_metadata_manager); + return true; +} + +std::shared_ptr SyncManager::get_user(const std::string& identity, + std::string refresh_token, + util::Optional auth_server_url, + SyncUser::TokenType token_type) +{ + std::lock_guard lock(m_user_mutex); + auto it = m_users.find(identity); + if (it == m_users.end()) { + // No existing user. + auto new_user = std::make_shared(std::move(refresh_token), identity, auth_server_url, token_type); + m_users.insert({ identity, new_user }); + return new_user; + } else { + auto user = it->second; + if (auth_server_url && *auth_server_url != user->server_url()) { + throw std::invalid_argument("Cannot retrieve an existing user specifying a different auth server."); + } + if (user->token_type() != token_type) { + throw std::invalid_argument("Cannot retrieve a user specifying a different token type."); + } + if (user->state() == SyncUser::State::Error) { + return nullptr; + } + user->update_refresh_token(std::move(refresh_token)); + return user; + } +} + +std::shared_ptr SyncManager::get_existing_logged_in_user(const std::string& identity) const +{ + std::lock_guard lock(m_user_mutex); + auto it = m_users.find(identity); + if (it == m_users.end()) { + return nullptr; + } + auto ptr = it->second; + return (ptr->state() == SyncUser::State::Active ? ptr : nullptr); +} + +std::vector> SyncManager::all_logged_in_users() const +{ + std::lock_guard lock(m_user_mutex); + std::vector> users; + users.reserve(m_users.size()); + for (auto& it : m_users) { + auto user = it.second; + if (user->state() == SyncUser::State::Active) { + users.emplace_back(std::move(user)); + } + } + return users; +} + +std::shared_ptr SyncManager::get_current_user() const +{ + std::lock_guard lock(m_user_mutex); + + auto is_active_user = [](auto& el) { return el.second->state() == SyncUser::State::Active; }; + auto it = std::find_if(m_users.begin(), m_users.end(), is_active_user); + if (it == m_users.end()) { + return nullptr; + } + if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end()) { + throw std::logic_error("Current user is not valid if more that one valid, logged-in user exists."); + } + return it->second; +} + +std::string SyncManager::path_for_realm(const std::string& user_identity, const std::string& raw_realm_url) const +{ + std::lock_guard lock(m_file_system_mutex); + REALM_ASSERT(m_file_manager); + return m_file_manager->path(user_identity, raw_realm_url); +} + +std::string SyncManager::recovery_directory_path() const +{ + std::lock_guard lock(m_file_system_mutex); + REALM_ASSERT(m_file_manager); + return m_file_manager->recovery_directory_path(); +} + +std::shared_ptr SyncManager::get_existing_active_session(const std::string& path) const +{ + std::lock_guard lock(m_session_mutex); + if (auto session = get_existing_session_locked(path)) { + if (auto external_reference = session->existing_external_reference()) + return external_reference; + } + return nullptr; +} + +std::shared_ptr SyncManager::get_existing_session_locked(const std::string& path) const +{ + REALM_ASSERT(!m_session_mutex.try_lock()); + auto it = m_sessions.find(path); + if (it == m_sessions.end()) { + return nullptr; + } + return it->second; +} + +std::shared_ptr SyncManager::get_existing_session(const std::string& path) const +{ + std::lock_guard lock(m_session_mutex); + if (auto session = get_existing_session_locked(path)) { + return session->external_reference(); + } + return nullptr; +} + +std::shared_ptr SyncManager::get_session(const std::string& path, const SyncConfig& sync_config) +{ + auto& client = get_sync_client(); // Throws + + std::lock_guard lock(m_session_mutex); + if (auto session = get_existing_session_locked(path)) { + sync_config.user->register_session(session); + return session->external_reference(); + } + + std::shared_ptr shared_session(new SyncSession(client, path, sync_config)); + m_sessions[path] = shared_session; + + // Create the external reference immediately to ensure that the session will become + // inactive if an exception is thrown in the following code. + auto external_reference = shared_session->external_reference(); + + sync_config.user->register_session(shared_session); + + return external_reference; +} + +void SyncManager::unregister_session(const std::string& path) +{ + std::lock_guard lock(m_session_mutex); + auto it = m_sessions.find(path); + REALM_ASSERT(it != m_sessions.end()); + + // If the session has an active external reference, leave it be. This will happen if the session + // moves to an inactive state while still externally reference, for instance, as a result of + // the session's user being logged out. + if (it->second->existing_external_reference()) + return; + + m_sessions.erase(path); +} + +SyncClient& SyncManager::get_sync_client() const +{ + std::lock_guard lock(m_mutex); + if (!m_sync_client) + m_sync_client = create_sync_client(); // Throws + return *m_sync_client; +} + +std::unique_ptr SyncManager::create_sync_client() const +{ + REALM_ASSERT(!m_mutex.try_lock()); + + std::unique_ptr logger; + if (m_logger_factory) { + logger = m_logger_factory->make_logger(m_log_level); // Throws + } + else { + auto stderr_logger = std::make_unique(); // Throws + stderr_logger->set_level_threshold(m_log_level); + logger = std::move(stderr_logger); + } + return std::make_unique(std::move(logger), + m_client_reconnect_mode, + m_client_validate_ssl); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp new file mode 100644 index 0000000..0042656 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp @@ -0,0 +1,245 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_permission.hpp" + +#include "impl/object_accessor_impl.hpp" +#include "object_schema.hpp" +#include "property.hpp" + +#include "sync/sync_config.hpp" +#include "sync/sync_manager.hpp" +#include "sync/sync_session.hpp" +#include "sync/sync_user.hpp" +#include "util/event_loop_signal.hpp" +#include "util/uuid.hpp" + +#include +#include + +using namespace realm; + +using PrimaryKey = Property::PrimaryKey; +using Indexed = Property::Indexed; +using Nullable = Property::Nullable; + +// MARK: - Utility + +namespace { + +Permission::AccessLevel extract_access_level(Object& permission, CppContext& context) +{ + auto may_manage = permission.get_property_value(&context, "mayManage"); + if (may_manage.has_value() && any_cast(may_manage)) + return Permission::AccessLevel::Admin; + + auto may_write = permission.get_property_value(&context, "mayWrite"); + if (may_write.has_value() && any_cast(may_write)) + return Permission::AccessLevel::Write; + + auto may_read = permission.get_property_value(&context, "mayRead"); + if (may_read.has_value() && any_cast(may_read)) + return Permission::AccessLevel::Read; + + return Permission::AccessLevel::None; +} + +} + +// MARK: - PermissionResults + +Permission PermissionResults::get(size_t index) +{ + Object permission(m_results.get_realm(), m_results.get_object_schema(), m_results.get(index)); + CppContext context; + return { + any_cast(permission.get_property_value(&context, "path")), + extract_access_level(permission, context), + { any_cast(permission.get_property_value(&context, "userId")) } + }; +} + +// MARK: - Permissions + +// A wrapper that stores a value and an associated notification token. +// The point of this type is to keep the notification token alive +// until the value can be properly processed or handled. +template +struct NotificationWrapper : public T { + using T::T; + + NotificationWrapper(T&& object) + : T(object) + { } + + template + void add_notification_callback(F&& callback) + { + m_token = T::add_notification_callback(std::forward(callback)); + } + +private: + NotificationToken m_token; +}; + +void Permissions::get_permissions(std::shared_ptr user, + PermissionResultsCallback callback, + const ConfigMaker& make_config) +{ + auto realm = Permissions::permission_realm(user, make_config); + auto table = ObjectStore::table_for_object_type(realm->read_group(), "Permission"); + auto results = std::make_shared>(std::move(realm), *table); + + // `get_permissions` works by temporarily adding an async notifier to the permission Realm. + // This notifier will run the `async` callback until the Realm contains permissions or + // an error happens. When either of these two things happen, the notifier will be + // unregistered by nulling out the `results_wrapper` container. + auto async = [results, callback=std::move(callback)](CollectionChangeSet, std::exception_ptr ex) mutable { + if (ex) { + callback(nullptr, ex); + results.reset(); + return; + } + if (results->size() > 0) { + // We monitor the raw results. The presence of a `__management` Realm indicates + // that the permissions have been downloaded (hence, we wait until size > 0). + TableRef table = ObjectStore::table_for_object_type(results->get_realm()->read_group(), "Permission"); + size_t col_idx = table->get_descriptor()->get_column_index("path"); + auto query = !(table->column(col_idx).ends_with("/__permission") + || table->column(col_idx).ends_with("/__management")); + // Call the callback with our new permissions object. This object will exclude the + // private Realms. + callback(std::make_unique(results->filter(std::move(query))), nullptr); + results.reset(); + } + }; + results->add_notification_callback(std::move(async)); +} + +void Permissions::set_permission(std::shared_ptr user, + Permission permission, + PermissionChangeCallback callback, + const ConfigMaker& make_config) +{ + const auto realm_url = user->server_url() + permission.path; + auto realm = Permissions::management_realm(std::move(user), make_config); + CppContext context; + + // Write the permission object. + realm->begin_transaction(); + auto raw = Object::create(&context, realm, *realm->schema().find("PermissionChange"), AnyDict{ + { "id", util::uuid_string() }, + { "createdAt", Timestamp(0, 0) }, + { "updatedAt", Timestamp(0, 0) }, + { "userId", permission.condition.user_id }, + { "realmUrl", realm_url }, + { "mayRead", permission.access != Permission::AccessLevel::None }, + { "mayWrite", permission.access == Permission::AccessLevel::Write || permission.access == Permission::AccessLevel::Admin }, + { "mayManage", permission.access == Permission::AccessLevel::Admin }, + }, false); + auto object = std::make_shared>(std::move(raw)); + realm->commit_transaction(); + + // Observe the permission object until the permission change has been processed or failed. + // The notifier is automatically unregistered upon the completion of the permission + // change, one way or another. + auto block = [object, callback=std::move(callback)](CollectionChangeSet, std::exception_ptr ex) mutable { + if (ex) { + callback(ex); + object.reset(); + return; + } + CppContext context; + auto status_code = object->get_property_value(&context, "statusCode"); + if (!status_code.has_value()) { + // Continue waiting for the sync server to complete the operation. + return; + } + // Determine whether an error happened or not. + auto code = any_cast(status_code); + std::exception_ptr exc_ptr = nullptr; + if (code) { + // The permission change failed because an error was returned from the server. + auto status = object->get_property_value(&context, "statusMessage"); + std::string error_str = (status.has_value() + ? any_cast(status) + : util::format("Error code: %1", code)); + exc_ptr = std::make_exception_ptr(PermissionChangeException(error_str, code)); + } + callback(exc_ptr); + object.reset(); + }; + object->add_notification_callback(std::move(block)); +} + +void Permissions::delete_permission(std::shared_ptr user, + Permission permission, + PermissionChangeCallback callback, + const ConfigMaker& make_config) +{ + permission.access = Permission::AccessLevel::None; + set_permission(std::move(user), std::move(permission), std::move(callback), make_config); +} + +SharedRealm Permissions::management_realm(std::shared_ptr user, const ConfigMaker& make_config) +{ + // FIXME: maybe we should cache the management Realm on the user, so we don't need to open it every time. + const auto realm_url = util::format("realm%1/~/__management", user->server_url().substr(4)); + Realm::Config config = make_config(user, std::move(realm_url)); + config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately; + config.schema = Schema{ + { "PermissionChange", { + Property::make("id", PropertyType::String, PrimaryKey::yes, Indexed::yes, Nullable::no), + Property::make("createdAt", PropertyType::Date, PrimaryKey::no, Indexed::no, Nullable::no), + Property::make("updatedAt", PropertyType::Date, PrimaryKey::no, Indexed::no, Nullable::no), + Property::make("statusCode", PropertyType::Int, PrimaryKey::no, Indexed::no, Nullable::yes), + Property::make("statusMessage", PropertyType::String, PrimaryKey::no, Indexed::no, Nullable::yes), + Property::make("userId", PropertyType::String, PrimaryKey::no, Indexed::no, Nullable::no), + Property::make("realmUrl", PropertyType::String, PrimaryKey::no, Indexed::no, Nullable::no), + Property::make("mayRead", PropertyType::Bool, PrimaryKey::no, Indexed::no, Nullable::yes), + Property::make("mayWrite", PropertyType::Bool, PrimaryKey::no, Indexed::no, Nullable::yes), + Property::make("mayManage", PropertyType::Bool, PrimaryKey::no, Indexed::no, Nullable::yes), + }} + }; + config.schema_version = 0; + auto shared_realm = Realm::get_shared_realm(std::move(config)); + user->register_management_session(config.path); + return shared_realm; +} + +SharedRealm Permissions::permission_realm(std::shared_ptr user, const ConfigMaker& make_config) +{ + // FIXME: maybe we should cache the permission Realm on the user, so we don't need to open it every time. + const auto realm_url = util::format("realm%1/~/__permission", user->server_url().substr(4)); + Realm::Config config = make_config(user, std::move(realm_url)); + config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately; + config.schema = Schema{ + { "Permission", { + Property::make("updatedAt", PropertyType::Date), + Property::make("userId", PropertyType::String), + Property::make("path", PropertyType::String), + Property::make("mayRead", PropertyType::Bool), + Property::make("mayWrite", PropertyType::Bool), + Property::make("mayManage", PropertyType::Bool), + }} + }; + config.schema_version = 0; + auto shared_realm = Realm::get_shared_realm(std::move(config)); + user->register_permission_session(config.path); + return shared_realm; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp new file mode 100644 index 0000000..b78c5e5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp @@ -0,0 +1,812 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_session.hpp" + +#include "sync/impl/sync_client.hpp" +#include "sync/impl/sync_file.hpp" +#include "sync/impl/sync_metadata.hpp" +#include "sync/sync_manager.hpp" +#include "sync/sync_user.hpp" + +#include +#include + + +using namespace realm; +using namespace realm::_impl; +using namespace realm::_impl::sync_session_states; + +using SessionWaiterPointer = void(sync::Session::*)(std::function); + +constexpr const char SyncError::c_original_file_path_key[]; +constexpr const char SyncError::c_recovery_file_path_key[]; + +/// A state which a `SyncSession` can currently be within. State classes handle various actions +/// and state transitions. +/// +/// STATES: +/// +/// WAITING_FOR_ACCESS_TOKEN: upon entering this state, the binding is informed +/// that the session wants an access token. The session is now waiting for the +/// binding to provide the token. +/// From: INACTIVE +/// To: +/// * ACTIVE: when the binding successfully refreshes the token +/// * INACTIVE: if asked to log out, or if asked to close and the stop policy +/// is Immediate. +/// * ERROR: if a fatal error occurs +/// +/// ACTIVE: the session is connected to the Realm Object Server and is actively +/// transferring data. +/// From: WAITING_FOR_ACCESS_TOKEN, DYING +/// To: +/// * WAITING_FOR_ACCESS_TOKEN: if the session is informed (through the error +/// handler) that the token expired +/// * INACTIVE: if asked to log out, or if asked to close and the stop policy +/// is Immediate. +/// * DYING: if asked to close and the stop policy is AfterChangesUploaded +/// * ERROR: if a fatal error occurs +/// +/// DYING: the session is performing clean-up work in preparation to be destroyed. +/// From: ACTIVE +/// To: +/// * INACTIVE: when the clean-up work completes, if the session wasn't +/// revived, or if explicitly asked to log out before the +/// clean-up work begins +/// * ACTIVE: if the session is revived +/// * ERROR: if a fatal error occurs +/// +/// INACTIVE: the user owning this session has logged out, the `sync::Session` +/// owned by this session is destroyed, and the session is quiescent. +/// Note that a session briefly enters this state before being destroyed, but +/// it can also enter this state and stay there if the user has been logged out. +/// From: initial, WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING +/// To: +/// * WAITING_FOR_ACCESS_TOKEN: if the session is revived +/// * ERROR: if a fatal error occurs +/// +/// ERROR: a non-recoverable error has occurred, and this session is semantically +/// invalid. The binding must create a new session with a different configuration. +/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING, INACTIVE +/// To: +/// * (none, this is a terminal state) +/// +struct SyncSession::State { + virtual ~State() { } + + // Move the given session into this state. All state transitions MUST be carried out through this method. + virtual void enter_state(std::unique_lock&, SyncSession&) const { } + + virtual void refresh_access_token(std::unique_lock&, + SyncSession&, std::string, + const util::Optional&) const { } + + virtual void bind_with_admin_token(std::unique_lock&, + SyncSession&, const std::string&, const std::string&) const { } + + // Returns true iff the lock is still locked when the method returns. + virtual bool access_token_expired(std::unique_lock&, SyncSession&) const { return true; } + + virtual void nonsync_transact_notify(std::unique_lock&, SyncSession&, sync::Session::version_type) const { } + + // Perform any work needed to reactivate a session that is not already active. + // Returns true iff the session should ask the binding to get a token for `bind()`. + virtual bool revive_if_needed(std::unique_lock&, SyncSession&) const { return false; } + + // The user that owns this session has been logged out, and the session should take appropriate action. + virtual void log_out(std::unique_lock&, SyncSession&) const { } + + // The session should be closed and moved to `inactive`, in accordance with its stop policy and other state. + virtual void close(std::unique_lock&, SyncSession&) const { } + + // Returns true iff the error has been fully handled and the error handler should immediately return. + virtual bool handle_error(std::unique_lock&, SyncSession&, const SyncError&) const { return false; } + + // Register a handler to wait for sync session uploads, downloads, or synchronization. + // PRECONDITION: the session state lock must be held at the time this method is called, until after it returns. + // Returns true iff the handler was registered, either immediately or placed in a queue for later registration. + virtual bool wait_for_completion(SyncSession&, + std::function, + SessionWaiterPointer) const { + return false; + } + + static const State& waiting_for_access_token; + static const State& active; + static const State& dying; + static const State& inactive; + static const State& error; +}; + +struct sync_session_states::WaitingForAccessToken : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + session.m_deferred_close = false; + } + + void refresh_access_token(std::unique_lock& lock, SyncSession& session, + std::string access_token, + const util::Optional& server_url) const override + { + REALM_ASSERT(session.m_session); + // Since the sync session was previously unbound, it's safe to do this from the + // calling thread. + if (!session.m_server_url) { + session.m_server_url = server_url; + } + if (session.m_session_has_been_bound) { + session.m_session->refresh(std::move(access_token)); + } else { + session.m_session->bind(*session.m_server_url, std::move(access_token)); + session.m_session_has_been_bound = true; + } + + // Register all the pending wait-for-completion blocks. + for (auto& package : session.m_completion_wait_packages) { + (*session.m_session.*package.waiter)(std::move(package.callback)); + } + session.m_completion_wait_packages.clear(); + + // Handle any deferred commit notification. + if (session.m_deferred_commit_notification) { + session.m_session->nonsync_transact_notify(*session.m_deferred_commit_notification); + session.m_deferred_commit_notification = util::none; + } + + session.advance_state(lock, active); + if (session.m_deferred_close) { + session.m_state->close(lock, session); + } + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } + + bool revive_if_needed(std::unique_lock&, SyncSession& session) const override + { + session.m_deferred_close = false; + return false; + } + + void nonsync_transact_notify(std::unique_lock&, + SyncSession& session, + sync::Session::version_type version) const override + { + // Notify at first available opportunity. + session.m_deferred_commit_notification = version; + } + + void close(std::unique_lock& lock, SyncSession& session) const override + { + switch (session.m_config.stop_policy) { + case SyncSessionStopPolicy::Immediately: + // Immediately kill the session. + session.advance_state(lock, inactive); + break; + case SyncSessionStopPolicy::LiveIndefinitely: + case SyncSessionStopPolicy::AfterChangesUploaded: + // Defer handling closing the session until after the login response succeeds. + session.m_deferred_close = true; + break; + } + } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + session.m_completion_wait_packages.push_back({ waiter, std::move(callback) }); + return true; + } +}; + +struct sync_session_states::Active : public SyncSession::State { + void refresh_access_token(std::unique_lock&, SyncSession& session, + std::string access_token, + const util::Optional&) const override + { + session.m_session->refresh(std::move(access_token)); + } + + bool access_token_expired(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, waiting_for_access_token); + std::shared_ptr session_ptr = session.shared_from_this(); + lock.unlock(); + session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr); + return false; + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } + + void nonsync_transact_notify(std::unique_lock&, SyncSession& session, + sync::Session::version_type version) const override + { + // Fully ready sync session, notify immediately. + session.m_session->nonsync_transact_notify(version); + } + + void close(std::unique_lock& lock, SyncSession& session) const override + { + switch (session.m_config.stop_policy) { + case SyncSessionStopPolicy::Immediately: + session.advance_state(lock, inactive); + break; + case SyncSessionStopPolicy::LiveIndefinitely: + // Don't do anything; session lives forever. + break; + case SyncSessionStopPolicy::AfterChangesUploaded: + // Wait for all pending changes to upload. + session.advance_state(lock, dying); + break; + } + } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + REALM_ASSERT(session.m_session); + (*session.m_session.*waiter)(std::move(callback)); + return true; + } +}; + +struct sync_session_states::Dying : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + size_t current_death_count = ++session.m_death_count; + std::weak_ptr weak_session = session.shared_from_this(); + session.m_session->async_wait_for_upload_completion([weak_session, current_death_count](std::error_code) { + if (auto session = weak_session.lock()) { + std::unique_lock lock(session->m_state_mutex); + if (session->m_state == &State::dying && session->m_death_count == current_death_count) { + session->advance_state(lock, inactive); + } + } + }); + } + + bool handle_error(std::unique_lock& lock, SyncSession& session, const SyncError& error) const override + { + if (error.is_fatal) { + session.advance_state(lock, inactive); + } + // If the error isn't fatal, don't change state, but don't + // allow it to be reported either. + return true; + } + + bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override + { + // Revive. + session.advance_state(lock, active); + return false; + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + REALM_ASSERT(session.m_session); + (*session.m_session.*waiter)(std::move(callback)); + return true; + } +}; + +struct sync_session_states::Inactive : public SyncSession::State { + void enter_state(std::unique_lock& lock, SyncSession& session) const override + { + // Inform any queued-up completion handlers that they were cancelled. + for (auto& package : session.m_completion_wait_packages) { + package.callback(util::error::operation_aborted); + } + session.m_completion_wait_packages.clear(); + session.m_session = nullptr; + session.m_server_url = util::none; + session.unregister(lock); + } + + void bind_with_admin_token(std::unique_lock& lock, SyncSession& session, + const std::string& admin_token, + const std::string& server_url) const override + { + session.create_sync_session(); + session.advance_state(lock, waiting_for_access_token); + session.m_state->refresh_access_token(lock, session, admin_token, server_url); + } + + bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override + { + // Revive. + session.create_sync_session(); + session.advance_state(lock, waiting_for_access_token); + return true; + } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + session.m_completion_wait_packages.push_back({ waiter, std::move(callback) }); + return true; + } +}; + +struct sync_session_states::Error : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + // Inform any queued-up completion handlers that they were cancelled. + for (auto& package : session.m_completion_wait_packages) { + package.callback(util::error::operation_aborted); + } + session.m_completion_wait_packages.clear(); + session.m_session = nullptr; + session.m_config = { nullptr, "", SyncSessionStopPolicy::Immediately, nullptr }; + } + + // Everything else is a no-op when in the error state. +}; + + +const SyncSession::State& SyncSession::State::waiting_for_access_token = WaitingForAccessToken(); +const SyncSession::State& SyncSession::State::active = Active(); +const SyncSession::State& SyncSession::State::dying = Dying(); +const SyncSession::State& SyncSession::State::inactive = Inactive(); +const SyncSession::State& SyncSession::State::error = Error(); + +SyncSession::SyncSession(SyncClient& client, std::string realm_path, SyncConfig config) +: m_state(&State::inactive) +, m_config(std::move(config)) +, m_realm_path(std::move(realm_path)) +, m_client(client) { } + +std::string SyncSession::get_recovery_file_path() +{ + return util::reserve_unique_file_name(SyncManager::shared().recovery_directory_path(), + util::create_timestamped_template("recovered_realm")); +} + +// This method should only be called from within the error handler callback registered upon the underlying `m_session`. +void SyncSession::handle_error(SyncError error) +{ + bool should_invalidate_session = error.is_fatal; + auto error_code = error.error_code; + + { + // See if the current state wishes to take responsibility for handling the error. + std::unique_lock lock(m_state_mutex); + if (m_state->handle_error(lock, *this, error)) { + return; + } + } + + if (error_code.category() == realm::sync::protocol_error_category()) { + using ProtocolError = realm::sync::ProtocolError; + switch (static_cast(error_code.value())) { + // Connection level errors + case ProtocolError::connection_closed: + case ProtocolError::other_error: + case ProtocolError::pong_timeout: + // Not real errors, don't need to be reported to the binding. + return; + case ProtocolError::unknown_message: + case ProtocolError::bad_syntax: + case ProtocolError::limits_exceeded: + case ProtocolError::wrong_protocol_version: + case ProtocolError::bad_session_ident: + case ProtocolError::reuse_of_session_ident: + case ProtocolError::bound_in_other_session: + case ProtocolError::bad_message_order: + break; + // Session errors + case ProtocolError::session_closed: + case ProtocolError::other_session_error: + case ProtocolError::disabled_session: + // The binding doesn't need to be aware of these because they are strictly informational, and do not + // represent actual errors. + return; + case ProtocolError::token_expired: { + std::unique_lock lock(m_state_mutex); + // This isn't an error from the binding's point of view. If we're connected we'll + // simply ask the binding to log in again. + m_state->access_token_expired(lock, *this); + return; + } + case ProtocolError::bad_authentication: { + std::shared_ptr user_to_invalidate; + should_invalidate_session = false; + { + std::unique_lock lock(m_state_mutex); + user_to_invalidate = user(); + advance_state(lock, State::error); + } + if (user_to_invalidate) + user_to_invalidate->invalidate(); + break; + } + case ProtocolError::illegal_realm_path: + case ProtocolError::no_such_realm: + case ProtocolError::permission_denied: + case ProtocolError::bad_client_version: + break; + case ProtocolError::bad_server_file_ident: + case ProtocolError::bad_client_file_ident: + case ProtocolError::bad_server_version: + case ProtocolError::diverging_histories: { + // Add a SyncFileActionMetadata marking the Realm as needing to be deleted. + auto recovery_path = get_recovery_file_path(); + auto original_path = path(); + error.user_info[SyncError::c_original_file_path_key] = original_path; + error.user_info[SyncError::c_recovery_file_path_key] = recovery_path; + SyncManager::shared().perform_metadata_update([this, + original_path=std::move(original_path), + recovery_path=std::move(recovery_path)](const auto& manager) { + SyncFileActionMetadata(manager, + SyncFileActionMetadata::Action::HandleRealmForClientReset, + original_path, + m_config.realm_url, + m_config.user->identity(), + util::Optional(std::move(recovery_path))); + }); + break; + } + case ProtocolError::bad_changeset: + break; + } + } else if (error_code.category() == realm::sync::client_error_category()) { + using ClientError = realm::sync::Client::Error; + switch (static_cast(error_code.value())) { + case ClientError::connection_closed: + // Not real errors, don't need to be reported to the binding. + return; + case ClientError::unknown_message: + case ClientError::bad_syntax: + case ClientError::limits_exceeded: + case ClientError::bad_session_ident: + case ClientError::bad_message_order: + case ClientError::bad_file_ident_pair: + case ClientError::bad_progress: + case ClientError::bad_changeset_header_syntax: + case ClientError::bad_changeset_size: + case ClientError::bad_origin_file_ident: + case ClientError::bad_server_version: + case ClientError::bad_changeset: + case ClientError::bad_request_ident: + case ClientError::bad_error_code: + case ClientError::bad_compression: + case ClientError::bad_client_version: + // Don't do anything special for these errors. + // Future functionality may require special-case handling for existing + // errors, or newly introduced error codes. + break; + } + } else { + // Unrecognized error code; just ignore it. + return; + } + if (should_invalidate_session) { + std::unique_lock lock(m_state_mutex); + advance_state(lock, State::error); + } + if (m_error_handler) { + m_error_handler(shared_from_this(), std::move(error)); + } +} + +void SyncSession::handle_progress_update(uint64_t downloaded, uint64_t downloadable, + uint64_t uploaded, uint64_t uploadable, bool is_fresh) +{ + std::vector> invocations; + { + std::lock_guard lock(m_progress_notifier_mutex); + m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded}; + m_latest_progress_data_is_fresh = is_fresh; + + for (auto it = m_notifiers.begin(); it != m_notifiers.end();) { + auto& package = it->second; + package.update(*m_current_progress, is_fresh); + + bool should_delete = false; + invocations.emplace_back(package.create_invocation(*m_current_progress, should_delete)); + + it = (should_delete ? m_notifiers.erase(it) : std::next(it)); + } + } + // Run the notifiers only after we've released the lock. + for (auto& invocation : invocations) { + invocation(); + } +} + +void SyncSession::NotifierPackage::update(const Progress& current_progress, bool data_is_fresh) +{ + if (is_streaming || captured_transferrable || !data_is_fresh) + return; + + captured_transferrable = direction == NotifierType::download ? current_progress.downloadable + : current_progress.uploadable; +} + +// PRECONDITION: `update()` must first be called on the same package. +std::function SyncSession::NotifierPackage::create_invocation(const Progress& current_progress, + bool& is_expired) const +{ + // It's possible for a non-streaming notifier to not yet have fresh transferrable bytes data. + // In that case, we don't call it at all. + // NOTE: `update()` is always called before `create_invocation()`, and will + // set `captured_transferrable` on the notifier package if fresh data has + // been received and the package is for a non-streaming notifier. + if (!is_streaming && !captured_transferrable) + return [](){ }; + + bool is_download = direction == NotifierType::download; + uint64_t transferred = is_download ? current_progress.downloaded : current_progress.uploaded; + uint64_t transferrable; + if (is_streaming) { + transferrable = is_download ? current_progress.downloadable : current_progress.uploadable; + } else { + transferrable = *captured_transferrable; + } + // A notifier is expired if at least as many bytes have been transferred + // as were originally considered transferrable. + is_expired = !is_streaming && transferred >= *captured_transferrable; + return [=, package=*this](){ + package.notifier(transferred, transferrable); + }; +} + +void SyncSession::create_sync_session() +{ + REALM_ASSERT(!m_session); + sync::Session::Config session_config; + session_config.changeset_cooker = m_config.transformer; + session_config.encryption_key = m_config.realm_encryption_key; + m_session = std::make_unique(m_client.client, m_realm_path, session_config); + + // The next time we get a token, call `bind()` instead of `refresh()`. + m_session_has_been_bound = false; + + // Configure the error handler. + std::weak_ptr weak_self = shared_from_this(); + auto wrapped_handler = [this, weak_self](std::error_code error_code, bool is_fatal, std::string message) { + auto self = weak_self.lock(); + if (!self) { + // An error was delivered after the session it relates to was destroyed. There's nothing useful + // we can do with it. + return; + } + handle_error(SyncError{error_code, std::move(message), is_fatal}); + }; + m_session->set_error_handler(std::move(wrapped_handler)); + + // Configure the sync transaction callback. + auto wrapped_callback = [this, weak_self](VersionID old_version, VersionID new_version) { + if (auto self = weak_self.lock()) { + if (m_sync_transact_callback) { + m_sync_transact_callback(old_version, new_version); + } + } + }; + m_session->set_sync_transact_callback(std::move(wrapped_callback)); + + // Set up the wrapped progress handler callback + auto wrapped_progress_handler = [this, weak_self](uint_fast64_t downloaded, uint_fast64_t downloadable, + uint_fast64_t uploaded, uint_fast64_t uploadable, + bool is_fresh) { + if (auto self = weak_self.lock()) { + handle_progress_update(downloaded, downloadable, uploaded, uploadable, is_fresh); + } + }; + m_session->set_progress_handler(std::move(wrapped_progress_handler)); +} + +void SyncSession::set_sync_transact_callback(std::function callback) +{ + m_sync_transact_callback = std::move(callback); +} + +void SyncSession::set_error_handler(std::function handler) +{ + m_error_handler = std::move(handler); +} + +void SyncSession::advance_state(std::unique_lock& lock, const State& state) +{ + REALM_ASSERT(lock.owns_lock()); + REALM_ASSERT(&state != m_state); + m_state = &state; + m_state->enter_state(lock, *this); +} + +void SyncSession::nonsync_transact_notify(sync::Session::version_type version) +{ + std::unique_lock lock(m_state_mutex); + m_state->nonsync_transact_notify(lock, *this, version); +} + +void SyncSession::revive_if_needed() +{ + util::Optional&> handler; + { + std::unique_lock lock(m_state_mutex); + if (m_state->revive_if_needed(lock, *this)) + handler = m_config.bind_session_handler; + } + if (handler) + handler.value()(m_realm_path, m_config, shared_from_this()); +} + +void SyncSession::log_out() +{ + std::unique_lock lock(m_state_mutex); + m_state->log_out(lock, *this); +} + +void SyncSession::close() +{ + std::unique_lock lock(m_state_mutex); + m_state->close(lock, *this); +} + +void SyncSession::unregister(std::unique_lock& lock) +{ + REALM_ASSERT(lock.owns_lock()); + REALM_ASSERT(m_state == &State::inactive); // Must stop an active session before unregistering. + + lock.unlock(); + SyncManager::shared().unregister_session(m_realm_path); +} + +bool SyncSession::wait_for_upload_completion(std::function callback) +{ + std::unique_lock lock(m_state_mutex); + return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_upload_completion); +} + +bool SyncSession::wait_for_download_completion(std::function callback) +{ + std::unique_lock lock(m_state_mutex); + return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_download_completion); +} + +uint64_t SyncSession::register_progress_notifier(std::function notifier, + NotifierType direction, bool is_streaming) +{ + std::function invocation; + uint64_t token_value = 0; + { + std::lock_guard lock(m_progress_notifier_mutex); + token_value = m_progress_notifier_token++; + NotifierPackage package{std::move(notifier), is_streaming, direction}; + if (!m_current_progress) { + // Simply register the package, since we have no data yet. + m_notifiers.emplace(token_value, std::move(package)); + return token_value; + } + package.update(*m_current_progress, m_latest_progress_data_is_fresh); + bool skip_registration = false; + invocation = package.create_invocation(*m_current_progress, skip_registration); + if (skip_registration) { + token_value = 0; + } else { + m_notifiers.emplace(token_value, std::move(package)); + } + } + invocation(); + return token_value; +} + +void SyncSession::unregister_progress_notifier(uint64_t token) +{ + std::lock_guard lock(m_progress_notifier_mutex); + m_notifiers.erase(token); +} + +void SyncSession::refresh_access_token(std::string access_token, util::Optional server_url) +{ + std::unique_lock lock(m_state_mutex); + if (!m_server_url && !server_url) { + // The first time this method is called, the server URL must be provided. + return; + } + m_state->refresh_access_token(lock, *this, std::move(access_token), server_url); +} + +void SyncSession::bind_with_admin_token(std::string admin_token, std::string server_url) +{ + std::unique_lock lock(m_state_mutex); + m_state->bind_with_admin_token(lock, *this, admin_token, server_url); +} + +SyncSession::PublicState SyncSession::state() const +{ + std::unique_lock lock(m_state_mutex); + if (m_state == &State::waiting_for_access_token) { + return PublicState::WaitingForAccessToken; + } else if (m_state == &State::active) { + return PublicState::Active; + } else if (m_state == &State::dying) { + return PublicState::Dying; + } else if (m_state == &State::inactive) { + return PublicState::Inactive; + } else if (m_state == &State::error) { + return PublicState::Error; + } + REALM_UNREACHABLE(); +} + +// Represents a reference to the SyncSession from outside of the sync subsystem. +// We attempt to keep the SyncSession in an active state as long as it has an external reference. +class SyncSession::ExternalReference { +public: + ExternalReference(std::shared_ptr session) : m_session(std::move(session)) + {} + + ~ExternalReference() + { + m_session->did_drop_external_reference(); + } + +private: + std::shared_ptr m_session; +}; + +std::shared_ptr SyncSession::external_reference() +{ + std::unique_lock lock(m_state_mutex); + + if (auto external_reference = m_external_reference.lock()) + return std::shared_ptr(external_reference, this); + + auto external_reference = std::make_shared(shared_from_this()); + m_external_reference = external_reference; + return std::shared_ptr(external_reference, this); +} + +std::shared_ptr SyncSession::existing_external_reference() +{ + std::unique_lock lock(m_state_mutex); + + if (auto external_reference = m_external_reference.lock()) + return std::shared_ptr(external_reference, this); + + return nullptr; +} + +void SyncSession::did_drop_external_reference() +{ + std::unique_lock lock(m_state_mutex); + + // If the session is being resurrected we should not close the session. + if (!m_external_reference.expired()) + return; + + m_state->close(lock, *this); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp new file mode 100644 index 0000000..fadc3b4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_user.hpp" + +#include "sync/impl/sync_metadata.hpp" +#include "sync/sync_manager.hpp" +#include "sync/sync_session.hpp" + +namespace realm { + +SyncUser::SyncUser(std::string refresh_token, std::string identity, + util::Optional server_url, TokenType token_type) +: m_state(State::Active) +, m_server_url(server_url.value_or("")) +, m_token_type(token_type) +, m_refresh_token(std::move(refresh_token)) +, m_identity(std::move(identity)) +{ + if (token_type == TokenType::Normal) { + SyncManager::shared().perform_metadata_update([this, server_url=std::move(server_url)](const auto& manager) { + auto metadata = SyncUserMetadata(manager, m_identity); + metadata.set_state(server_url, m_refresh_token); + m_is_admin = metadata.is_admin(); + }); + } +} + +std::vector> SyncUser::all_sessions() +{ + std::lock_guard lock(m_mutex); + std::vector> sessions; + if (m_state == State::Error) { + return sessions; + } + for (auto it = m_sessions.begin(); it != m_sessions.end();) { + if (auto ptr_to_session = it->second.lock()) { + if (!ptr_to_session->is_in_error_state()) { + sessions.emplace_back(std::move(ptr_to_session)); + it++; + continue; + } + } + // This session is bad, destroy it. + it = m_sessions.erase(it); + } + return sessions; +} + +std::shared_ptr SyncUser::session_for_on_disk_path(const std::string& path) +{ + std::lock_guard lock(m_mutex); + if (m_state == State::Error) { + return nullptr; + } + auto it = m_sessions.find(path); + if (it == m_sessions.end()) { + return nullptr; + } + auto locked = it->second.lock(); + if (!locked) { + // Remove the session from the map, because it has fatally errored out or the entry is invalid. + m_sessions.erase(it); + } + return locked; +} + +void SyncUser::update_refresh_token(std::string token) +{ + std::vector> sessions_to_revive; + { + std::unique_lock lock(m_mutex); + if (auto session = m_management_session.lock()) + sessions_to_revive.emplace_back(std::move(session)); + + if (auto session = m_permission_session.lock()) + sessions_to_revive.emplace_back(std::move(session)); + + switch (m_state) { + case State::Error: + return; + case State::Active: + m_refresh_token = token; + break; + case State::LoggedOut: { + sessions_to_revive.reserve(m_waiting_sessions.size()); + m_refresh_token = token; + m_state = State::Active; + for (auto& pair : m_waiting_sessions) { + if (auto ptr = pair.second.lock()) { + m_sessions[pair.first] = ptr; + sessions_to_revive.emplace_back(std::move(ptr)); + } + } + m_waiting_sessions.clear(); + break; + } + } + // Update persistent user metadata. + if (m_token_type != TokenType::Admin) { + SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = SyncUserMetadata(manager, m_identity); + metadata.set_state(m_server_url, token); + }); + } + } + // (Re)activate all pending sessions. + // Note that we do this after releasing the lock, since the session may + // need to access protected User state in the process of binding itself. + for (auto& session : sessions_to_revive) { + session->revive_if_needed(); + } +} + +void SyncUser::log_out() +{ + if (m_token_type == TokenType::Admin) { + // Admin-token users cannot be logged out. + return; + } + std::lock_guard lock(m_mutex); + if (m_state == State::LoggedOut) { + return; + } + m_state = State::LoggedOut; + // Move all active sessions into the waiting sessions pool. If the user is + // logged back in, they will automatically be reactivated. + for (auto& pair : m_sessions) { + if (auto ptr = pair.second.lock()) { + ptr->log_out(); + m_waiting_sessions[pair.first] = ptr; + } + } + m_sessions.clear(); + // Deactivate the sessions for the management and admin Realms. + if (auto session = m_management_session.lock()) + session->log_out(); + + if (auto session = m_permission_session.lock()) + session->log_out(); + + // Mark the user as 'dead' in the persisted metadata Realm. + SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = SyncUserMetadata(manager, m_identity, false); + metadata.mark_for_removal(); + }); +} + +void SyncUser::set_is_admin(bool is_admin) +{ + if (m_token_type == TokenType::Admin) { + return; + } + m_is_admin = is_admin; + SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = SyncUserMetadata(manager, m_identity); + metadata.set_is_admin(is_admin); + }); +} + +void SyncUser::invalidate() +{ + std::lock_guard lock(m_mutex); + m_state = State::Error; +} + +std::string SyncUser::refresh_token() const +{ + std::lock_guard lock(m_mutex); + return m_refresh_token; +} + +SyncUser::State SyncUser::state() const +{ + std::lock_guard lock(m_mutex); + return m_state; +} + +void SyncUser::register_session(std::shared_ptr session) +{ + const std::string& path = session->path(); + std::unique_lock lock(m_mutex); + switch (m_state) { + case State::Active: + // Immediately ask the session to come online. + m_sessions[path] = session; + // FIXME: `SyncUser`s shouldn't even wrap admin tokens; the bindings should do that. + if (m_token_type == TokenType::Admin) { + session->bind_with_admin_token(m_refresh_token, session->config().realm_url); + } else { + lock.unlock(); + session->revive_if_needed(); + } + break; + case State::LoggedOut: + m_waiting_sessions[path] = session; + break; + case State::Error: + break; + } +} + +void SyncUser::register_management_session(const std::string& path) +{ + std::lock_guard lock(m_mutex); + if (m_management_session.lock() || m_state == State::Error) + return; + + m_management_session = SyncManager::shared().get_existing_session(path); +} + +void SyncUser::register_permission_session(const std::string& path) +{ + std::lock_guard lock(m_mutex); + if (m_permission_session.lock() || m_state == State::Error) + return; + + m_permission_session = SyncManager::shared().get_existing_session(path); +} + +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp new file mode 100644 index 0000000..25f9129 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "thread_safe_reference.hpp" + +#include "impl/realm_coordinator.hpp" +#include "list.hpp" +#include "object.hpp" +#include "object_schema.hpp" +#include "results.hpp" + +#include + +using namespace realm; + +ThreadSafeReferenceBase::ThreadSafeReferenceBase(SharedRealm source_realm) : m_source_realm(std::move(source_realm)) +{ + m_source_realm->verify_thread(); + if (m_source_realm->is_in_transaction()) { + throw InvalidTransactionException("Cannot obtain thread safe reference during a write transaction."); + } + + try { + m_version_id = get_source_shared_group().pin_version(); + } catch (...) { + invalidate(); + throw; + } +} + +ThreadSafeReferenceBase::~ThreadSafeReferenceBase() +{ + if (!is_invalidated()) + invalidate(); +} + +template +V ThreadSafeReferenceBase::invalidate_after_import(Realm& destination_realm, T construct_with_shared_group) { + destination_realm.verify_thread(); + REALM_ASSERT_DEBUG(!m_source_realm->is_in_transaction()); + REALM_ASSERT_DEBUG(!is_invalidated()); + + SharedGroup& destination_shared_group = *Realm::Internal::get_shared_group(destination_realm); + auto unpin_version = util::make_scope_exit([&]() noexcept { invalidate(); }); + + return construct_with_shared_group(destination_shared_group); +} + +SharedGroup& ThreadSafeReferenceBase::get_source_shared_group() const { + return *Realm::Internal::get_shared_group(*m_source_realm); +} + +bool ThreadSafeReferenceBase::has_same_config(Realm& realm) const { + return &Realm::Internal::get_coordinator(*m_source_realm) == &Realm::Internal::get_coordinator(realm); +} + +void ThreadSafeReferenceBase::invalidate() { + REALM_ASSERT_DEBUG(m_source_realm); + SharedRealm thread_local_realm = Realm::Internal::get_coordinator(*m_source_realm).get_realm(); + Realm::Internal::get_shared_group(*thread_local_realm)->unpin_version(m_version_id); + m_source_realm = nullptr; +} + +ThreadSafeReference::ThreadSafeReference(List const& list) +: ThreadSafeReferenceBase(list.get_realm()) +, m_link_view(get_source_shared_group().export_linkview_for_handover(list.m_link_view)) { } + +List ThreadSafeReference::import_into_realm(SharedRealm realm) && { + return invalidate_after_import(*realm, [&](SharedGroup& shared_group) { + LinkViewRef link_view = shared_group.import_linkview_from_handover(std::move(m_link_view)); + return List(std::move(realm), std::move(link_view)); + }); +} + +ThreadSafeReference::ThreadSafeReference(Object const& object) +: ThreadSafeReferenceBase(object.realm()) +, m_row(get_source_shared_group().export_for_handover(object.row())) +, m_object_schema_name(object.get_object_schema().name) { } + +Object ThreadSafeReference::import_into_realm(SharedRealm realm) && { + return invalidate_after_import(*realm, [&](SharedGroup& shared_group) { + Row row = *shared_group.import_from_handover(std::move(m_row)); + auto object_schema = realm->schema().find(m_object_schema_name); + REALM_ASSERT_DEBUG(object_schema != realm->schema().end()); + return Object(std::move(realm), *object_schema, row); + }); +} + +ThreadSafeReference::ThreadSafeReference(Results const& results) +: ThreadSafeReferenceBase(results.get_realm()) +, m_query(get_source_shared_group().export_for_handover(results.get_query(), ConstSourcePayload::Copy)) +, m_sort_order([&]() { + SortDescriptor::HandoverPatch sort_order; + SortDescriptor::generate_patch(results.get_sort(), sort_order); + return sort_order; +}()) +, m_distinct_descriptor([&]() { + SortDescriptor::HandoverPatch distinct_descriptor; + SortDescriptor::generate_patch(results.get_distinct(), distinct_descriptor); + return distinct_descriptor; +}()){ } + +Results ThreadSafeReference::import_into_realm(SharedRealm realm) && { + return invalidate_after_import(*realm, [&](SharedGroup& shared_group) { + Query query = *shared_group.import_from_handover(std::move(m_query)); + Table& table = *query.get_table(); + SortDescriptor sort_descriptor = SortDescriptor::create_from_and_consume_patch(m_sort_order, table); + SortDescriptor distinct_descriptor = SortDescriptor::create_from_and_consume_patch(m_distinct_descriptor, table); + return Results(std::move(realm), std::move(query), std::move(sort_descriptor), std::move(distinct_descriptor)); + }); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/format.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/format.cpp new file mode 100644 index 0000000..4103c8d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/format.cpp @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "util/format.hpp" + +#include + +#include +#include + +namespace realm { namespace _impl { +Printable::Printable(StringData value) : m_type(Type::String), m_string(value.data()) { } + +void Printable::print(std::ostream& out) const +{ + switch (m_type) { + case Printable::Type::Bool: + out << (m_uint ? "true" : "false"); + break; + case Printable::Type::Uint: + out << m_uint; + break; + case Printable::Type::Int: + out << m_int; + break; + case Printable::Type::String: + out << m_string; + break; + } +} + +std::string format(const char* fmt, std::initializer_list values) +{ + std::stringstream ss; + while (*fmt) { + auto next = strchr(fmt, '%'); + + // emit the rest of the format string if there are no more percents + if (!next) { + ss << fmt; + break; + } + + // emit everything up to the next percent + ss.write(fmt, next - fmt); + ++next; + REALM_ASSERT(*next); + + // %% produces a single escaped % + if (*next == '%') { + ss << '%'; + fmt = next + 1; + continue; + } + REALM_ASSERT(isdigit(*next)); + + // The const_cast is safe because stroul does not actually modify + // the pointed-to string, but it lacks a const overload + auto index = strtoul(next, const_cast(&fmt), 10) - 1; + REALM_ASSERT(index < values.size()); + (values.begin() + index)->print(ss); + } + return ss.str(); +} + +} // namespace _impl +} // namespace realm diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp new file mode 100644 index 0000000..8dec0f7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include "util/uuid.hpp" + +#include +#include +#include +#include +#include + +namespace { + +// Seed `engine` with as much random state as it requires, based on the approach outlined in P0205R0. +// +template +T create_and_seed_engine() +{ + constexpr auto bytes_needed = T::state_size * sizeof(typename T::result_type); + + constexpr auto numbers_needed = sizeof(std::random_device::result_type) < sizeof(std::seed_seq::result_type) + ? (bytes_needed / sizeof(std::random_device::result_type)) + : (bytes_needed / sizeof(std::seed_seq::result_type)); + + std::array state; + std::random_device rd; + std::generate(begin(state), end(state), std::ref(rd)); + std::seed_seq seeds(begin(state), end(state)); + + T engine; + engine.seed(seeds); + return engine; +} + +} // unnamed namespace + +namespace realm { +namespace util { + +std::string uuid_string() +{ + static auto engine = create_and_seed_engine(); + + std::array uuid_bytes; + std::uniform_int_distribution distribution(0, std::numeric_limits::max()); + std::generate(begin(uuid_bytes), end(uuid_bytes), [&] { return distribution(engine); }); + + // Version 4 UUID. + uuid_bytes[6] = (uuid_bytes[6] & 0x0f) | 0x40; + // IETF variant. + uuid_bytes[8] = (uuid_bytes[8] & 0x3f) | 0x80; + + std::array uuid_formatted; + snprintf(uuid_formatted.data(), uuid_formatted.size() + 1, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3], + uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7], + uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11], + uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]); + + return std::string(uuid_formatted.data(), uuid_formatted.size()); +} + +} // namespace util +} // namespace realm diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMAccessor.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAccessor.mm new file mode 100644 index 0000000..ff93d17 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAccessor.mm @@ -0,0 +1,694 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMAccessor.h" + +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.h" +#import "RLMSchema_Private.h" +#import "RLMUtil.hpp" +#import "results.hpp" +#import "property.hpp" + +#import +#import + +template +static inline T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { + RLMVerifyAttached(obj); + return obj->_row.get_table()->get(obj->_info->objectSchema->persisted_properties[index].table_column, + obj->_row.get_index()); +} + +template +static NSNumber *getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { + RLMVerifyAttached(obj); + auto col = obj->_info->objectSchema->persisted_properties[index].table_column; + if (obj->_row.is_null(col)) { + return nil; + } + return @(obj->_row.get_table()->get(col, obj->_row.get_index())); +} + +// long getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, long long val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), val, setDefault); +} + +// float getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, float val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), val, setDefault); +} + +// double getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, double val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), val, setDefault); +} + +// bool getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, BOOL val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), val, setDefault); +} + +// string getter/setter +static inline NSString *RLMGetString(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMStringDataToNSString(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + try { + obj->_row.get_table()->set_string(colIndex, obj->_row.get_index(), RLMStringDataWithNSString(val), setDefault); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +static inline void setNull(realm::Table& table, size_t colIndex, size_t rowIndex, bool setDefault) { + try { + table.set_null(colIndex, rowIndex, setDefault); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +// date getter/setter +static inline NSDate *RLMGetDate(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMTimestampToNSDate(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSDate *const date, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + if (date) { + obj->_row.get_table()->set_timestamp(colIndex, obj->_row.get_index(), RLMTimestampForNSDate(date), setDefault); + } + else { + setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + } +} + +// data getter/setter +static inline NSData *RLMGetData(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMBinaryDataToNSData(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSData *const data, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + try { + obj->_row.get_table()->set_binary(colIndex, obj->_row.get_index(), RLMBinaryDataForNSData(data), setDefault); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, + __unsafe_unretained NSString *const className, + __unsafe_unretained id const value, + RLMCreationOptions creationOptions) NS_RETURNS_RETAINED; +static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, + __unsafe_unretained NSString *const className, + __unsafe_unretained id const value, + RLMCreationOptions creationOptions) { + RLMObjectBase *link = RLMDynamicCast(value); + if (!link || ![link->_objectSchema.className isEqualToString:className]) { + // create from non-rlmobject + return RLMCreateObjectInRealmWithValue(realm, className, value, creationOptions & RLMCreationOptionsCreateOrUpdate); + } + + if (link.isInvalidated) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + + if (link->_realm == realm) { + return link; + } + + if (creationOptions & RLMCreationOptionsPromoteUnmanaged) { + if (!link->_realm) { + RLMAddObjectToRealm(link, realm, creationOptions & RLMCreationOptionsCreateOrUpdate); + return link; + } + @throw RLMException(@"Can not add objects from a different Realm"); + } + + // copy from another realm or copy from unmanaged + return RLMCreateObjectInRealmWithValue(realm, className, link, creationOptions & RLMCreationOptionsCreateOrUpdate); +} + +// link getter/setter +static inline RLMObjectBase *RLMGetLink(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propertyIndex) { + RLMVerifyAttached(obj); + auto colIndex = obj->_info->objectSchema->persisted_properties[propertyIndex].table_column; + + if (obj->_row.is_null_link(colIndex)) { + return nil; + } + NSUInteger index = obj->_row.get_link(colIndex); + return RLMCreateObjectAccessor(obj->_realm, obj->_info->linkTargetType(propertyIndex), index); +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained RLMObjectBase *const val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + if (!val) { + obj->_row.nullify_link(colIndex); + return; + } + + RLMObjectBase *link = RLMGetLinkedObjectForValue(obj->_realm, val->_objectSchema.className, + val, RLMCreationOptionsPromoteUnmanaged); + + // make sure it is the correct type + if (link->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { + @throw RLMException(@"Can't set object of type '%@' to property of type '%@'", + val->_objectSchema.className, + obj->_info->propertyForTableColumn(colIndex).objectClassName); + } + obj->_row.get_table()->set_link(colIndex, obj->_row.get_index(), link->_row.get_index(), setDefault); +} + +// array getter/setter +static inline RLMArray *RLMGetArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + RLMVerifyAttached(obj); + auto prop = obj->_info->rlmObjectSchema.properties[colIndex]; + return [[RLMArrayLinkView alloc] initWithParent:obj property:prop]; +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained id const array, __unused bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex); + // remove all old + // FIXME: make sure delete rules don't purge objects + linkView->clear(); + for (RLMObjectBase *link in array) { + RLMObjectBase * addedLink = RLMGetLinkedObjectForValue(obj->_realm, link->_objectSchema.className, link, RLMCreationOptionsPromoteUnmanaged); + linkView->add(addedLink->_row.get_index()); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const intObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (intObject) { + obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), intObject.longLongValue, setDefault); + } + else { + setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const floatObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (floatObject) { + obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), floatObject.floatValue, setDefault); + } + else { + setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const doubleObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (doubleObject) { + obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), doubleObject.doubleValue, setDefault); + } + else { + setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const boolObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (boolObject) { + obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), boolObject.boolValue, setDefault); + } + else { + setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline RLMLinkingObjects *RLMGetLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const property) { + RLMVerifyAttached(obj); + auto& objectInfo = obj->_realm->_info[property.objectClassName]; + auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String); + auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), objectInfo.table(), linkingProperty->table_column); + realm::Results results(obj->_realm->_realm, std::move(backlinkView)); + return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)]; +} + +// any getter/setter +static inline id RLMGetAnyProperty(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx) { + RLMVerifyAttached(obj); + return RLMMixedToObjc(obj->_row.get_mixed(col_ndx)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger, __unsafe_unretained id, bool) { + RLMVerifyInWriteTransaction(obj); + @throw RLMException(@"Modifying Mixed properties is not supported"); +} + +// dynamic getter with column closure +static id RLMAccessorGetter(RLMProperty *prop, const char *type) { + NSUInteger index = prop.index; + bool boxed = prop.optional || *type == '@'; + switch (prop.type) { + case RLMPropertyTypeInt: + if (boxed) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; + } + switch (*type) { + case 'c': + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; + case 's': + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; + case 'i': + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; + case 'l': + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; + case 'q': + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; + default: + @throw RLMException(@"Unexpected property type for Objective-C type code"); + } + case RLMPropertyTypeFloat: + if (boxed) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; + } + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }; + case RLMPropertyTypeDouble: + if (boxed) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; + } + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }; + case RLMPropertyTypeBool: + if (boxed) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; + } + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }; + case RLMPropertyTypeString: + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetString(obj, index); + }; + case RLMPropertyTypeDate: + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetDate(obj, index); + }; + case RLMPropertyTypeData: + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetData(obj, index); + }; + case RLMPropertyTypeObject: + return ^id(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetLink(obj, index); + }; + case RLMPropertyTypeArray: + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetArray(obj, index); + }; + case RLMPropertyTypeAny: + @throw RLMException(@"Cannot create accessor class for schema with Mixed properties"); + case RLMPropertyTypeLinkingObjects: + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetLinkingObjects(obj, prop); + }; + } +} + +template +static void RLMWrapSetter(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const name, Function&& f) { + if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, obj->_row.get_index(), *obj->_info)) { + info->willChange(name); + f(); + info->didChange(name); + } + else { + f(); + } +} + +template +static id makeSetter(__unsafe_unretained RLMProperty *const prop) { + NSUInteger index = prop.index; + NSString *name = prop.name; + if (prop.isPrimary) { + return ^(__unused RLMObjectBase *obj, __unused ArgType val) { + @throw RLMException(@"Primary key can't be changed after an object is inserted."); + }; + } + return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) { + RLMWrapSetter(obj, name, [&] { + RLMSetValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, + static_cast(val), false); + }); + }; +} + +// dynamic setter with column closure +static id RLMAccessorSetter(RLMProperty *prop, const char *type) { + bool boxed = prop.optional || *type == '@'; + switch (prop.type) { + case RLMPropertyTypeInt: + if (boxed) { + return makeSetter *>(prop); + } + switch (*type) { + case 'c': return makeSetter(prop); + case 's': return makeSetter(prop); + case 'i': return makeSetter(prop); + case 'l': return makeSetter(prop); + case 'q': return makeSetter(prop); + default: + @throw RLMException(@"Unexpected property type for Objective-C type code"); + } + case RLMPropertyTypeFloat: + return boxed ? makeSetter *>(prop) : makeSetter(prop); + case RLMPropertyTypeDouble: + return boxed ? makeSetter *>(prop) : makeSetter(prop); + case RLMPropertyTypeBool: + return boxed ? makeSetter *>(prop) : makeSetter(prop); + case RLMPropertyTypeString: return makeSetter(prop); + case RLMPropertyTypeDate: return makeSetter(prop); + case RLMPropertyTypeData: return makeSetter(prop); + case RLMPropertyTypeObject: return makeSetter(prop); + case RLMPropertyTypeArray: return makeSetter(prop); + case RLMPropertyTypeAny: return makeSetter(prop); + case RLMPropertyTypeLinkingObjects: return nil; + } +} + +// call getter for superclass for property at colIndex +static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { + typedef id (*getter_type)(RLMObjectBase *, SEL); + RLMProperty *prop = obj->_objectSchema[propName]; + Class superClass = class_getSuperclass(obj.class); + getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel]; + return superGetter(obj, prop.getterSel); +} + +// call setter for superclass for property at colIndex +static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { + typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar); + RLMProperty *prop = obj->_objectSchema[propName]; + Class superClass = class_getSuperclass(obj.class); + setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel]; + superSetter(obj, prop.setterSel, val); +} + +// getter/setter for unmanaged object +static id RLMAccessorUnmanagedGetter(RLMProperty *prop, const char *) { + // only override getters for RLMArray and linking objects properties + if (prop.type == RLMPropertyTypeArray) { + NSString *objectClassName = prop.objectClassName; + NSString *propName = prop.name; + + return ^(RLMObjectBase *obj) { + id val = RLMSuperGet(obj, propName); + if (!val) { + val = [[RLMArray alloc] initWithObjectClassName:objectClassName]; + RLMSuperSet(obj, propName, val); + } + return val; + }; + } + else if (prop.type == RLMPropertyTypeLinkingObjects) { + return ^(RLMObjectBase *){ + return [RLMResults emptyDetachedResults]; + }; + } + return nil; +} +static id RLMAccessorUnmanagedSetter(RLMProperty *prop, const char *) { + if (prop.type != RLMPropertyTypeArray) { + return nil; + } + + NSString *propName = prop.name; + NSString *objectClassName = prop.objectClassName; + return ^(RLMObjectBase *obj, id ar) { + // make copy when setting (as is the case for all other variants) + RLMArray *standaloneAr = [[RLMArray alloc] initWithObjectClassName:objectClassName]; + [standaloneAr addObjects:ar]; + RLMSuperSet(obj, propName, standaloneAr); + }; +} + +// implement the class method className on accessors to return the className of the +// base object +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class){ return className; }); + class_addMethod(metaClass, @selector(className), imp, "@@:"); +} + +// implement the shared schema method +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class cls) { + if (cls == accessorClass) { + return schema; + } + + // If we aren't being called directly on the class this was overriden + // for, the class is either a subclass which we haven't initialized yet, + // or it's a runtime-generated class which should use the parent's + // schema. We check for the latter by checking if the immediate + // descendent of the desired class is a class generated by us (there + // may be further subclasses not generated by us for things like KVO). + Class parent = class_getSuperclass(cls); + while (parent != accessorClass) { + cls = parent; + parent = class_getSuperclass(cls); + } + + static const char accessorClassPrefix[] = "RLM:"; + if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { + return schema; + } + + return [RLMSchema sharedSchemaForClass:cls]; + }); + class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); +} + +static void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, + id (*getter)(RLMProperty *, const char *), + id (*setter)(RLMProperty *, const char *)) { + SEL sel = prop.getterSel; + auto getterMethod = class_getInstanceMethod(cls, sel); + if (!getterMethod) { + return; + } + + const char *getterType = method_getTypeEncoding(getterMethod); + if (id block = getter(prop, getterType)) { + class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType); + } + + if (!(sel = prop.setterSel)) { + return; + } + auto setterMethod = class_getInstanceMethod(cls, sel); + if (!setterMethod) { + return; + } + if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from + class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod)); + } +} + +static Class RLMCreateAccessorClass(Class objectClass, + RLMObjectSchema *schema, + const char *accessorClassName, + id (*getterGetter)(RLMProperty *, const char *), + id (*setterGetter)(RLMProperty *, const char *)) { + REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass)); + + // create and register proxy class which derives from object class + Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0); + if (!accClass) { + // Class with that name already exists, so just return the pre-existing one + // This should only happen for our standalone "accessors" + return objc_lookUpClass(accessorClassName); + } + + // override getters/setters for each propery + for (RLMProperty *prop in schema.properties) { + addMethod(accClass, prop, getterGetter, setterGetter); + } + for (RLMProperty *prop in schema.computedProperties) { + addMethod(accClass, prop, getterGetter, setterGetter); + } + + objc_registerClassPair(accClass); + + return accClass; +} + +Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) { + return RLMCreateAccessorClass(objectClass, schema, name, RLMAccessorGetter, RLMAccessorSetter); +} + +Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) { + return RLMCreateAccessorClass(objectClass, schema, [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String, + RLMAccessorUnmanagedGetter, RLMAccessorUnmanagedSetter); +} + +void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) { + RLMObjectSchema *schema = obj->_objectSchema; + RLMProperty *prop = schema[propName]; + if (!prop) { + @throw RLMException(@"Invalid property name '%@' for class '%@'.", + propName, obj->_objectSchema.className); + } + if (prop.isPrimary) { + @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val); + } + if (!RLMIsObjectValidForProperty(val, prop)) { + @throw RLMException(@"Invalid property value '%@' for property '%@' of class '%@'", + val, propName, obj->_objectSchema.className); + } + + RLMDynamicSet(obj, prop, RLMCoerceToNil(val), RLMCreationOptionsPromoteUnmanaged); +} + +// Precondition: the property is not a primary key +void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained id const val, RLMCreationOptions creationOptions) { + REALM_ASSERT_DEBUG(!prop.isPrimary); + bool setDefault = creationOptions & RLMCreationOptionsSetDefault; + + auto col = obj->_info->tableColumn(prop); + RLMWrapSetter(obj, prop.name, [&] { + switch (prop.type) { + case RLMPropertyTypeInt: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; + case RLMPropertyTypeFloat: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; + case RLMPropertyTypeDouble: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; + case RLMPropertyTypeBool: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; + case RLMPropertyTypeString: RLMSetValue(obj, col, (NSString *)val, setDefault); break; + case RLMPropertyTypeDate: RLMSetValue(obj, col, (NSDate *)val, setDefault); break; + case RLMPropertyTypeData: RLMSetValue(obj, col, (NSData *)val, setDefault); break; + case RLMPropertyTypeObject: { + if (!val || val == NSNull.null) { + RLMSetValue(obj, col, (RLMObjectBase *)nil, setDefault); + } + else { + auto linkedObj = RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, val, creationOptions); + RLMSetValue(obj, col, linkedObj, setDefault); + } + break; + } + case RLMPropertyTypeArray: + if (!val || val == NSNull.null) { + RLMSetValue(obj, col, (id)nil, setDefault); + } + else { + id rawLinks = val; + NSMutableArray *links = [NSMutableArray array]; + for (id rawLink in rawLinks) { + [links addObject:RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, rawLink, creationOptions)]; + } + RLMSetValue(obj, col, links, setDefault); + } + break; + case RLMPropertyTypeAny: + RLMSetValue(obj, col, val, setDefault); + break; + case RLMPropertyTypeLinkingObjects: + @throw RLMException(@"Linking objects properties are read-only"); + } + }); +} + +id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) { + NSUInteger index = prop.index; + switch (prop.type) { + case RLMPropertyTypeInt: return getBoxed(obj, index); + case RLMPropertyTypeFloat: return getBoxed(obj, index); + case RLMPropertyTypeDouble: return getBoxed(obj, index); + case RLMPropertyTypeBool: return getBoxed(obj, index); + case RLMPropertyTypeString: return RLMGetString(obj, index); + case RLMPropertyTypeDate: return RLMGetDate(obj, index); + case RLMPropertyTypeData: return RLMGetData(obj, index); + case RLMPropertyTypeObject: return RLMGetLink(obj, index); + case RLMPropertyTypeArray: return RLMGetArray(obj, index); + case RLMPropertyTypeAny: return RLMGetAnyProperty(obj, index); + case RLMPropertyTypeLinkingObjects: return RLMGetLinkingObjects(obj, prop); + } +} + +id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const propName, bool asList) { + RLMProperty *prop = obj->_objectSchema[propName]; + if (!prop) { + @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + } + if (asList && prop.type == RLMPropertyTypeArray && prop.swiftIvar) { + RLMListBase *list = object_getIvar(obj, prop.swiftIvar); + if (!list._rlmArray) { + list._rlmArray = RLMDynamicGet(obj, prop); + } + return list; + } + + return RLMDynamicGet(obj, prop); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMAnalytics.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAnalytics.mm new file mode 100644 index 0000000..b33afd8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAnalytics.mm @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously submits build information to Realm if running in an iOS +// simulator or on OS X if a debugger is attached. Does nothing if running on an +// iOS / watchOS device or if a debugger is *not* attached. +// +// To be clear: this does *not* run when your app is in production or on +// your end-user’s devices; it will only run in the simulator or when a debugger +// is attached. +// +// Why are we doing this? In short, because it helps us build a better product +// for you. None of the data personally identifies you, your employer or your +// app, but it *will* help us understand what language you use, what iOS +// versions you target, etc. Having this info will help prioritizing our time, +// adding new features and deprecating old features. Collecting an anonymized +// bundle & anonymized MAC is the only way for us to count actual usage of the +// other metrics accurately. If we don’t have a way to deduplicate the info +// reported, it will be useless, as a single developer building their Swift app +// 10 times would report 10 times more than a single Objective-C developer that +// only builds once, making the data all but useless. +// No one likes sharing data unless it’s necessary, we get it, and we’ve +// debated adding this for a long long time. Since Realm is a free product +// without an email signup, we feel this is a necessary step so we can collect +// relevant data to build a better product for you. If you truly, absolutely +// feel compelled to not send this data back to Realm, then you can set an env +// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe +// letting these analytics run is a small price to pay for the product & support +// we give you. +// +// Currently the following information is reported: +// - What version of Realm is being used, and from which language (obj-c or Swift). +// - What version of OS X it's running on (in case Xcode aggressively drops +// support for older versions again, we need to know what we need to support). +// - The minimum iOS/OS X version that the application is targeting (again, to +// help us decide what versions we need to support). +// - An anonymous MAC address and bundle ID to aggregate the other information on. +// - What version of Swift is being used (if applicable). + +#import "RLMAnalytics.hpp" + +#import + +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_MAC || (TARGET_OS_WATCH && TARGET_OS_SIMULATOR) || (TARGET_OS_TV && TARGET_OS_SIMULATOR) +#import "RLMRealm.h" +#import "RLMUtil.hpp" + +#import +#import +#import +#import +#import + +#import + +#ifndef REALM_COCOA_VERSION +#import "RLMVersion.h" +#endif + +#import + +// Declared for RealmSwiftObjectUtil +@interface NSObject (SwiftVersion) ++ (NSString *)swiftVersion; +@end + +// Wrapper for sysctl() that handles the memory management stuff +static auto RLMSysCtl(int *mib, u_int mibSize, size_t *bufferSize) { + std::unique_ptr buffer(nullptr, &free); + + int ret = sysctl(mib, mibSize, nullptr, bufferSize, nullptr, 0); + if (ret != 0) { + return buffer; + } + + buffer.reset(malloc(*bufferSize)); + if (!buffer) { + return buffer; + } + + ret = sysctl(mib, mibSize, buffer.get(), bufferSize, nullptr, 0); + if (ret != 0) { + buffer.reset(); + } + + return buffer; +} + +// Get the version of OS X we're running on (even in the simulator this gives +// the OS X version and not the simulated iOS version) +static NSString *RLMOSVersion() { + std::array mib = {{CTL_KERN, KERN_OSRELEASE}}; + size_t bufferSize; + auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize); + if (!buffer) { + return nil; + } + + return [[NSString alloc] initWithBytesNoCopy:buffer.release() + length:bufferSize - 1 + encoding:NSUTF8StringEncoding + freeWhenDone:YES]; +} + +// Hash the data in the given buffer and convert it to a hex-format string +static NSString *RLMHashData(const void *bytes, size_t length) { + unsigned char buffer[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(bytes, static_cast(length), buffer); + + char formatted[CC_SHA256_DIGEST_LENGTH * 2 + 1]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) { + sprintf(formatted + i * 2, "%02x", buffer[i]); + } + + return [[NSString alloc] initWithBytes:formatted + length:CC_SHA256_DIGEST_LENGTH * 2 + encoding:NSUTF8StringEncoding]; +} + +// Returns the hash of the MAC address of the first network adaptor since the +// vendorIdentifier isn't constant between iOS simulators. +static NSString *RLMMACAddress() { + int en0 = static_cast(if_nametoindex("en0")); + if (!en0) { + return nil; + } + + std::array mib = {{CTL_NET, PF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, en0}}; + size_t bufferSize; + auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize); + if (!buffer) { + return nil; + } + + // sockaddr_dl struct is immediately after the if_msghdr struct in the buffer + auto sockaddr = reinterpret_cast(static_cast(buffer.get()) + 1); + auto mac = reinterpret_cast(sockaddr->sdl_data + sockaddr->sdl_nlen); + + return RLMHashData(mac, 6); +} + +static NSDictionary *RLMAnalyticsPayload() { + NSBundle *appBundle = NSBundle.mainBundle; + NSString *hashedBundleID = appBundle.bundleIdentifier; + + // Main bundle isn't always the one of interest (e.g. when running tests + // it's xctest rather than the app's bundle), so look for one with a bundle ID + if (!hashedBundleID) { + for (NSBundle *bundle in NSBundle.allBundles) { + if ((hashedBundleID = bundle.bundleIdentifier)) { + appBundle = bundle; + break; + } + } + } + + // If we found a bundle ID anywhere, hash it as it could contain sensitive + // information (e.g. the name of an unnanounced product) + if (hashedBundleID) { + NSData *data = [hashedBundleID dataUsingEncoding:NSUTF8StringEncoding]; + hashedBundleID = RLMHashData(data.bytes, data.length); + } + + NSString *osVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString]; + Class swiftObjectUtilClass = NSClassFromString(@"RealmSwiftObjectUtil"); + BOOL isSwift = swiftObjectUtilClass != nil; + NSString *swiftVersion = isSwift ? [swiftObjectUtilClass swiftVersion] : @"N/A"; + + static NSString *kUnknownString = @"unknown"; + NSString *hashedMACAddress = RLMMACAddress() ?: kUnknownString; + + return @{ + @"event": @"Run", + @"properties": @{ + // MixPanel properties + @"token": @"ce0fac19508f6c8f20066d345d360fd0", + + // Anonymous identifiers to deduplicate events + @"distinct_id": hashedMACAddress, + @"Anonymized MAC Address": hashedMACAddress, + @"Anonymized Bundle ID": hashedBundleID ?: kUnknownString, + + // Which version of Realm is being used + @"Binding": @"cocoa", + @"Language": isSwift ? @"swift" : @"objc", + @"Realm Version": REALM_COCOA_VERSION, + @"Sync Version": @(REALM_SYNC_VER_STRING), +#if TARGET_OS_WATCH + @"Target OS Type": @"watchos", +#elif TARGET_OS_TV + @"Target OS Type": @"tvos", +#elif TARGET_OS_IPHONE + @"Target OS Type": @"ios", +#else + @"Target OS Type": @"osx", +#endif + @"Swift Version": swiftVersion, + // Current OS version the app is targetting + @"Target OS Version": osVersionString, + // Minimum OS version the app is targetting + @"Target OS Minimum Version": appBundle.infoDictionary[@"MinimumOSVersion"] ?: kUnknownString, + + // Host OS version being built on + @"Host OS Type": @"osx", + @"Host OS Version": RLMOSVersion() ?: kUnknownString, + } + }; +} + +void RLMSendAnalytics() { + if (getenv("REALM_DISABLE_ANALYTICS") || !RLMIsDebuggerAttached() || RLMIsRunningInPlayground()) { + return; + } + + + NSData *payload = [NSJSONSerialization dataWithJSONObject:RLMAnalyticsPayload() options:0 error:nil]; + NSString *url = [NSString stringWithFormat:@"https://api.mixpanel.com/track/?data=%@&ip=1", [payload base64EncodedStringWithOptions:0]]; + + // No error handling or anything because logging errors annoyed people for no + // real benefit, and it's not clear what else we could do + [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url]] resume]; +} + +#else + +void RLMSendAnalytics() {} + +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMArray.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMArray.mm new file mode 100644 index 0000000..26922fd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMArray.mm @@ -0,0 +1,520 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMArray_Private.hpp" + +#import "RLMObjectSchema.h" +#import "RLMObjectStore.h" +#import "RLMObject_Private.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMThreadSafeReference_Private.hpp" +#import "RLMUtil.hpp" + +#import + +// See -countByEnumeratingWithState:objects:count +@interface RLMArrayHolder : NSObject { +@public + std::unique_ptr items; +} +@end +@implementation RLMArrayHolder +@end + +@interface RLMArray () +@end + +@implementation RLMArray { +@public + // Backing array when this instance is unmanaged + NSMutableArray *_backingArray; +} + +template +static void changeArray(__unsafe_unretained RLMArray *const ar, + NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { + if (!ar->_backingArray) { + ar->_backingArray = [NSMutableArray new]; + } + + if (RLMObjectBase *parent = ar->_parentObject) { + NSIndexSet *indexes = is(); + [parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + f(); + [parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + } + else { + f(); + } +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return is; }); +} + +- (instancetype)initWithObjectClassName:(__unsafe_unretained NSString *const)objectClassName { + self = [super init]; + if (self) { + _objectClassName = objectClassName; + } + return self; +} + +- (RLMRealm *)realm { + return nil; +} + +// +// Generic implementations for all RLMArray variants +// + +- (id)firstObject { + if (self.count) { + return [self objectAtIndex:0]; + } + return nil; +} + +- (id)lastObject { + NSUInteger count = self.count; + if (count) { + return [self objectAtIndex:count-1]; + } + return nil; +} + +- (void)addObjects:(id)objects { + for (id obj in objects) { + [self addObject:obj]; + } +} + +- (void)addObject:(RLMObject *)object { + [self insertObject:object atIndex:self.count]; +} + +- (void)removeLastObject { + NSUInteger count = self.count; + if (count) { + [self removeObjectAtIndex:count-1]; + } +} + +- (id)objectAtIndexedSubscript:(NSUInteger)index { + return [self objectAtIndex:index]; +} + +- (void)setObject:(id)newValue atIndexedSubscript:(NSUInteger)index { + [self replaceObjectAtIndex:index withObject:newValue]; +} + +// +// Unmanaged RLMArray implementation +// + +static void RLMValidateMatchingObjectType(RLMArray *array, RLMObject *object) { + if (!object) { + @throw RLMException(@"Object must not be nil"); + } + if (!object->_objectSchema) { + @throw RLMException(@"Object cannot be inserted unless the schema is initialized. " + "This can happen if you try to insert objects into a RLMArray / List from a default value or from an overriden unmanaged initializer (`init()`)."); + } + if (![array->_objectClassName isEqualToString:object->_objectSchema.className]) { + @throw RLMException(@"Object type '%@' does not match RLMArray type '%@'.", + object->_objectSchema.className, array->_objectClassName); + } +} + +static void RLMValidateArrayBounds(__unsafe_unretained RLMArray *const ar, + NSUInteger index, bool allowOnePastEnd=false) { + NSUInteger max = ar->_backingArray.count + allowOnePastEnd; + if (index >= max) { + @throw RLMException(@"Index %llu is out of bounds (must be less than %llu).", + (unsigned long long)index, (unsigned long long)max); + } +} + +- (id)objectAtIndex:(NSUInteger)index { + RLMValidateArrayBounds(self, index); + if (!_backingArray) { + _backingArray = [NSMutableArray new]; + } + return [_backingArray objectAtIndex:index]; +} + +- (NSUInteger)count { + return _backingArray.count; +} + +- (BOOL)isInvalidated { + return NO; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(__unused NSUInteger)len { + if (state->state != 0) { + return 0; + } + + // We need to enumerate a copy of the backing array so that it doesn't + // reflect changes made during enumeration. This copy has to be autoreleased + // (since there's nowhere for us to store a strong reference), and uses + // RLMArrayHolder rather than an NSArray because NSArray doesn't guarantee + // that it'll use a single contiguous block of memory, and if it doesn't + // we'd need to forward multiple calls to this method to the same NSArray, + // which would require holding a reference to it somewhere. + __autoreleasing RLMArrayHolder *copy = [[RLMArrayHolder alloc] init]; + copy->items = std::make_unique(self.count); + + NSUInteger i = 0; + for (id object in _backingArray) { + copy->items[i++] = object; + } + + state->itemsPtr = (__unsafe_unretained id *)(void *)copy->items.get(); + // needs to point to something valid, but the whole point of this is so + // that it can't be changed + state->mutationsPtr = state->extra; + state->state = i; + + return i; +} + +- (void)addObjectsFromArray:(NSArray *)array { + for (id obj in array) { + RLMValidateMatchingObjectType(self, obj); + } + changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingArray.count, array.count), ^{ + [_backingArray addObjectsFromArray:array]; + }); +} + +- (void)insertObject:(RLMObject *)anObject atIndex:(NSUInteger)index { + RLMValidateMatchingObjectType(self, anObject); + RLMValidateArrayBounds(self, index, true); + changeArray(self, NSKeyValueChangeInsertion, index, ^{ + [_backingArray insertObject:anObject atIndex:index]; + }); +} + +- (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ + NSUInteger currentIndex = [indexes firstIndex]; + for (RLMObject *obj in objects) { + RLMValidateMatchingObjectType(self, obj); + [_backingArray insertObject:obj atIndex:currentIndex]; + currentIndex = [indexes indexGreaterThanIndex:currentIndex]; + } + }); +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + RLMValidateArrayBounds(self, index); + changeArray(self, NSKeyValueChangeRemoval, index, ^{ + [_backingArray removeObjectAtIndex:index]; + }); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeRemoval, indexes, ^{ + [_backingArray removeObjectsAtIndexes:indexes]; + }); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + RLMValidateMatchingObjectType(self, anObject); + RLMValidateArrayBounds(self, index); + changeArray(self, NSKeyValueChangeReplacement, index, ^{ + [_backingArray replaceObjectAtIndex:index withObject:anObject]; + }); +} + +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex { + RLMValidateArrayBounds(self, sourceIndex); + RLMValidateArrayBounds(self, destinationIndex); + RLMObjectBase *original = _backingArray[sourceIndex]; + + auto start = std::min(sourceIndex, destinationIndex); + auto len = std::max(sourceIndex, destinationIndex) - start + 1; + changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{ + [_backingArray removeObjectAtIndex:sourceIndex]; + [_backingArray insertObject:original atIndex:destinationIndex]; + }); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 { + RLMValidateArrayBounds(self, index1); + RLMValidateArrayBounds(self, index2); + + changeArray(self, NSKeyValueChangeReplacement, ^{ + [_backingArray exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; + }, [=] { + NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1]; + [set addIndex:index2]; + return set; + }); +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + RLMValidateMatchingObjectType(self, object); + NSUInteger index = 0; + for (RLMObject *cmp in _backingArray) { + if (RLMObjectBaseAreEqual(object, cmp)) { + return index; + } + index++; + } + return NSNotFound; +} + +- (void)removeAllObjects { + changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, _backingArray.count), ^{ + [_backingArray removeAllObjects]; + }); +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... +{ + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args +{ + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if (!_backingArray) { + return [super valueForKeyPath:keyPath]; + } + // Although delegating to valueForKeyPath: here would allow to support + // nested key paths as well, limiting functionality gives consistency + // between unmanaged and managed arrays. + if ([keyPath characterAtIndex:0] == '@') { + NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; + if (operatorRange.location != NSNotFound) { + NSString *operatorKeyPath = [keyPath substringFromIndex:operatorRange.location + 1]; + if ([operatorKeyPath rangeOfString:@"."].location != NSNotFound) { + @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); + } + } + } + return [_backingArray valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + if ([key isEqualToString:RLMInvalidatedKey]) { + return @NO; // Unmanaged arrays are never invalidated + } + if (!_backingArray) { + return @[]; + } + return [_backingArray valueForKey:key]; +} + +- (void)setValue:(id)value forKey:(NSString *)key { + [_backingArray setValue:value forKey:key]; +} + +- (void)validateAggregateProperty:(NSString *)propertyName + method:(SEL)aggregateMethod + allowDate:(bool)allowDate { + RLMObjectSchema *objectSchema; + if (_backingArray.count) { + objectSchema = [_backingArray[0] objectSchema]; + } + else { + objectSchema = [RLMSchema.partialSharedSchema schemaForClassName:_objectClassName]; + } + + RLMProperty *prop = RLMValidatedProperty(objectSchema, propertyName); + switch (prop.type) { + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + break; + case RLMPropertyTypeDate: + if (allowDate) { + break; + } + [[clang::fallthrough]]; + default: + @throw RLMException(@"%@ is not supported for %@ property '%@.%@'", + NSStringFromSelector(aggregateMethod), + RLMTypeToString(prop.type), _objectClassName, propertyName); + } +} + +- (id)minOfProperty:(NSString *)property { + [self validateAggregateProperty:property method:_cmd allowDate:true]; + return [_backingArray valueForKeyPath:[@"@min." stringByAppendingString:property]]; +} + +- (id)maxOfProperty:(NSString *)property { + [self validateAggregateProperty:property method:_cmd allowDate:true]; + return [_backingArray valueForKeyPath:[@"@max." stringByAppendingString:property]]; +} + +- (id)sumOfProperty:(NSString *)property { + [self validateAggregateProperty:property method:_cmd allowDate:false]; + return [_backingArray valueForKeyPath:[@"@sum." stringByAppendingString:property]]; +} + +- (id)averageOfProperty:(NSString *)property { + [self validateAggregateProperty:property method:_cmd allowDate:false]; + return [_backingArray valueForKeyPath:[@"@avg." stringByAppendingString:property]]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + if (!_backingArray) { + return NSNotFound; + } + return [_backingArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger, BOOL *) { + return [predicate evaluateWithObject:obj]; + }]; +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + if (!_backingArray) { + _backingArray = [NSMutableArray new]; + } + return [_backingArray objectsAtIndexes:indexes]; +} + +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { + RLMValidateArrayObservationKey(keyPath, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +// +// Methods unsupported on unmanaged RLMArray instances +// + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate +{ + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} + +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending +{ + return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]]; +} + +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending +{ + return [self sortedResultsUsingKeyPath:property ascending:ascending]; +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties +{ + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} +#pragma clang diagnostic pop + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... +{ + va_list args; + va_start(args, predicateFormat); + NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; + va_end(args); + return index; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args +{ + return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat + arguments:args]]; +} + +#pragma mark - Superclass Overrides + +- (NSString *)description { + return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; +} + +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { + return RLMDescriptionWithMaxDepth(@"RLMArray", self, depth); +} + +#pragma mark - Thread Confined Protocol Conformance + +- (std::unique_ptr)makeThreadSafeReference { + REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`"); +} + +- (id)objectiveCMetadata { + REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`"); +} + ++ (instancetype)objectWithThreadSafeReference:(__unused std::unique_ptr)reference + metadata:(__unused id)metadata + realm:(__unused RLMRealm *)realm { + REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`"); +} + +@end + +@implementation RLMSortDescriptor + ++ (instancetype)sortDescriptorWithKeyPath:(NSString *)keyPath ascending:(BOOL)ascending { + RLMSortDescriptor *desc = [[RLMSortDescriptor alloc] init]; + desc->_keyPath = keyPath; + desc->_ascending = ascending; + return desc; +} + ++ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending { + return [RLMSortDescriptor sortDescriptorWithKeyPath:propertyName ascending:ascending]; +} + +- (instancetype)reversedSortDescriptor { + return [self.class sortDescriptorWithKeyPath:_keyPath ascending:!_ascending]; +} + +- (NSString *)property { + return _keyPath; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMArrayLinkView.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMArrayLinkView.mm new file mode 100644 index 0000000..e19064c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMArrayLinkView.mm @@ -0,0 +1,518 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMArray_Private.hpp" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema.h" +#import "RLMThreadSafeReference_Private.hpp" +#import "RLMUtil.hpp" + +#import "list.hpp" +#import "results.hpp" + +#import +#import + +@interface RLMArrayLinkViewHandoverMetadata : NSObject +@property (nonatomic) NSString *parentClassName; +@property (nonatomic) NSString *key; +@end + +@implementation RLMArrayLinkViewHandoverMetadata +@end + +@interface RLMArrayLinkView () +@end + +// +// RLMArray implementation +// +@implementation RLMArrayLinkView { +@public + realm::List _backingList; + RLMRealm *_realm; + RLMClassInfo *_objectInfo; + RLMClassInfo *_ownerInfo; + std::unique_ptr _observationInfo; +} + +- (RLMArrayLinkView *)initWithList:(realm::List)list + realm:(__unsafe_unretained RLMRealm *const)realm + parentInfo:(RLMClassInfo *)parentInfo + property:(__unsafe_unretained RLMProperty *const)property { + self = [self initWithObjectClassName:property.objectClassName]; + if (self) { + _realm = realm; + REALM_ASSERT(list.get_realm() == realm->_realm); + _backingList = std::move(list); + _objectInfo = &parentInfo->linkTargetType(property.index); + _ownerInfo = parentInfo; + _key = property.name; + } + return self; +} + +- (RLMArrayLinkView *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject + property:(__unsafe_unretained RLMProperty *const)property { + __unsafe_unretained RLMRealm *const realm = parentObject->_realm; + realm::List list(realm->_realm, parentObject->_row.get_linklist(parentObject->_info->tableColumn(property))); + return [self initWithList:std::move(list) + realm:realm + parentInfo:parentObject->_info + property:property]; +} + +void RLMValidateArrayObservationKey(__unsafe_unretained NSString *const keyPath, + __unsafe_unretained RLMArray *const array) { + if (![keyPath isEqualToString:RLMInvalidatedKey]) { + @throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@", + [array class], array, keyPath); + } +} + +void RLMEnsureArrayObservationInfo(std::unique_ptr& info, + __unsafe_unretained NSString *const keyPath, + __unsafe_unretained RLMArray *const array, + __unsafe_unretained id const observed) { + RLMValidateArrayObservationKey(keyPath, array); + if (!info && array.class == [RLMArrayLinkView class]) { + RLMArrayLinkView *lv = static_cast(array); + info = std::make_unique(*lv->_ownerInfo, + lv->_backingList.get_origin_row_index(), + observed); + } +} + +// +// validation helpers +// +[[gnu::noinline]] +[[noreturn]] +static void throwError(NSString *aggregateMethod) { + try { + throw; + } + catch (realm::InvalidTransactionException const&) { + @throw RLMException(@"Cannot modify managed RLMArray outside of a write transaction"); + } + catch (realm::IncorrectThreadException const&) { + @throw RLMException(@"Realm accessed from incorrect thread"); + } + catch (realm::List::InvalidatedException const&) { + @throw RLMException(@"RLMArray has been invalidated or the containing object has been deleted"); + } + catch (realm::List::OutOfBoundsIndexException const& e) { + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + e.requested, e.valid_count); + } + catch (realm::Results::UnsupportedColumnTypeException const& e) { + @throw RLMException(@"%@ is not supported for %@ property '%s'", + aggregateMethod, + RLMTypeToString((RLMPropertyType)e.column_type), + e.column_name.data()); + } +} + +template +static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) { + try { + return f(); + } + catch (...) { + throwError(aggregateMethod); + } +} + +static void validateObjectToAdd(__unsafe_unretained RLMArrayLinkView *const ar, + __unsafe_unretained RLMObject *const obj) { + if (!obj) { + @throw RLMException(@"Cannot add `nil` to RLMArray<%@>", ar->_objectClassName); + } + + NSString *objectClassName = obj->_objectSchema.className; + if (![objectClassName isEqualToString:ar->_objectClassName]) { + @throw RLMException(@"Cannot add object of type '%@' to RLMArray<%@>", + objectClassName, ar->_objectClassName); + } + + if (obj->_realm != ar.realm) { + [ar.realm addObject:obj]; + } + else if (obj->_realm && !obj->_row.is_attached()) { + @throw RLMException(@"Object has been deleted or invalidated."); + } +} + +template +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, + NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { + translateErrors([&] { ar->_backingList.verify_in_transaction(); }); + RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(), + ar->_backingList.get_origin_row_index(), + *ar->_ownerInfo); + if (info) { + NSIndexSet *indexes = is(); + info->willChange(ar->_key, kind, indexes); + try { + f(); + } + catch (...) { + info->didChange(ar->_key, kind, indexes); + throwError(nil); + } + info->didChange(ar->_key, kind, indexes); + } + else { + translateErrors([&] { f(); }); + } +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return is; }); +} + +// +// public method implementations +// +- (RLMRealm *)realm { + return _realm; +} + +- (NSUInteger)count { + return translateErrors([&] { return _backingList.size(); }); +} + +- (BOOL)isInvalidated { + return translateErrors([&] { return !_backingList.is_valid(); }); +} + +- (RLMClassInfo *)objectInfo { + return _objectInfo; +} + +- (BOOL)isEqual:(id)object { + if (RLMArrayLinkView *linkView = RLMDynamicCast(object)) { + return linkView->_backingList == _backingList; + } + return NO; +} + +- (NSUInteger)hash { + return std::hash()(_backingList); +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unused __unsafe_unretained id [])buffer + count:(NSUInteger)len { + __autoreleasing RLMFastEnumerator *enumerator; + if (state->state == 0) { + translateErrors([&] { _backingList.verify_attached(); }); + + enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_objectInfo]; + state->extra[0] = (long)enumerator; + state->extra[1] = self.count; + } + else { + enumerator = (__bridge id)(void *)state->extra[0]; + } + + return [enumerator countByEnumeratingWithState:state count:len]; +} + +- (id)objectAtIndex:(NSUInteger)index { + return RLMCreateObjectAccessor(_realm, *_objectInfo, + translateErrors([&] { return _backingList.get(index).get_index(); })); +} + +static void RLMInsertObject(RLMArrayLinkView *ar, RLMObject *object, NSUInteger index) { + if (index == NSUIntegerMax) { + index = translateErrors([&] { return ar->_backingList.size(); }); + } + + validateObjectToAdd(ar, object); + changeArray(ar, NSKeyValueChangeInsertion, index, ^{ + ar->_backingList.insert(index, object->_row.get_index()); + }); +} + +- (void)addObject:(RLMObject *)object { + RLMInsertObject(self, object, NSUIntegerMax); +} + +- (void)insertObject:(RLMObject *)object atIndex:(NSUInteger)index { + RLMInsertObject(self, object, index); +} + +- (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ + NSUInteger index = [indexes firstIndex]; + for (RLMObject *obj in objects) { + validateObjectToAdd(self, obj); + _backingList.insert(index, obj->_row.get_index()); + index = [indexes indexGreaterThanIndex:index]; + } + }); +} + + +- (void)removeObjectAtIndex:(NSUInteger)index { + changeArray(self, NSKeyValueChangeRemoval, index, ^{ + _backingList.remove(index); + }); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeRemoval, indexes, ^{ + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *) { + _backingList.remove(idx); + }]; + }); +} + +- (void)addObjectsFromArray:(NSArray *)array { + changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{ + for (RLMObject *obj in array) { + validateObjectToAdd(self, obj); + _backingList.add(obj->_row.get_index()); + } + }); +} + +- (void)removeAllObjects { + changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, self.count), ^{ + _backingList.remove_all(); + }); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObject *)object { + validateObjectToAdd(self, object); + changeArray(self, NSKeyValueChangeReplacement, index, ^{ + _backingList.set(index, object->_row.get_index()); + }); +} + +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex { + auto start = std::min(sourceIndex, destinationIndex); + auto len = std::max(sourceIndex, destinationIndex) - start + 1; + changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{ + _backingList.move(sourceIndex, destinationIndex); + }); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 { + changeArray(self, NSKeyValueChangeReplacement, ^{ + _backingList.swap(index1, index2); + }, [=] { + NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1]; + [set addIndex:index2]; + return set; + }); +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + if (object.invalidated) { + @throw RLMException(@"Object has been deleted or invalidated"); + } + + // check that object types align + if (![_objectClassName isEqualToString:object->_objectSchema.className]) { + @throw RLMException(@"Object of type (%@) does not match RLMArray type (%@)", + object->_objectSchema.className, _objectClassName); + } + + return translateErrors([&] { return RLMConvertNotFound(_backingList.find(object->_row)); }); +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if ([keyPath hasPrefix:@"@"]) { + // Delegate KVC collection operators to RLMResults + auto query = translateErrors([&] { return _backingList.get_query(); }); + RLMResults *results = [RLMResults resultsWithObjectInfo:*_objectInfo + results:realm::Results(_realm->_realm, std::move(query))]; + return [results valueForKeyPath:keyPath]; + } + return [super valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + // Ideally we'd use "@invalidated" for this so that "invalidated" would use + // normal array KVC semantics, but observing @things works very oddly (when + // it's part of a key path, it's triggered automatically when array index + // changes occur, and can't be sent explicitly, but works normally when it's + // the entire key path), and an RLMArrayLinkView *can't* have objects where + // invalidated is true, so we're not losing much. + if ([key isEqualToString:RLMInvalidatedKey]) { + return @(!_backingList.is_valid()); + } + + translateErrors([&] { _backingList.verify_attached(); }); + return RLMCollectionValueForKey(self, key); +} + +- (void)setValue:(id)value forKey:(NSString *)key { + translateErrors([&] { _backingList.verify_in_transaction(); }); + RLMCollectionSetValueForKey(self, key, value); +} + +- (id)aggregate:(NSString *)property + method:(realm::util::Optional (realm::List::*)(size_t))method + methodName:(NSString *)methodName { + size_t column = _objectInfo->tableColumn(property); + auto value = translateErrors([&] { return (_backingList.*method)(column); }, methodName); + if (!value) { + return nil; + } + return RLMMixedToObjc(*value); +} + +- (id)minOfProperty:(NSString *)property { + return [self aggregate:property method:&realm::List::min methodName:@"minOfProperty"]; +} + +- (id)maxOfProperty:(NSString *)property { + return [self aggregate:property method:&realm::List::max methodName:@"maxOfProperty"]; +} + +- (id)sumOfProperty:(NSString *)property { + return [self aggregate:property method:&realm::List::sum methodName:@"sumOfProperty"]; +} + +- (id)averageOfProperty:(NSString *)property { + return [self aggregate:property method:&realm::List::average methodName:@"averageOfProperty"]; +} + +- (void)deleteObjectsFromRealm { + // delete all target rows from the realm + RLMTrackDeletions(_realm, ^{ + translateErrors([&] { _backingList.delete_all(); }); + }); +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { + if (properties.count == 0) { + auto results = translateErrors([&] { return _backingList.filter({}); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; + } + + auto order = RLMSortDescriptorFromDescriptors(*_objectInfo, properties); + auto results = translateErrors([&] { return _backingList.sort(std::move(order)); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; +} + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group); + auto results = translateErrors([&] { return _backingList.filter(std::move(query)); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + auto query = translateErrors([&] { return _backingList.get_query(); }); + query.and_query(RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group)); +#if REALM_VER_MAJOR >= 2 + auto indexInTable = query.find(); + if (indexInTable == realm::not_found) { + return NSNotFound; + } + auto row = query.get_table()->get(indexInTable); + return _backingList.find(row); +#else + return RLMConvertNotFound(query.find()); +#endif +} + +- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes { + // FIXME: this is called by KVO when array changes are made. It's not clear + // why, and returning nil seems to work fine. + return nil; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + RLMEnsureArrayObservationInfo(_observationInfo, keyPath, self, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +- (NSUInteger)indexInSource:(NSUInteger)index { + return _backingList.get_unchecked(index); +} + +- (realm::TableView)tableView { + return translateErrors([&] { return _backingList.get_query(); }).find_all(); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { + [_realm verifyNotificationsAreSupported]; + return RLMAddNotificationBlock(self, _backingList, block); +} +#pragma clang diagnostic pop + +#pragma mark - Thread Confined Protocol Conformance + +- (std::unique_ptr)makeThreadSafeReference { + realm::ThreadSafeReference list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList); + return std::make_unique>(std::move(list_reference)); +} + +- (RLMArrayLinkViewHandoverMetadata *)objectiveCMetadata { + RLMArrayLinkViewHandoverMetadata *metadata = [[RLMArrayLinkViewHandoverMetadata alloc] init]; + metadata.parentClassName = _ownerInfo->rlmObjectSchema.className; + metadata.key = _key; + return metadata; +} + ++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference + metadata:(RLMArrayLinkViewHandoverMetadata *)metadata + realm:(RLMRealm *)realm { + REALM_ASSERT_DEBUG(dynamic_cast *>(reference.get())); + auto list_reference = static_cast *>(reference.get()); + + realm::List list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference)); + if (!list.is_valid()) { + return nil; + } + RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName]; + return [[RLMArrayLinkView alloc] initWithList:std::move(list) + realm:realm + parentInfo:parentInfo + property:parentInfo->rlmObjectSchema[metadata.key]]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMAuthResponseModel.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAuthResponseModel.m new file mode 100644 index 0000000..2eb5b7c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMAuthResponseModel.m @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMAuthResponseModel.h" + +#import "RLMTokenModels.h" +#import "RLMSyncUtil_Private.h" + +static const NSString *const kRLMSyncAccessTokenKey = @"access_token"; +static const NSString *const kRLMSyncRefreshTokenKey = @"refresh_token"; + +@interface RLMAuthResponseModel () + +@property (nonatomic, readwrite) RLMTokenModel *accessToken; +@property (nonatomic, readwrite) RLMTokenModel *refreshToken; + +@end + +@implementation RLMAuthResponseModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken { + if (self = [super init]) { + // Get the access token. + if (requireAccessToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } + // Get the refresh token. + if (requireRefreshToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } + return self; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMClassInfo.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMClassInfo.mm new file mode 100644 index 0000000..ca0ce84 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMClassInfo.mm @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMClassInfo.hpp" + +#import "RLMRealm_Private.hpp" +#import "RLMObjectSchema_Private.h" +#import "RLMSchema.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMUtil.hpp" + +#import "object_schema.hpp" +#import "object_store.hpp" +#import "schema.hpp" +#import "shared_realm.hpp" + +#import + +using namespace realm; + +RLMClassInfo::RLMClassInfo(RLMRealm *realm, RLMObjectSchema *rlmObjectSchema, + const realm::ObjectSchema *objectSchema) +: realm(realm), rlmObjectSchema(rlmObjectSchema), objectSchema(objectSchema) { } + +realm::Table *RLMClassInfo::table() const { + if (!m_table) { + m_table = ObjectStore::table_for_object_type(realm.group, objectSchema->name).get(); + } + return m_table; +} + +RLMProperty *RLMClassInfo::propertyForTableColumn(NSUInteger col) const noexcept { + auto const& props = objectSchema->persisted_properties; + for (size_t i = 0; i < props.size(); ++i) { + if (props[i].table_column == col) { + return rlmObjectSchema.properties[i]; + } + } + return nil; +} + +RLMProperty *RLMClassInfo::propertyForPrimaryKey() const noexcept { + return rlmObjectSchema.primaryKeyProperty; +} + +NSUInteger RLMClassInfo::tableColumn(NSString *propertyName) const { + return tableColumn(RLMValidatedProperty(rlmObjectSchema, propertyName)); +} + +NSUInteger RLMClassInfo::tableColumn(RLMProperty *property) const { + return objectSchema->persisted_properties[property.index].table_column; +} + +RLMClassInfo &RLMClassInfo::linkTargetType(size_t propertyIndex) { + if (propertyIndex < m_linkTargets.size() && m_linkTargets[propertyIndex]) { + return *m_linkTargets[propertyIndex]; + } + if (m_linkTargets.size() <= propertyIndex) { + m_linkTargets.resize(propertyIndex + 1); + } + m_linkTargets[propertyIndex] = &realm->_info[rlmObjectSchema.properties[propertyIndex].objectClassName]; + return *m_linkTargets[propertyIndex]; +} + +RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); } +RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); } +RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); } +RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::end() const noexcept { return m_objects.end(); } + +RLMClassInfo& RLMSchemaInfo::operator[](NSString *name) { + auto it = m_objects.find(name); + if (it == m_objects.end()) { + @throw RLMException(@"Object type '%@' is not managed by the Realm. " + @"If using a custom `objectClasses` / `objectTypes` array in your configuration, " + @"add `%@` to the list of `objectClasses` / `objectTypes`.", + name, name); + } + return *&it->second; +} + +RLMSchemaInfo::RLMSchemaInfo(RLMRealm *realm) { + RLMSchema *rlmSchema = realm.schema; + realm::Schema const& schema = realm->_realm->schema(); + REALM_ASSERT(rlmSchema.objectSchema.count == schema.size()); + + m_objects.reserve(schema.size()); + for (RLMObjectSchema *rlmObjectSchema in rlmSchema.objectSchema) { + m_objects.emplace(std::piecewise_construct, + std::forward_as_tuple(rlmObjectSchema.className), + std::forward_as_tuple(realm, rlmObjectSchema, + &*schema.find(rlmObjectSchema.objectName.UTF8String))); + } +} + +RLMSchemaInfo RLMSchemaInfo::clone(realm::Schema const& source_schema, + __unsafe_unretained RLMRealm *const target_realm) { + RLMSchemaInfo info; + info.m_objects.reserve(m_objects.size()); + + auto& schema = target_realm->_realm->schema(); + for (auto& pair : m_objects) { + size_t idx = pair.second.objectSchema - &*source_schema.begin(); + info.m_objects.emplace(std::piecewise_construct, + std::forward_as_tuple(pair.first), + std::forward_as_tuple(target_realm, pair.second.rlmObjectSchema, + &*schema.begin() + idx)); + } + return info; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMCollection.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMCollection.mm new file mode 100644 index 0000000..c893549 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMCollection.mm @@ -0,0 +1,367 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMCollection_Private.hpp" + +#import "RLMArray_Private.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" + +#import "collection_notifications.hpp" +#import "list.hpp" +#import "results.hpp" + +#import + +static const int RLMEnumerationBufferSize = 16; + +@implementation RLMFastEnumerator { + // The buffer supplied by fast enumeration does not retain the objects given + // to it, but because we create objects on-demand and don't want them + // autoreleased (a table can have more rows than the device has memory for + // accessor objects) we need a thing to retain them. + id _strongBuffer[RLMEnumerationBufferSize]; + + RLMRealm *_realm; + RLMClassInfo *_info; + + // Collection being enumerated. Only one of these two will be valid: when + // possible we enumerate the collection directly, but when in a write + // transaction we instead create a frozen TableView and enumerate that + // instead so that mutating the collection during enumeration works. + id _collection; + realm::TableView _tableView; +} + +- (instancetype)initWithCollection:(id)collection objectSchema:(RLMClassInfo&)info { + self = [super init]; + if (self) { + _realm = collection.realm; + _info = &info; + + if (_realm.inWriteTransaction) { + _tableView = [collection tableView]; + } + else { + _collection = collection; + [_realm registerEnumerator:self]; + } + } + return self; +} + +- (void)dealloc { + if (_collection) { + [_realm unregisterEnumerator:self]; + } +} + +- (void)detach { + _tableView = [_collection tableView]; + _collection = nil; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + count:(NSUInteger)len { + [_realm verifyThread]; + if (!_tableView.is_attached() && !_collection) { + @throw RLMException(@"Collection is no longer valid"); + } + // The fast enumeration buffer size is currently a hardcoded number in the + // compiler so this can't actually happen, but just in case it changes in + // the future... + if (len > RLMEnumerationBufferSize) { + len = RLMEnumerationBufferSize; + } + + NSUInteger batchCount = 0, count = state->extra[1]; + + Class accessorClass = _info->rlmObjectSchema.accessorClass; + for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { + RLMObject *accessor = RLMCreateManagedAccessor(accessorClass, _realm, _info); + if (_collection) { + accessor->_row = (*_info->table())[[_collection indexInSource:index]]; + } + else if (_tableView.is_row_attached(index)) { + accessor->_row = (*_info->table())[_tableView.get_source_ndx(index)]; + } + RLMInitializeSwiftAccessorGenerics(accessor); + _strongBuffer[batchCount] = accessor; + batchCount++; + } + + for (NSUInteger i = batchCount; i < len; ++i) { + _strongBuffer[i] = nil; + } + + if (batchCount == 0) { + // Release our data if we're done, as we're autoreleased and so may + // stick around for a while + _collection = nil; + if (_tableView.is_attached()) { + _tableView = {}; + } + else { + [_realm unregisterEnumerator:self]; + } + } + + state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer; + state->state += batchCount; + state->mutationsPtr = state->extra+1; + + return batchCount; +} +@end + + +NSArray *RLMCollectionValueForKey(id collection, NSString *key) { + size_t count = collection.count; + if (count == 0) { + return @[]; + } + + RLMRealm *realm = collection.realm; + RLMClassInfo *info = collection.objectInfo; + + NSMutableArray *results = [NSMutableArray arrayWithCapacity:count]; + if ([key isEqualToString:@"self"]) { + for (size_t i = 0; i < count; i++) { + size_t rowIndex = [collection indexInSource:i]; + [results addObject:RLMCreateObjectAccessor(realm, *info, rowIndex) ?: NSNull.null]; + } + return results; + } + + for (RLMProperty *prop in info->rlmObjectSchema.swiftGenericProperties) { + if ([prop.name isEqual:key] && !prop.optional) { + for (size_t i = 0; i < count; i++) { + size_t rowIndex = [collection indexInSource:i]; + RLMObjectBase *accessor = RLMCreateObjectAccessor(realm, *info, rowIndex); + [results addObject:[accessor valueForKey:key] ?: NSNull.null]; + } + return results; + } + } + + RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info); + realm::Table *table = info->table(); + for (size_t i = 0; i < count; i++) { + size_t rowIndex = [collection indexInSource:i]; + accessor->_row = (*table)[rowIndex]; + RLMInitializeSwiftAccessorGenerics(accessor); + [results addObject:[accessor valueForKey:key] ?: NSNull.null]; + } + + return results; +} + +void RLMCollectionSetValueForKey(id collection, NSString *key, id value) { + realm::TableView tv = [collection tableView]; + if (tv.size() == 0) { + return; + } + + RLMRealm *realm = collection.realm; + RLMClassInfo *info = collection.objectInfo; + RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info); + for (size_t i = 0; i < tv.size(); i++) { + accessor->_row = tv[i]; + RLMInitializeSwiftAccessorGenerics(accessor); + [accessor setValue:value forKey:key]; + } +} + +NSString *RLMDescriptionWithMaxDepth(NSString *name, + id collection, + NSUInteger depth) { + if (depth == 0) { + return @""; + } + + const NSUInteger maxObjects = 100; + auto str = [NSMutableString stringWithFormat:@"%@ <%p> (\n", name, (void *)collection]; + size_t index = 0, skipped = 0; + for (id obj in collection) { + NSString *sub; + if ([obj respondsToSelector:@selector(descriptionWithMaxDepth:)]) { + sub = [obj descriptionWithMaxDepth:depth - 1]; + } + else { + sub = [obj description]; + } + + // Indent child objects + NSString *objDescription = [sub stringByReplacingOccurrencesOfString:@"\n" + withString:@"\n\t"]; + [str appendFormat:@"\t[%zu] %@,\n", index++, objDescription]; + if (index >= maxObjects) { + skipped = collection.count - maxObjects; + break; + } + } + + // Remove last comma and newline characters + if (collection.count > 0) { + [str deleteCharactersInRange:NSMakeRange(str.length-2, 2)]; + } + if (skipped) { + [str appendFormat:@"\n\t... %zu objects skipped.", skipped]; + } + [str appendFormat:@"\n)"]; + return str; +} + +@implementation RLMCancellationToken { + realm::NotificationToken _token; + __unsafe_unretained RLMRealm *_realm; +} +- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm { + self = [super init]; + if (self) { + _token = std::move(token); + _realm = realm; + } + return self; +} + +- (RLMRealm *)realm { + return _realm; +} + +- (void)suppressNextNotification { + _token.suppress_next(); +} + +- (void)stop { + _token = {}; +} + +@end + +@implementation RLMCollectionChange { + realm::CollectionChangeSet _indices; +} + +- (instancetype)initWithChanges:(realm::CollectionChangeSet)indices { + self = [super init]; + if (self) { + _indices = std::move(indices); + } + return self; +} + +static NSArray *toArray(realm::IndexSet const& set) { + NSMutableArray *ret = [NSMutableArray new]; + for (auto index : set.as_indexes()) { + [ret addObject:@(index)]; + } + return ret; +} + +- (NSArray *)insertions { + return toArray(_indices.insertions); +} + +- (NSArray *)deletions { + return toArray(_indices.deletions); +} + +- (NSArray *)modifications { + return toArray(_indices.modifications); +} + +static NSArray *toIndexPathArray(realm::IndexSet const& set, NSUInteger section) { + NSMutableArray *ret = [NSMutableArray new]; + NSUInteger path[2] = {section, 0}; + for (auto index : set.as_indexes()) { + path[1] = index; + [ret addObject:[NSIndexPath indexPathWithIndexes:path length:2]]; + } + return ret; +} + +- (NSArray *)deletionsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.deletions, section); +} + +- (NSArray *)insertionsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.insertions, section); + +} + +- (NSArray *)modificationsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.modifications, section); + +} +@end + +template +RLMNotificationToken *RLMAddNotificationBlock(id objcCollection, + Collection& collection, + void (^block)(id, RLMCollectionChange *, NSError *), + bool suppressInitialChange) { + struct IsValid { + static bool call(realm::List const& list) { + return list.is_valid(); + } + static bool call(realm::Results const&) { + return true; + } + }; + + auto skip = suppressInitialChange ? std::make_shared(true) : nullptr; + auto cb = [=, &collection](realm::CollectionChangeSet const& changes, + std::exception_ptr err) { + if (err) { + try { + rethrow_exception(err); + } + catch (...) { + NSError *error = nil; + RLMRealmTranslateException(&error); + block(nil, nil, error); + return; + } + } + + if (!IsValid::call(collection)) { + return; + } + + if (skip && *skip) { + *skip = false; + block(objcCollection, nil, nil); + } + else if (changes.empty()) { + block(objcCollection, nil, nil); + } + else { + block(objcCollection, [[RLMCollectionChange alloc] initWithChanges:changes], nil); + } + }; + + return [[RLMCancellationToken alloc] initWithToken:collection.add_notification_callback(cb) + realm:(RLMRealm *)[objcCollection realm]]; +} + +// Explicitly instantiate the templated function for the two types we'll use it on +template RLMNotificationToken *RLMAddNotificationBlock(id, realm::List&, void (^)(id, RLMCollectionChange *, NSError *), bool); +template RLMNotificationToken *RLMAddNotificationBlock(id, realm::Results&, void (^)(id, RLMCollectionChange *, NSError *), bool); diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMConstants.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMConstants.m new file mode 100644 index 0000000..7136127 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMConstants.m @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +RLMNotification const RLMRealmRefreshRequiredNotification = @"RLMRealmRefreshRequiredNotification"; +RLMNotification const RLMRealmDidChangeNotification = @"RLMRealmDidChangeNotification"; + +NSString * const RLMErrorDomain = @"io.realm"; + +NSString * const RLMUnknownSystemErrorDomain = @"io.realm.unknown"; + +NSString * const RLMExceptionName = @"RLMException"; + +NSString * const RLMRealmVersionKey = @"RLMRealmVersion"; + +NSString * const RLMRealmCoreVersionKey = @"RLMRealmCoreVersion"; + +NSString * const RLMInvalidatedKey = @"invalidated"; diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMListBase.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMListBase.mm new file mode 100644 index 0000000..78cf91a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMListBase.mm @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMListBase.h" + +#import "RLMArray_Private.hpp" +#import "RLMObservation.hpp" + +@interface RLMArray (KVO) +- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes; +@end + +@implementation RLMListBase { + std::unique_ptr _observationInfo; +} + +- (instancetype)initWithArray:(RLMArray *)array { + self = [super init]; + if (self) { + __rlmArray = array; + } + return self; +} + +- (id)valueForKey:(NSString *)key { + return [__rlmArray valueForKey:key]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { + return [__rlmArray countByEnumeratingWithState:state objects:buffer count:len]; +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + return [__rlmArray objectsAtIndexes:indexes]; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + RLMEnsureArrayObservationInfo(_observationInfo, keyPath, __rlmArray, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMMigration.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMMigration.mm new file mode 100644 index 0000000..6d8da01 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMMigration.mm @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMMigration_Private.h" + +#import "RLMAccessor.h" +#import "RLMObject_Private.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMProperty_Private.h" +#import "RLMRealm_Dynamic.h" +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.h" +#import "RLMSchema_Private.hpp" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "shared_realm.hpp" +#import "schema.hpp" + +#import + +using namespace realm; + +// The source realm for a migration has to use a SharedGroup to be able to share +// the file with the destination realm, but we don't want to let the user call +// beginWriteTransaction on it as that would make no sense. +@interface RLMMigrationRealm : RLMRealm +@end + +@implementation RLMMigrationRealm +- (BOOL)readonly { + return YES; +} + +- (void)beginWriteTransaction { + @throw RLMException(@"Cannot modify the source Realm in a migration"); +} +@end + +@implementation RLMMigration { + realm::Schema *_schema; +} + +- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema { + self = [super init]; + if (self) { + _realm = realm; + _oldRealm = oldRealm; + _schema = &schema; + object_setClass(_oldRealm, RLMMigrationRealm.class); + } + return self; +} + +- (RLMSchema *)oldSchema { + return self.oldRealm.schema; +} + +- (RLMSchema *)newSchema { + return self.realm.schema; +} + +- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block { + // get all objects + RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil; + RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil; + + if (objects && oldObjects) { + for (long i = oldObjects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(oldObjects[i], objects[i]); + } + } + } + else if (objects) { + for (long i = objects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(nil, objects[i]); + } + } + } + else if (oldObjects) { + for (long i = oldObjects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(oldObjects[i], nil); + } + } + } +} + +- (void)execute:(RLMMigrationBlock)block { + @autoreleasepool { + // disable all primary keys for migration and use DynamicObject for all types + for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) { + objectSchema.accessorClass = RLMDynamicObject.class; + objectSchema.primaryKeyProperty.isPrimary = NO; + } + for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) { + objectSchema.accessorClass = RLMDynamicObject.class; + } + + block(self, _oldRealm->_realm->schema_version()); + + _oldRealm = nil; + _realm = nil; + } +} + +- (RLMObject *)createObject:(NSString *)className withValue:(id)value { + return [_realm createObject:className withValue:value]; +} + +- (RLMObject *)createObject:(NSString *)className withObject:(id)object { + return [self createObject:className withValue:object]; +} + +- (void)deleteObject:(RLMObject *)object { + [_realm deleteObject:object]; +} + +- (BOOL)deleteDataForClassName:(NSString *)name { + if (!name) { + return false; + } + + TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String); + if (!table) { + return false; + } + + if ([_realm.schema schemaForClassName:name]) { + table->clear(); + } + else { + realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String); + } + + return true; +} + +- (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName { + const char *objectType = className.UTF8String; + realm::ObjectStore::rename_property(_realm.group, *_schema, objectType, oldName.UTF8String, newName.UTF8String); +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMNetworkClient.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMNetworkClient.m new file mode 100644 index 0000000..ca8a156 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMNetworkClient.m @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMNetworkClient.h" + +#import "RLMRealmConfiguration.h" +#import "RLMSyncErrorResponseModel.h" +#import "RLMSyncUtil_Private.h" + +typedef void(^RLMServerURLSessionCompletionBlock)(NSData *, NSURLResponse *, NSError *); + +static NSUInteger const kHTTPCodeRange = 100; + +typedef enum : NSUInteger { + Informational = 1, // 1XX + Success = 2, // 2XX + Redirection = 3, // 3XX + ClientError = 4, // 4XX + ServerError = 5, // 5XX +} RLMServerHTTPErrorCodeType; + +static NSRange RLM_rangeForErrorType(RLMServerHTTPErrorCodeType type) { + return NSMakeRange(type*100, kHTTPCodeRange); +} + +@implementation RLMNetworkClient + ++ (NSURLSession *)session { + return [NSURLSession sharedSession]; +} + ++ (NSURL *)urlForServer:(NSURL *)serverURL endpoint:(RLMServerEndpoint)endpoint { + NSString *pathComponent = nil; + switch (endpoint) { + case RLMServerEndpointAuth: + pathComponent = @"auth"; + break; + case RLMServerEndpointLogout: + // TODO: fix this + pathComponent = @"logout"; + NSAssert(NO, @"logout endpoint isn't implemented yet, don't use it"); + break; + case RLMServerEndpointAddCredentials: + // TODO: fix this + pathComponent = @"addCredentials"; + NSAssert(NO, @"add credentials endpoint isn't implemented yet, don't use it"); + break; + case RLMServerEndpointRemoveCredentials: + // TODO: fix this + pathComponent = @"removeCredentials"; + NSAssert(NO, @"remove credentials endpoint isn't implemented yet, don't use it"); + break; + } + NSAssert(pathComponent != nil, @"Unrecognized value for RLMServerEndpoint enum"); + return [serverURL URLByAppendingPathComponent:pathComponent]; +} + ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + completion:(RLMSyncCompletionBlock)completionBlock { + static NSTimeInterval const defaultTimeout = 60; + [self postRequestToEndpoint:endpoint + server:serverURL + JSON:jsonDictionary + timeout:defaultTimeout + completion:completionBlock]; +} + +// FIXME: should completion argument also pass back the NSURLResponse and/or the raw data? ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + timeout:(NSTimeInterval)timeout + completion:(RLMSyncCompletionBlock)completionBlock { + + NSError *localError = nil; + + // Attempt to convert the JSON + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary + options:(NSJSONWritingOptions)0 + error:&localError]; + if (!jsonData) { + completionBlock(localError, nil); + return; + } + + // Create the request + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self urlForServer:serverURL endpoint:endpoint]]; + request.HTTPBody = jsonData; + request.HTTPMethod = @"POST"; + request.timeoutInterval = MAX(timeout, 10); + [request addValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"]; + [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + RLMServerURLSessionCompletionBlock handler = ^(NSData *data, + NSURLResponse *response, + NSError *error) { + if (error != nil) { + // Network error + completionBlock(error, nil); + return; + } + + NSError *localError = nil; + + if (![self validateResponse:response data:data error:&localError]) { + // Response error + completionBlock(localError, nil); + return; + } + + // Parse out the JSON + id json = [NSJSONSerialization JSONObjectWithData:data + options:(NSJSONReadingOptions)0 + error:&localError]; + if (!json || localError) { + // JSON parsing error + completionBlock(localError, nil); + } else if (![json isKindOfClass:[NSDictionary class]]) { + // JSON response malformed + localError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + completionBlock(localError, nil); + } else { + // JSON parsed successfully + completionBlock(nil, (NSDictionary *)json); + } + }; + + // Add the request to a task and start it + NSURLSessionTask *task = [self.session dataTaskWithRequest:request + completionHandler:handler]; + [task resume]; +} + ++ (BOOL)validateResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error { + __autoreleasing NSError *localError = nil; + if (!error) { + error = &localError; + } + + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + // FIXME: Provide error message + *error = [NSError errorWithDomain:RLMSyncErrorDomain code:RLMSyncErrorBadResponse userInfo:nil]; + return NO; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + BOOL badResponse = (NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ClientError)) + || NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ServerError))); + if (badResponse) { + NSError *responseError = [self errorFromResponseData:data]; + if (responseError && responseError.userInfo[kRLMSyncErrorStatusCodeKey]) { + switch (responseError.code) { + case RLMSyncAuthErrorInvalidCredential: + case RLMSyncAuthErrorUserDoesNotExist: + case RLMSyncAuthErrorUserAlreadyExists: + // Authentication error + *error = responseError; + break; + + default: + // HTTP status error with some additional infor from the server + *error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorHTTPStatusCodeError + userInfo:responseError.userInfo]; + break; + } + } else { + // Fallback to HTTP status error without any additional info + *error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorHTTPStatusCodeError + userInfo:@{kRLMSyncErrorStatusCodeKey: @(httpResponse.statusCode)}]; + } + + return NO; + } + + if (!data) { + // FIXME: provide error message + *error = [NSError errorWithDomain:RLMSyncErrorDomain code:RLMSyncErrorBadResponse userInfo:nil]; + return NO; + } + + return YES; +} + ++ (NSError *)errorFromResponseData:(NSData *)data { + if (data.length == 0) { + return nil; + } + + id json = [NSJSONSerialization JSONObjectWithData:data + options:(NSJSONReadingOptions)0 + error:nil]; + if (!json || ![json isKindOfClass:[NSDictionary class]]) { + return nil; + } + + RLMSyncErrorResponseModel *responseModel = [[RLMSyncErrorResponseModel alloc] initWithDictionary:json]; + if (!responseModel) { + return nil; + } + + NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionaryWithObject:@(responseModel.status) forKey:kRLMSyncErrorStatusCodeKey]; + [mutableUserInfo setValue:responseModel.title forKey:NSLocalizedDescriptionKey]; + [mutableUserInfo setValue:responseModel.hint forKey:NSLocalizedRecoverySuggestionErrorKey]; + + return [NSError errorWithDomain:RLMSyncErrorDomain code:responseModel.code userInfo:mutableUserInfo]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMObject.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObject.mm new file mode 100644 index 0000000..da32804 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObject.mm @@ -0,0 +1,379 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObject_Private.hpp" + +#import "RLMAccessor.h" +#import "RLMArray.h" +#import "RLMCollection_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMProperty.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" + +#import "collection_notifications.hpp" +#import "object.hpp" + +@interface RLMPropertyChange () +@property (nonatomic, readwrite, strong) NSString *name; +@property (nonatomic, readwrite, strong, nullable) id previousValue; +@property (nonatomic, readwrite, strong, nullable) id value; +@end + +// We declare things in RLMObject which are actually implemented in RLMObjectBase +// for documentation's sake, which leads to -Wunimplemented-method warnings. +// Other alternatives to this would be to disable -Wunimplemented-method for this +// file (but then we could miss legitimately missing things), or declaring the +// inherited things in a category (but they currently aren't nicely grouped for +// that). +@implementation RLMObject + +// synthesized in RLMObjectBase +@dynamic invalidated, realm, objectSchema; + +#pragma mark - Designated Initializers + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { + return [super initWithValue:value schema:schema]; +} + +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema { + return [super initWithRealm:realm schema:schema]; +} + +#pragma mark - Convenience Initializers + +- (instancetype)initWithValue:(id)value { + [self.class sharedSchema]; // ensure this class' objectSchema is loaded in the partialSharedSchema + RLMSchema *schema = RLMSchema.partialSharedSchema; + return [super initWithValue:value schema:schema]; +} + +#pragma mark - Class-based Object Creation + ++ (instancetype)createInDefaultRealmWithValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, false); +} + ++ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, false); +} + ++ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value { + return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value]; +} + ++ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value { + // verify primary key + RLMObjectSchema *schema = [self sharedSchema]; + if (!schema.primaryKeyProperty) { + NSString *reason = [NSString stringWithFormat:@"'%@' does not have a primary key and can not be updated", schema.className]; + @throw [NSException exceptionWithName:@"RLMExecption" reason:reason userInfo:nil]; + } + return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true); +} + +#pragma mark - Subscripting + +- (id)objectForKeyedSubscript:(NSString *)key { + return RLMObjectBaseObjectForKeyedSubscript(self, key); +} + +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { + RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj); +} + +#pragma mark - Getting & Querying + ++ (RLMResults *)allObjects { + return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil); +} + ++ (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm { + return RLMGetObjects(realm, self.className, nil); +} + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args]; + va_end(args); + return results; +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args { + return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + ++ (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate); +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate { + return RLMGetObjects(realm, self.className, predicate); +} + ++ (instancetype)objectForPrimaryKey:(id)primaryKey { + return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey); +} + ++ (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey { + return RLMGetObject(realm, self.className, primaryKey); +} + +#pragma mark - Other Instance Methods + +- (BOOL)isEqualToObject:(RLMObject *)object { + return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object); +} + +- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block { + return RLMObjectAddNotificationBlock(self, ^(NSArray *propertyNames, + NSArray *oldValues, NSArray *newValues, NSError *error) { + if (error) { + block(false, nil, error); + } + else if (!propertyNames) { + block(true, nil, nil); + } + else { + auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count]; + for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) { + auto prop = [RLMPropertyChange new]; + prop.name = propertyNames[i]; + prop.previousValue = RLMCoerceToNil(oldValues[i]); + prop.value = RLMCoerceToNil(newValues[i]); + [properties addObject:prop]; + } + block(false, properties, nil); + } + }); +} + ++ (NSString *)className { + return [super className]; +} + +#pragma mark - Default values for schema definition + ++ (NSArray *)indexedProperties { + return @[]; +} + ++ (NSDictionary *)linkingObjectsProperties { + return @{}; +} + ++ (NSDictionary *)defaultPropertyValues { + return nil; +} + ++ (NSString *)primaryKey { + return nil; +} + ++ (NSArray *)ignoredProperties { + return nil; +} + ++ (NSArray *)requiredProperties { + return @[]; +} + +@end + +@implementation RLMDynamicObject + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + +- (id)valueForUndefinedKey:(NSString *)key { + return RLMDynamicGetByName(self, key, false); +} + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key { + RLMDynamicValidatedSet(self, key, value); +} + +@end + +@implementation RLMWeakObjectHandle { + realm::Row _row; + RLMClassInfo *_info; + Class _objectClass; +} + +- (instancetype)initWithObject:(RLMObjectBase *)object { + if (!(self = [super init])) { + return nil; + } + + _row = object->_row; + _info = object->_info; + _objectClass = object.class; + + return self; +} + +- (RLMObjectBase *)object { + RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info); + object->_row = std::move(_row); + return object; +} + +- (id)copyWithZone:(__unused NSZone *)zone { + RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init]; + copy->_row = _row; + copy->_info = _info; + copy->_objectClass = _objectClass; + return copy; +} + +@end + +@interface RLMObjectNotificationToken : RLMCancellationToken +@end +@implementation RLMObjectNotificationToken { +@public + realm::Object _object; +} +@end + +RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) { + if (!obj->_realm) { + @throw RLMException(@"Only objects which are managed by a Realm support change notifications"); + } + [obj->_realm verifyNotificationsAreSupported]; + + struct { + void (^block)(NSArray *, NSArray *, NSArray *, NSError *); + RLMObjectBase *object; + + NSArray *propertyNames = nil; + NSArray *oldValues = nil; + bool deleted = false; + + void populateProperties(realm::CollectionChangeSet const& c) { + if (propertyNames) { + return; + } + if (!c.deletions.empty()) { + deleted = true; + return; + } + if (c.columns.empty()) { + return; + } + + auto properties = [NSMutableArray new]; + for (size_t i = 0; i < c.columns.size(); ++i) { + if (c.columns[i].empty()) { + continue; + } + if (auto prop = object->_info->propertyForTableColumn(i)) { + [properties addObject:prop.name]; + } + } + if (properties.count) { + propertyNames = properties; + } + } + + NSArray *readValues(realm::CollectionChangeSet const& c) { + if (c.empty()) { + return nil; + } + populateProperties(c); + if (!propertyNames) { + return nil; + } + + auto values = [NSMutableArray arrayWithCapacity:propertyNames.count]; + for (NSString *name in propertyNames) { + id value = [object valueForKey:name]; + if (!value || [value isKindOfClass:[RLMArray class]]) { + [values addObject:NSNull.null]; + } + else { + [values addObject:value]; + } + } + return values; + } + + void before(realm::CollectionChangeSet const& c) { + @autoreleasepool { + oldValues = readValues(c); + } + } + + void after(realm::CollectionChangeSet const& c) { + @autoreleasepool { + auto newValues = readValues(c); + if (deleted) { + block(nil, nil, nil, nil); + } + else if (newValues) { + block(propertyNames, oldValues, newValues, nil); + } + propertyNames = nil; + oldValues = nil; + } + } + + void error(std::exception_ptr err) { + @autoreleasepool { + try { + rethrow_exception(err); + } + catch (...) { + NSError *error = nil; + RLMRealmTranslateException(&error); + block(nil, nil, nil, error); + } + } + } + } callback{block, obj}; + + realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row); + auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm]; + token->_object = std::move(object); + return token; +} + +@implementation RLMPropertyChange +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectBase.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectBase.mm new file mode 100644 index 0000000..5672403 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectBase.mm @@ -0,0 +1,489 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObject_Private.hpp" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObservation.hpp" +#import "RLMOptionalBase.h" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMThreadSafeReference_Private.hpp" +#import "RLMUtil.hpp" + +#import "object.hpp" + +using namespace realm; + +const NSUInteger RLMDescriptionMaxDepth = 5; + +static bool maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) { + obj->_objectSchema = [obj.class sharedSchema]; + if (!obj->_objectSchema) { + return false; + } + + // set default values + if (!obj->_objectSchema.isSwiftClass) { + NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema); + for (NSString *key in dict) { + [obj setValue:dict[key] forKey:key]; + } + } + + // set unmanaged accessor class + object_setClass(obj, obj->_objectSchema.unmanagedClass); + return true; +} + +@interface RLMObjectBase () +@end + +@implementation RLMObjectBase +// unmanaged init +- (instancetype)init { + if ((self = [super init])) { + maybeInitObjectSchemaForUnmanaged(self); + } + return self; +} + +- (void)dealloc { + // This can't be a unique_ptr because associated objects are removed + // *after* c++ members are destroyed and dealloc is called, and we need it + // to be in a validish state when that happens + delete _observationInfo; + _observationInfo = nullptr; +} + +static id validatedObjectForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained RLMSchema *const schema) { + RLMValidateValueForProperty(obj, prop); + + if (obj && prop.type == RLMPropertyTypeObject) { + RLMObjectSchema *objSchema = schema[prop.objectClassName]; + if ([obj isKindOfClass:objSchema.objectClass]) { + return obj; + } + else { + return [[objSchema.objectClass alloc] initWithValue:obj schema:schema]; + } + } + if (prop.type == RLMPropertyTypeArray) { + RLMObjectSchema *objSchema = schema[prop.objectClassName]; + RLMArray *objects = [[RLMArray alloc] initWithObjectClassName:objSchema.className]; + for (id el in obj) { + if ([el isKindOfClass:objSchema.objectClass]) { + [objects addObject:el]; + } + else { + [objects addObject:[[objSchema.objectClass alloc] initWithValue:el schema:schema]]; + } + } + return objects; + } + + return obj; +} + +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { + if (!(self = [super init])) { + return self; + } + + if (!value || value == NSNull.null) { + @throw RLMException(@"Must provide a non-nil value."); + } + + if (!maybeInitObjectSchemaForUnmanaged(self)) { + // Don't populate fields from the passed-in object if we're called + // during schema init + return self; + } + + NSArray *properties = _objectSchema.properties; + if (NSArray *array = RLMDynamicCast(value)) { + if (array.count > properties.count) { + @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", + (unsigned long long)array.count, (unsigned long long)properties.count); + } + NSUInteger i = 0; + for (id val in array) { + RLMProperty *prop = properties[i++]; + [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), prop, schema) + forKey:prop.name]; + } + } + else { + // assume our object is an NSDictionary or an object with kvc properties + for (RLMProperty *prop in properties) { + id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className); + + // don't set unspecified properties + if (!obj) { + continue; + } + + [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), prop, schema) + forKey:prop.name]; + } + } + + return self; +} + +id RLMCreateManagedAccessor(Class cls, __unsafe_unretained RLMRealm *realm, RLMClassInfo *info) { + RLMObjectBase *obj = [[cls alloc] initWithRealm:realm schema:info->rlmObjectSchema]; + obj->_info = info; + return obj; +} + +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema { + self = [super init]; + if (self) { + _realm = realm; + _objectSchema = schema; + } + return self; +} + +- (id)valueForKey:(NSString *)key { + if (_observationInfo) { + return _observationInfo->valueForKey(key); + } + return [super valueForKey:key]; +} + +// Generic Swift properties can't be dynamic, so KVO doesn't work for them by default +- (id)valueForUndefinedKey:(NSString *)key { + if (Ivar ivar = _objectSchema[key].swiftIvar) { + return RLMCoerceToNil(object_getIvar(self, ivar)); + } + return [super valueForUndefinedKey:key]; +} + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key { + RLMProperty *property = _objectSchema[key]; + if (Ivar ivar = property.swiftIvar) { + if (property.type == RLMPropertyTypeArray && [value conformsToProtocol:@protocol(NSFastEnumeration)]) { + RLMArray *array = [object_getIvar(self, ivar) _rlmArray]; + [array removeAllObjects]; + [array addObjects:validatedObjectForProperty(value, property, RLMSchema.partialSharedSchema)]; + } + else if (property.optional) { + RLMOptionalBase *optional = object_getIvar(self, ivar); + optional.underlyingValue = value; + } + return; + } + [super setValue:value forUndefinedKey:key]; +} + +// overridden at runtime per-class for performance ++ (NSString *)className { + NSString *className = NSStringFromClass(self); + if ([RLMSwiftSupport isSwiftClassName:className]) { + className = [RLMSwiftSupport demangleClassName:className]; + } + return className; +} + +// overridden at runtime per-class for performance ++ (RLMObjectSchema *)sharedSchema { + return [RLMSchema sharedSchemaForClass:self.class]; +} + ++ (Class)objectUtilClass:(BOOL)isSwift { + return RLMObjectUtilClass(isSwift); +} + +- (NSString *)description +{ + if (self.isInvalidated) { + return @"[invalid object]"; + } + + return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; +} + +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { + if (depth == 0) { + return @""; + } + + NSString *baseClassName = _objectSchema.className; + NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName]; + + for (RLMProperty *property in _objectSchema.properties) { + id object = RLMObjectBaseObjectForKeyedSubscript(self, property.name); + NSString *sub; + if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) { + sub = [object descriptionWithMaxDepth:depth - 1]; + } + else if (property.type == RLMPropertyTypeData) { + static NSUInteger maxPrintedDataLength = 24; + NSData *data = object; + NSUInteger length = data.length; + if (length > maxPrintedDataLength) { + data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength]; + } + NSString *dataDescription = [data description]; + sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length]; + } + else { + sub = [object description]; + } + [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + [mString appendString:@"}"]; + + return [NSString stringWithString:mString]; +} + +- (RLMRealm *)realm { + return _realm; +} + +- (RLMObjectSchema *)objectSchema { + return _objectSchema; +} + +- (BOOL)isInvalidated { + // if not unmanaged and our accessor has been detached, we have been deleted + return self.class == _objectSchema.accessorClass && !_row.is_attached(); +} + +- (BOOL)isEqual:(id)object { + if (RLMObjectBase *other = RLMDynamicCast(object)) { + if (_objectSchema.primaryKeyProperty) { + return RLMObjectBaseAreEqual(self, other); + } + } + return [super isEqual:object]; +} + +- (NSUInteger)hash { + if (_objectSchema.primaryKeyProperty) { + id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name]; + + // modify the hash of our primary key value to avoid potential (although unlikely) collisions + return [primaryProperty hash] ^ 1; + } + else { + return [super hash]; + } +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return RLMIsObjectSubclass(self); +} + ++ (NSString *)_realmObjectName { + return nil; +} + +- (id)mutableArrayValueForKey:(NSString *)key { + id obj = [self valueForKey:key]; + if ([obj isKindOfClass:[RLMArray class]]) { + return obj; + } + return [super mutableArrayValueForKey:key]; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + if (!_observationInfo) { + _observationInfo = new RLMObservationInfo(self); + } + _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath); + + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { + [super removeObserver:observer forKeyPath:keyPath]; + if (_observationInfo) + _observationInfo->removeObserver(); +} + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + const char *className = class_getName(self); + const char accessorClassPrefix[] = "RLM:Managed"; + if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { + if ([class_getSuperclass(self.class) sharedSchema][key]) { + return NO; + } + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +#pragma mark - Thread Confined Protocol Conformance + +- (std::unique_ptr)makeThreadSafeReference { + Object object(_realm->_realm, *_info->objectSchema, _row); + realm::ThreadSafeReference reference = _realm->_realm->obtain_thread_safe_reference(std::move(object)); + return std::make_unique>(std::move(reference)); +} + +- (id)objectiveCMetadata { + return nil; +} + ++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference + metadata:(__unused id)metadata + realm:(RLMRealm *)realm { + REALM_ASSERT_DEBUG(dynamic_cast *>(reference.get())); + auto object_reference = static_cast *>(reference.get()); + + Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference)); + if (!object.is_valid()) { + return nil; + } + NSString *objectClassName = @(object.get_object_schema().name.c_str()); + + return RLMCreateObjectAccessor(realm, realm->_info[objectClassName], object.row().get_index()); +} + +@end + +RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) { + return object ? object->_realm : nil; +} + +RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) { + return object ? object->_objectSchema : nil; +} + +id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) { + if (!object) { + return nil; + } + + if (object->_realm) { + return RLMDynamicGetByName(object, key, false); + } + else { + return [object valueForKey:key]; + } +} + +void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) { + if (!object) { + return; + } + + if (object->_realm) { + RLMDynamicValidatedSet(object, key, obj); + } + else { + [object setValue:obj forKey:key]; + } +} + + +BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) { + // if not the correct types throw + if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) { + @throw RLMException(@"Can only compare objects of class RLMObjectBase"); + } + // if identical object (or both are nil) + if (o1 == o2) { + return YES; + } + // if one is nil + if (o1 == nil || o2 == nil) { + return NO; + } + // if not in realm or differing realms + if (o1->_realm == nil || o1->_realm != o2->_realm) { + return NO; + } + // if either are detached + if (!o1->_row.is_attached() || !o2->_row.is_attached()) { + return NO; + } + // if table and index are the same + return o1->_row.get_table() == o2->_row.get_table() + && o1->_row.get_index() == o2->_row.get_index(); +} + +id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) { + @try { + return [object valueForKey:key]; + } + @catch (NSException *e) { + if ([e.name isEqualToString:NSUndefinedKeyException]) { + @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'", + object, className, key); + } + @throw; + } +} + +Class RLMObjectUtilClass(BOOL isSwift) { + static Class objectUtilObjc = [RLMObjectUtil class]; + static Class objectUtilSwift = NSClassFromString(@"RealmSwiftObjectUtil"); + return isSwift && objectUtilSwift ? objectUtilSwift : objectUtilObjc; +} + +@implementation RLMObjectUtil + ++ (NSArray *)ignoredPropertiesForClass:(Class)cls { + return [cls ignoredProperties]; +} + ++ (NSArray *)indexedPropertiesForClass:(Class)cls { + return [cls indexedProperties]; +} + ++ (NSDictionary *)linkingObjectsPropertiesForClass:(Class)cls { + return [cls linkingObjectsProperties]; +} + ++ (NSDictionary *)linkingObjectProperties:(__unused id)object { + return nil; +} + ++ (NSArray *)getGenericListPropertyNames:(__unused id)obj { + return nil; +} + ++ (NSDictionary *)getLinkingObjectsProperties:(__unused id)obj { + return nil; +} + ++ (NSDictionary *)getOptionalProperties:(__unused id)obj { + return nil; +} + ++ (NSArray *)requiredPropertiesForClass:(Class)cls { + return [cls requiredProperties]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectSchema.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectSchema.mm new file mode 100644 index 0000000..ec0c8b0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectSchema.mm @@ -0,0 +1,445 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObjectSchema_Private.hpp" + +#import "RLMArray.h" +#import "RLMListBase.h" +#import "RLMObject_Private.h" +#import "RLMProperty_Private.hpp" +#import "RLMRealm_Dynamic.h" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" + +using namespace realm; + +// private properties +@interface RLMObjectSchema () +@property (nonatomic, readwrite) NSDictionary *allPropertiesByName; +@property (nonatomic, readwrite) NSString *className; +@end + +@implementation RLMObjectSchema { + NSArray *_swiftGenericProperties; +} + +- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties { + self = [super init]; + self.className = objectClassName; + self.properties = properties; + self.objectClass = objectClass; + self.accessorClass = objectClass; + self.unmanagedClass = objectClass; + return self; +} + +// return properties by name +- (RLMProperty *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)key { + return _allPropertiesByName[key]; +} + +// create property map when setting property array +- (void)setProperties:(NSArray *)properties { + _properties = properties; + [self _propertiesDidChange]; +} + +- (void)setComputedProperties:(NSArray *)computedProperties { + _computedProperties = computedProperties; + [self _propertiesDidChange]; +} + +- (void)_propertiesDidChange { + NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:_properties.count + _computedProperties.count]; + NSUInteger index = 0; + for (RLMProperty *prop in _properties) { + prop.index = index++; + map[prop.name] = prop; + if (prop.isPrimary) { + self.primaryKeyProperty = prop; + } + } + for (RLMProperty *prop in _computedProperties) { + map[prop.name] = prop; + } + _allPropertiesByName = map; +} + + +- (void)setPrimaryKeyProperty:(RLMProperty *)primaryKeyProperty { + _primaryKeyProperty.isPrimary = NO; + primaryKeyProperty.isPrimary = YES; + _primaryKeyProperty = primaryKeyProperty; +} + ++ (instancetype)schemaForObjectClass:(Class)objectClass { + RLMObjectSchema *schema = [RLMObjectSchema new]; + + // determine classname from objectclass as className method has not yet been updated + NSString *className = NSStringFromClass(objectClass); + bool isSwift = [RLMSwiftSupport isSwiftClassName:className]; + if (isSwift) { + className = [RLMSwiftSupport demangleClassName:className]; + } + schema.className = className; + schema.objectClass = objectClass; + schema.accessorClass = objectClass; + schema.isSwiftClass = isSwift; + + // create array of RLMProperties, inserting properties of superclasses first + Class cls = objectClass; + Class superClass = class_getSuperclass(cls); + NSArray *allProperties = @[]; + while (superClass && superClass != RLMObjectBase.class) { + allProperties = [[RLMObjectSchema propertiesForClass:cls isSwift:isSwift] arrayByAddingObjectsFromArray:allProperties]; + cls = superClass; + superClass = class_getSuperclass(superClass); + } + NSArray *persistedProperties = [allProperties filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RLMProperty *property, NSDictionary *) { + return !RLMPropertyTypeIsComputed(property.type); + }]]; + schema.properties = persistedProperties; + + NSArray *computedProperties = [allProperties filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RLMProperty *property, NSDictionary *) { + return RLMPropertyTypeIsComputed(property.type); + }]]; + schema.computedProperties = computedProperties; + + // verify that we didn't add any properties twice due to inheritance + if (allProperties.count != [NSSet setWithArray:[allProperties valueForKey:@"name"]].count) { + NSCountedSet *countedPropertyNames = [NSCountedSet setWithArray:[allProperties valueForKey:@"name"]]; + NSSet *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) { + return [countedPropertyNames countForObject:object] > 1; + }]]; + + if (duplicatePropertyNames.count == 1) { + @throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.allObjects.firstObject, className); + } else { + @throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames.allObjects componentsJoinedByString:@"', '"]); + } + } + + if (NSString *primaryKey = [objectClass primaryKey]) { + for (RLMProperty *prop in schema.properties) { + if ([primaryKey isEqualToString:prop.name]) { + prop.indexed = YES; + schema.primaryKeyProperty = prop; + break; + } + } + + if (!schema.primaryKeyProperty) { + @throw RLMException(@"Primary key property '%@' does not exist on object '%@'", primaryKey, className); + } + if (schema.primaryKeyProperty.type != RLMPropertyTypeInt && schema.primaryKeyProperty.type != RLMPropertyTypeString) { + @throw RLMException(@"Property '%@' cannot be made the primary key of '%@' because it is not a 'string' or 'int' property.", + primaryKey, className); + } + } + + for (RLMProperty *prop in schema.properties) { + if (prop.optional && !RLMPropertyTypeIsNullable(prop.type)) { + @throw RLMException(@"Property '%@.%@' cannot be made optional because optional '%@' properties are not supported.", + className, prop.name, RLMTypeToString(prop.type)); + } + } + + return schema; +} + ++ (nullable NSString *)baseNameForLazySwiftProperty:(NSString *)propertyName { + // A Swift lazy var shows up as two separate children on the reflection tree: one named 'x', and another that is + // optional and is named 'x.storage'. Note that '.' is illegal in either a Swift or Objective-C property name. + NSString *const storageSuffix = @".storage"; + if ([propertyName hasSuffix:storageSuffix]) { + return [propertyName substringToIndex:propertyName.length - storageSuffix.length]; + } + return nil; +} + ++ (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass { + Class objectUtil = [objectClass objectUtilClass:isSwiftClass]; + NSArray *ignoredProperties = [objectUtil ignoredPropertiesForClass:objectClass]; + NSDictionary *linkingObjectsProperties = [objectUtil linkingObjectsPropertiesForClass:objectClass]; + + // For Swift classes we need an instance of the object when parsing properties + id swiftObjectInstance = isSwiftClass ? [[objectClass alloc] init] : nil; + + unsigned int count; + objc_property_t *props = class_copyPropertyList(objectClass, &count); + NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:count]; + NSSet *indexed = [[NSSet alloc] initWithArray:[objectUtil indexedPropertiesForClass:objectClass]]; + for (unsigned int i = 0; i < count; i++) { + NSString *propertyName = @(property_getName(props[i])); + if ([ignoredProperties containsObject:propertyName]) { + continue; + } + + RLMProperty *prop = nil; + if (isSwiftClass) { + prop = [[RLMProperty alloc] initSwiftPropertyWithName:propertyName + indexed:[indexed containsObject:propertyName] + linkPropertyDescriptor:linkingObjectsProperties[propertyName] + property:props[i] + instance:swiftObjectInstance]; + } + else { + prop = [[RLMProperty alloc] initWithName:propertyName + indexed:[indexed containsObject:propertyName] + linkPropertyDescriptor:linkingObjectsProperties[propertyName] + property:props[i]]; + } + + if (prop) { + [propArray addObject:prop]; + } + } + free(props); + + if (isSwiftClass) { + // List<> properties don't show up as objective-C properties due to + // being generic, so use Swift reflection to get a list of them, and + // then access their ivars directly + for (NSString *propName in [objectUtil getGenericListPropertyNames:swiftObjectInstance]) { + Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); + id value = object_getIvar(swiftObjectInstance, ivar); + NSString *className = [value _rlmArray].objectClassName; + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propName]; + }]; + if (existing != NSNotFound) { + [propArray removeObjectAtIndex:existing]; + } + [propArray addObject:[[RLMProperty alloc] initSwiftListPropertyWithName:propName + ivar:ivar + objectClassName:className]]; + } + + // Ditto for LinkingObjects<> properties. + NSDictionary *linkingObjectsProperties = [objectUtil getLinkingObjectsProperties:swiftObjectInstance]; + for (NSString *propName in linkingObjectsProperties) { + NSDictionary *info = linkingObjectsProperties[propName]; + Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); + + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propName]; + }]; + if (existing != NSNotFound) { + [propArray removeObjectAtIndex:existing]; + } + + [propArray addObject:[[RLMProperty alloc] initSwiftLinkingObjectsPropertyWithName:propName + ivar:ivar + objectClassName:info[@"class"] + linkOriginPropertyName:info[@"property"]]]; + } + } + + if (auto optionalProperties = [objectUtil getOptionalProperties:swiftObjectInstance]) { + for (RLMProperty *property in propArray) { + property.optional = false; + } + [optionalProperties enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSNumber *propertyType, __unused BOOL *stop) { + if ([ignoredProperties containsObject:propertyName]) { + return; + } + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propertyName]; + }]; + RLMProperty *property; + if (existing != NSNotFound) { + property = propArray[existing]; + property.optional = true; + } + if (auto type = RLMCoerceToNil(propertyType)) { + if (existing == NSNotFound) { + // Check to see if this optional property is an underlying storage property for a Swift lazy var. + // Managed lazy vars are't allowed. + // NOTE: Revisit this once property behaviors are implemented in Swift. + if (NSString *lazyPropertyBaseName = [self baseNameForLazySwiftProperty:propertyName]) { + if ([ignoredProperties containsObject:lazyPropertyBaseName]) { + // This property is the storage property for a ignored lazy Swift property. Just continue. + return; + } else { + @throw RLMException(@"Lazy managed property '%@' is not allowed on a Realm Swift object class. Either add the property to the ignored properties list or make it non-lazy.", lazyPropertyBaseName); + } + } + // The current property isn't a storage property for a lazy Swift property. + property = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:propertyName + indexed:[indexed containsObject:propertyName] + ivar:class_getInstanceVariable(objectClass, propertyName.UTF8String) + propertyType:RLMPropertyType(type.intValue)]; + [propArray addObject:property]; + } + else { + property.type = RLMPropertyType(type.intValue); + } + } + }]; + } + if (auto requiredProperties = [objectUtil requiredPropertiesForClass:objectClass]) { + for (RLMProperty *property in propArray) { + bool required = [requiredProperties containsObject:property.name]; + if (required && property.type == RLMPropertyTypeObject) { + @throw RLMException(@"Object properties cannot be made required, " + "but '+[%@ requiredProperties]' included '%@'", objectClass, property.name); + } + property.optional &= !required; + } + } + + for (RLMProperty *property in propArray) { + if (!property.optional && property.type == RLMPropertyTypeObject) { // remove if/when core supports required link columns + @throw RLMException(@"The `%@.%@` property must be marked as being optional.", [objectClass className], property.name); + } + } + + return propArray; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMObjectSchema *schema = [[RLMObjectSchema allocWithZone:zone] init]; + schema->_objectClass = _objectClass; + schema->_className = _className; + schema->_objectClass = _objectClass; + schema->_accessorClass = _objectClass; + schema->_unmanagedClass = _unmanagedClass; + schema->_isSwiftClass = _isSwiftClass; + + // call property setter to reset map and primary key + schema.properties = [[NSArray allocWithZone:zone] initWithArray:_properties copyItems:YES]; + schema.computedProperties = [[NSArray allocWithZone:zone] initWithArray:_computedProperties copyItems:YES]; + + return schema; +} + +- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema { + if (objectSchema.properties.count != _properties.count) { + return NO; + } + + if (![_properties isEqualToArray:objectSchema.properties]) { + return NO; + } + if (![_computedProperties isEqualToArray:objectSchema.computedProperties]) { + return NO; + } + + return YES; +} + +- (NSString *)description { + NSMutableString *propertiesString = [NSMutableString string]; + for (RLMProperty *property in self.properties) { + [propertiesString appendFormat:@"\t%@\n", [property.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + for (RLMProperty *property in self.computedProperties) { + [propertiesString appendFormat:@"\t%@\n", [property.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + return [NSString stringWithFormat:@"%@ {\n%@}", self.className, propertiesString]; +} + +- (NSString *)objectName { + return [self.objectClass _realmObjectName] ?: _className; +} + +- (realm::ObjectSchema)objectStoreCopy { + ObjectSchema objectSchema; + objectSchema.name = self.objectName.UTF8String; + objectSchema.primary_key = _primaryKeyProperty ? _primaryKeyProperty.name.UTF8String : ""; + for (RLMProperty *prop in _properties) { + Property p = [prop objectStoreCopy]; + p.is_primary = (prop == _primaryKeyProperty); + objectSchema.persisted_properties.push_back(std::move(p)); + } + for (RLMProperty *prop in _computedProperties) { + objectSchema.computed_properties.push_back([prop objectStoreCopy]); + } + return objectSchema; +} + ++ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)objectSchema { + RLMObjectSchema *schema = [RLMObjectSchema new]; + schema.className = @(objectSchema.name.c_str()); + + // create array of RLMProperties + NSMutableArray *properties = [NSMutableArray arrayWithCapacity:objectSchema.persisted_properties.size()]; + for (const Property &prop : objectSchema.persisted_properties) { + RLMProperty *property = [RLMProperty propertyForObjectStoreProperty:prop]; + property.isPrimary = (prop.name == objectSchema.primary_key); + [properties addObject:property]; + } + schema.properties = properties; + + NSMutableArray *computedProperties = [NSMutableArray arrayWithCapacity:objectSchema.computed_properties.size()]; + for (const Property &prop : objectSchema.computed_properties) { + [computedProperties addObject:[RLMProperty propertyForObjectStoreProperty:prop]]; + } + schema.computedProperties = computedProperties; + + // get primary key from realm metadata + if (objectSchema.primary_key.length()) { + NSString *primaryKeyString = [NSString stringWithUTF8String:objectSchema.primary_key.c_str()]; + schema.primaryKeyProperty = schema[primaryKeyString]; + if (!schema.primaryKeyProperty) { + @throw RLMException(@"No property matching primary key '%@'", primaryKeyString); + } + } + + // for dynamic schema use vanilla RLMDynamicObject accessor classes + schema.objectClass = RLMObject.class; + schema.accessorClass = RLMDynamicObject.class; + schema.unmanagedClass = RLMObject.class; + + return schema; +} + +- (NSArray *)swiftGenericProperties { + if (_swiftGenericProperties) { + return _swiftGenericProperties; + } + + // This check isn't semantically required, but avoiding accessing the local + // static helps perf in the obj-c case + if (!_isSwiftClass) { + return _swiftGenericProperties = @[]; + } + + // Check if it's a swift class using the obj-c API + static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject"); + if (![_accessorClass isSubclassOfClass:s_swiftObjectClass]) { + return _swiftGenericProperties = @[]; + } + + NSMutableArray *genericProperties = [NSMutableArray new]; + for (RLMProperty *prop in _properties) { + if (prop->_swiftIvar) { + [genericProperties addObject:prop]; + } + } + // Currently all computed properties are Swift generics + [genericProperties addObjectsFromArray:_computedProperties]; + + return _swiftGenericProperties = genericProperties; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectStore.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectStore.mm new file mode 100644 index 0000000..6bc53c2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObjectStore.mm @@ -0,0 +1,550 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObjectStore.h" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObservation.hpp" +#import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMOptionalBase.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "results.hpp" +#import "shared_realm.hpp" + +#import + +using namespace realm; + +void RLMRealmCreateAccessors(RLMSchema *schema) { + const size_t bufferSize = sizeof("RLM:Managed ") // includes null terminator + + std::numeric_limits::digits10 + + realm::Group::max_table_name_length; + + char className[bufferSize] = "RLM:Managed "; + char *const start = className + strlen(className); + + for (RLMObjectSchema *objectSchema in schema.objectSchema) { + if (objectSchema.accessorClass != objectSchema.objectClass) { + continue; + } + + static unsigned long long count = 0; + sprintf(start, "%llu %s", count++, objectSchema.className.UTF8String); + objectSchema.accessorClass = RLMManagedAccessorClassForObjectClass(objectSchema.objectClass, objectSchema, className); + } +} + +static inline void RLMVerifyRealmRead(__unsafe_unretained RLMRealm *const realm) { + if (!realm) { + @throw RLMException(@"Realm must not be nil"); + } + [realm verifyThread]; +} + +static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm) { + RLMVerifyRealmRead(realm); + // if realm is not writable throw + if (!realm.inWriteTransaction) { + @throw RLMException(@"Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first."); + } +} + +void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const object) { + if (!object || !object->_row || !object->_objectSchema->_isSwiftClass) { + return; + } + if (![object isKindOfClass:object->_objectSchema.objectClass]) { + // It can be a different class if it's a dynamic object, and those don't + // require any init here (and would crash since they don't have the ivars) + return; + } + + for (RLMProperty *prop in object->_objectSchema.swiftGenericProperties) { + if (prop->_type == RLMPropertyTypeArray) { + RLMArray *array = [[RLMArrayLinkView alloc] initWithParent:object property:prop]; + [object_getIvar(object, prop.swiftIvar) set_rlmArray:array]; + } + else if (prop.type == RLMPropertyTypeLinkingObjects) { + id linkingObjects = object_getIvar(object, prop.swiftIvar); + [linkingObjects setObject:(id)[[RLMWeakObjectHandle alloc] initWithObject:object]]; + [linkingObjects setProperty:prop]; + } + else { + RLMOptionalBase *optional = object_getIvar(object, prop.swiftIvar); + optional.property = prop; + optional.object = object; + } + } +} + +static NSUInteger createRowForObject(RLMClassInfo const& info) { + try { + return info.table()->add_empty_row(); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +/* If a row exists with the specified primary key value, return its index. Otherwise, return `realm::not_found`. + * + * Precondition: `info` must refer to a class which has a primary key property + * Precondition: `primaryValue` is a validated property value that has been coerced to `nil` + */ +static NSUInteger getRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + + RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); + const NSUInteger primaryPropertyColumn = info.tableColumn(primaryProperty); + + switch (primaryProperty.type) { + case RLMPropertyTypeString: + return info.table()->find_first_string(primaryPropertyColumn, RLMStringDataWithNSString(primaryValue)); + + case RLMPropertyTypeInt: + if (primaryValue) { + return info.table()->find_first_int(primaryPropertyColumn, [primaryValue longLongValue]); + } else { + return info.table()->find_first_null(primaryPropertyColumn); + } + + default: + REALM_UNREACHABLE(); + } +} + +/* Create a row with the specified primary key value and return its index. + * + * Precondition: `info` must refer to a class which has a valid primary key property + * Precondition: a write transaction is in progress + * Precondition: no row already exists with the specified `primaryValue` for this model + */ +static NSUInteger createRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); + REALM_ASSERT_DEBUG(getRowForObjectWithPrimaryKey(info, primaryValue) == realm::not_found); + + RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); + const NSUInteger primaryColumnIndex = info.tableColumn(primaryProperty); + + // create row + const NSUInteger rowIndex = createRowForObject(info); + Row row = info.table()->get(rowIndex); + + // set value for primary key + RLMValidateValueForProperty(primaryValue, primaryProperty); + primaryValue = RLMCoerceToNil(primaryValue); + + try { + switch (primaryProperty.type) { + case RLMPropertyTypeString: + REALM_ASSERT_DEBUG(!primaryValue || [primaryValue isKindOfClass:NSString.class]); + row.set_string_unique(primaryColumnIndex, RLMStringDataWithNSString(primaryValue)); + break; + + case RLMPropertyTypeInt: + if (primaryValue) { + REALM_ASSERT_DEBUG([primaryValue isKindOfClass:NSNumber.class]); + row.set_int_unique(primaryColumnIndex, [primaryValue longLongValue]); + } else { + row.set_null(primaryColumnIndex); // FIXME: Use `set_null_unique` once Core supports it + } + break; + + default: + REALM_UNREACHABLE(); + } + } + catch (std::exception const& e) { + @throw RLMException(e); + } + return rowIndex; +} + +/* If a row exists with the specified primary key value, returns its index. Otherwise, creates a new row with the + * specified primary key value and returns its index. The out parameter `foundExisting` will be set to indicate + * whether or not a new row was created. + * + * Precondition: `info` must refer to a class which has a valid primary key property + * Precondition: a write transaction is in progress + */ +static NSUInteger createOrGetRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue, + bool* foundExisting = nullptr) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); + + const NSUInteger existingRow = getRowForObjectWithPrimaryKey(info, primaryValue); + if (existingRow == realm::not_found) { + *foundExisting = false; + return createRowForObjectWithPrimaryKey(info, primaryValue); + } else { + *foundExisting = true; + return existingRow; + } +} + +/* If the class has a primary key, calls `valueForProperty` with that key and creates or gets the row with + * this primary key value. Otherwise if the class has no primary key, creates a new row. The out parameter + * `foundExisting` will be set to indicate whether or not a new row was created. + * + * Precondition: a write transaction is in progress + */ +template +static NSUInteger createOrGetRowForObject(RLMClassInfo const& info, F valueForProperty, + bool createOrUpdate, bool* foundExisting) { + // try to get existing row if this class has a primary key + if (RLMProperty *primaryProperty = info.propertyForPrimaryKey()) { + // get primary value + const id primaryValue = valueForProperty(primaryProperty); + + // search for existing object based on primary key type, creating a new row if one does not exist + NSUInteger rowIndex = createOrGetRowForObjectWithPrimaryKey(info, RLMCoerceToNil(primaryValue), foundExisting); + + // ensure that `createOrUpdate` is set if we found an existing row + if (*foundExisting && !createOrUpdate) { + @throw RLMException(@"Can't create object with existing primary key value '%@'.", primaryValue); + } + return rowIndex; + } + // if no existing, create row + else { + *foundExisting = false; + return createRowForObject(info); + } +} + +void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, + __unsafe_unretained RLMRealm *const realm, + bool createOrUpdate) { + RLMVerifyInWriteTransaction(realm); + + // verify that object is unmanaged + if (object.invalidated) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + if (object->_realm) { + if (object->_realm == realm) { + // no-op + return; + } + // for differing realms users must explicitly create the object in the second realm + @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm."); + } + if (object->_observationInfo && object->_observationInfo->hasObservers()) { + @throw RLMException(@"Cannot add an object with observers to a Realm"); + } + + // set the realm and schema + NSString *objectClassName = object->_objectSchema.className; + auto& info = realm->_info[objectClassName]; + object->_info = &info; + object->_objectSchema = info.rlmObjectSchema; + object->_realm = realm; + + // get or create row + bool foundExisting; + auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return [object valueForKey:p.name]; }; + object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; + + RLMCreationOptions creationOptions = RLMCreationOptionsPromoteUnmanaged; + if (createOrUpdate) { + creationOptions |= RLMCreationOptionsCreateOrUpdate; + } + + // populate all properties + for (RLMProperty *prop in info.rlmObjectSchema.properties) { + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + // get object from ivar using key value coding + id value = nil; + if (prop.swiftIvar) { + if (prop.type == RLMPropertyTypeArray) { + value = static_cast(object_getIvar(object, prop.swiftIvar))._rlmArray; + } + else { // optional + value = static_cast(object_getIvar(object, prop.swiftIvar)).underlyingValue; + } + } + else if ([object respondsToSelector:prop.getterSel]) { + value = [object valueForKey:prop.getterName]; + } + + if (!value && !prop.optional) { + @throw RLMException(@"Nil value specified for required property '%@' in '%@'", + prop.name, info.rlmObjectSchema.className); + } + + // set the ivars for object and array properties to nil as otherwise the + // accessors retain objects that are no longer accessible via the properties + // this is mainly an issue when the object graph being added has cycles, + // as it's not obvious that the user has to set the *ivars* to nil to + // avoid leaking memory + if (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray) { + if (!prop.swiftIvar) { + ((void(*)(id, SEL, id))objc_msgSend)(object, prop.setterSel, nil); + } + } + + // set in table with out validation + RLMDynamicSet(object, prop, RLMCoerceToNil(value), creationOptions); + } + + // set to proper accessor class + object_setClass(object, info.rlmObjectSchema.accessorClass); + + RLMInitializeSwiftAccessorGenerics(object); +} + +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, + id value, bool createOrUpdate = false) { + RLMVerifyInWriteTransaction(realm); + + if (createOrUpdate && RLMIsObjectSubclass([value class])) { + RLMObjectBase *obj = value; + if ([obj->_objectSchema.className isEqualToString:className] && obj->_realm == realm) { + // This is a no-op if value is an RLMObject of the same type already backed by the target realm. + return value; + } + } + + if (!value || value == NSNull.null) { + @throw RLMException(@"Must provide a non-nil value."); + } + + // create the object + auto& info = realm->_info[className]; + RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + + RLMCreationOptions creationOptions = createOrUpdate ? RLMCreationOptionsCreateOrUpdate : RLMCreationOptionsNone; + + // create row, and populate + if (NSArray *array = RLMDynamicCast(value)) { + NSArray *props = info.rlmObjectSchema.properties; + if (array.count > props.count) { + @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", + (unsigned long long)array.count, (unsigned long long)props.count); + } + + // get or create our accessor + bool foundExisting; + auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { + auto index = [props indexOfObject:p]; + if (index >= array.count) { + @throw RLMException(@"Invalid array input: primary key must be present."); + } + return array[index]; + }; + object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, + createOrUpdate, &foundExisting)]; + + // populate + NSUInteger i = 0; + for (id val in array) { + RLMProperty *prop = props[i++]; + + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + RLMValidateValueForProperty(val, prop); + RLMDynamicSet(object, prop, RLMCoerceToNil(val), creationOptions); + } + } + else { + __block bool foundExisting = false; + __block NSDictionary *defaultValues = nil; + __block bool usedDefault = false; + auto getValue = ^(RLMProperty *prop) { + id propValue; + if ([value respondsToSelector:prop.getterSel]) { + propValue = RLMValidatedValueForProperty(value, prop.getterName, + info.rlmObjectSchema.className); + } + else { + propValue = RLMValidatedValueForProperty(value, prop.name, info.rlmObjectSchema.className); + } + usedDefault = !propValue && !foundExisting; + if (usedDefault) { + if (!defaultValues) { + defaultValues = RLMDefaultValuesForObjectSchema(info.rlmObjectSchema); + } + propValue = defaultValues[prop.name]; + if (!propValue && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray)) { + propValue = NSNull.null; + } + } + return propValue; + }; + // get or create our accessor + object->_row = (*info.table())[createOrGetRowForObject(info, getValue, createOrUpdate, &foundExisting)]; + + // populate + for (RLMProperty *prop in info.rlmObjectSchema.properties) { + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + if (id propValue = getValue(prop)) { + // add SetDefault to creationoptions + RLMCreationOptions propertyCreationOptions = creationOptions; + if (usedDefault) { + propertyCreationOptions |= RLMCreationOptionsSetDefault; + } + RLMValidateValueForProperty(propValue, prop); + RLMDynamicSet(object, prop, RLMCoerceToNil(propValue), propertyCreationOptions); + } + else if (!foundExisting && !prop.optional) { + @throw RLMException(@"Property '%@' of object of type '%@' cannot be nil.", prop.name, info.rlmObjectSchema.className); + } + } + } + + RLMInitializeSwiftAccessorGenerics(object); + return object; +} + +void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object, + __unsafe_unretained RLMRealm *const realm) { + if (realm != object->_realm) { + @throw RLMException(@"Can only delete an object from the Realm it belongs to."); + } + + RLMVerifyInWriteTransaction(object->_realm); + + // move last row to row we are deleting + if (object->_row.is_attached()) { + RLMTrackDeletions(realm, ^{ + object->_row.get_table()->move_last_over(object->_row.get_index()); + }); + } + + // set realm to nil + object->_realm = nil; +} + +void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) { + RLMVerifyInWriteTransaction(realm); + + // clear table for each object schema + for (auto& info : realm->_info) { + RLMClearTable(info.second); + } +} + +RLMResults *RLMGetObjects(__unsafe_unretained RLMRealm *const realm, + NSString *objectClassName, + NSPredicate *predicate) { + RLMVerifyRealmRead(realm); + + // create view from table and predicate + RLMClassInfo& info = realm->_info[objectClassName]; + if (!info.table()) { + // read-only realms may be missing tables since we can't add any + // missing ones on init + return [RLMResults resultsWithObjectInfo:info results:{}]; + } + + if (predicate) { + realm::Query query = RLMPredicateToQuery(predicate, info.rlmObjectSchema, realm.schema, realm.group); + return [RLMResults resultsWithObjectInfo:info + results:realm::Results(realm->_realm, std::move(query))]; + } + + return [RLMResults resultsWithObjectInfo:info + results:realm::Results(realm->_realm, *info.table())]; +} + +id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) { + RLMVerifyRealmRead(realm); + + RLMClassInfo& info = realm->_info[objectClassName]; + auto primaryProperty = info.objectSchema->primary_key_property(); + if (!primaryProperty) { + @throw RLMException(@"%@ does not have a primary key", objectClassName); + } + + auto table = info.table(); + if (!table) { + // read-only realms may be missing tables since we can't add any + // missing ones on init + return nil; + } + + key = RLMCoerceToNil(key); + if (!key && !primaryProperty->is_nullable) { + @throw RLMException(@"Invalid null value for non-nullable primary key."); + } + + size_t row = realm::not_found; + switch (primaryProperty->type) { + case PropertyType::String: { + NSString *string = RLMDynamicCast(key); + if (!key || string) { + row = table->find_first_string(primaryProperty->table_column, RLMStringDataWithNSString(string)); + } else { + @throw RLMException(@"Invalid value '%@' of type '%@' for string primary key.", key, [key class]); + } + break; + } + case PropertyType::Int: + if (NSNumber *number = RLMDynamicCast(key)) { + row = table->find_first_int(primaryProperty->table_column, number.longLongValue); + } else if (!key) { + row = table->find_first_null(primaryProperty->table_column); + } else { + @throw RLMException(@"Invalid value '%@' of type '%@' for int primary key.", key, [key class]); + } + break; + default: + REALM_UNREACHABLE(); + } + + if (row == realm::not_found) { + return nil; + } + + return RLMCreateObjectAccessor(realm, info, row); +} + +RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, + RLMClassInfo& info, + NSUInteger index) { + return RLMCreateObjectAccessor(realm, info, (*info.table())[index]); +} + +// Create accessor and register with realm +RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, + RLMClassInfo& info, + realm::RowExpr row) { + RLMObjectBase *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + accessor->_row = row; + RLMInitializeSwiftAccessorGenerics(accessor); + return accessor; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMObservation.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObservation.mm new file mode 100644 index 0000000..e6538b6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMObservation.mm @@ -0,0 +1,500 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMObservation.hpp" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" + +#import + +using namespace realm; + +namespace { + template + struct IteratorPair { + Iterator first; + Iterator second; + }; + template + Iterator begin(IteratorPair const& p) { + return p.first; + } + template + Iterator end(IteratorPair const& p) { + return p.second; + } + + template + auto reverse(Container const& c) { + return IteratorPair{c.rbegin(), c.rend()}; + } +} + +RLMObservationInfo::RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object) +: object(object) +, objectSchema(&objectSchema) +{ + setRow(*objectSchema.table(), row); +} + +RLMObservationInfo::RLMObservationInfo(id object) +: object(object) +{ +} + +RLMObservationInfo::~RLMObservationInfo() { + if (prev) { + // Not the head of the linked list, so just detach from the list + REALM_ASSERT_DEBUG(prev->next == this); + prev->next = next; + if (next) { + REALM_ASSERT_DEBUG(next->prev == this); + next->prev = prev; + } + } + else if (objectSchema) { + // The head of the list, so remove self from the object schema's array + // of observation info, either replacing self with the next info or + // removing entirely if there is no next + auto end = objectSchema->observedObjects.end(); + auto it = find(objectSchema->observedObjects.begin(), end, this); + if (it != end) { + if (next) { + *it = next; + next->prev = nullptr; + } + else { + iter_swap(it, std::prev(end)); + objectSchema->observedObjects.pop_back(); + } + } + } + // Otherwise the observed object was unmanaged, so nothing to do + +#ifdef DEBUG + // ensure that incorrect cleanup fails noisily + object = (__bridge id)(void *)-1; + prev = (RLMObservationInfo *)-1; + next = (RLMObservationInfo *)-1; +#endif +} + +NSString *RLMObservationInfo::columnName(size_t col) const noexcept { + return objectSchema->propertyForTableColumn(col).name; +} + +void RLMObservationInfo::willChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const { + if (indexes) { + forEach([=](__unsafe_unretained auto o) { + [o willChange:kind valuesAtIndexes:indexes forKey:key]; + }); + } + else { + forEach([=](__unsafe_unretained auto o) { + [o willChangeValueForKey:key]; + }); + } +} + +void RLMObservationInfo::didChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const { + if (indexes) { + forEach([=](__unsafe_unretained auto o) { + [o didChange:kind valuesAtIndexes:indexes forKey:key]; + }); + } + else { + forEach([=](__unsafe_unretained auto o) { + [o didChangeValueForKey:key]; + }); + } +} + +void RLMObservationInfo::prepareForInvalidation() { + REALM_ASSERT_DEBUG(objectSchema); + REALM_ASSERT_DEBUG(!prev); + for (auto info = this; info; info = info->next) + info->invalidated = true; +} + +void RLMObservationInfo::setRow(realm::Table &table, size_t newRow) { + REALM_ASSERT_DEBUG(!row); + REALM_ASSERT_DEBUG(objectSchema); + row = table[newRow]; + for (auto info : objectSchema->observedObjects) { + if (info->row && info->row.get_index() == row.get_index()) { + prev = info; + next = info->next; + if (next) + next->prev = this; + info->next = this; + return; + } + } + objectSchema->observedObjects.push_back(this); +} + +void RLMObservationInfo::recordObserver(realm::Row& objectRow, RLMClassInfo *objectInfo, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained NSString *const keyPath) { + ++observerCount; + if (row) { + return; + } + + // add ourselves to the list of observed objects if this is the first time + // an observer is being added to a managed object + if (objectRow) { + this->objectSchema = objectInfo; + setRow(*objectRow.get_table(), objectRow.get_index()); + return; + } + + // Arrays need a reference to their containing object to avoid having to + // go through the awful proxy object from mutableArrayValueForKey. + // For managed objects we do this when the object is added or created + // (and have to to support notifications from modifying an object which + // was never observed), but for Swift classes (both RealmSwift and + // RLMObject) we can't do it then because we don't know what the parent + // object is. + + NSUInteger sep = [keyPath rangeOfString:@"."].location; + NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep]; + RLMProperty *prop = objectSchema[key]; + if (prop && prop.type == RLMPropertyTypeArray) { + id value = valueForKey(key); + RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value; + array->_key = key; + array->_parentObject = object; + } + else if (auto swiftIvar = prop.swiftIvar) { + if (auto optional = RLMDynamicCast(object_getIvar(object, swiftIvar))) { + optional.property = prop; + optional.object = object; + } + } +} + +void RLMObservationInfo::removeObserver() { + --observerCount; +} + +id RLMObservationInfo::valueForKey(NSString *key) { + if (invalidated) { + if ([key isEqualToString:RLMInvalidatedKey]) { + return @YES; + } + return cachedObjects[key]; + } + + if (key != lastKey) { + lastKey = key; + lastProp = objectSchema ? objectSchema->rlmObjectSchema[key] : nil; + } + + static auto superValueForKey = reinterpret_cast([NSObject methodForSelector:@selector(valueForKey:)]); + if (!lastProp) { + // Not a managed property, so use NSObject's implementation of valueForKey: + return RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key)); + } + + auto getSuper = [&] { + return row ? RLMDynamicGet(object, lastProp) : RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key)); + }; + + // We need to return the same object each time for observing over keypaths + // to work, so we store a cache of them here. We can't just cache them on + // the object as that leads to retain cycles. + if (lastProp.type == RLMPropertyTypeArray) { + RLMArray *value = cachedObjects[key]; + if (!value) { + value = getSuper(); + if (!cachedObjects) { + cachedObjects = [NSMutableDictionary new]; + } + cachedObjects[key] = value; + } + return value; + } + + if (lastProp.type == RLMPropertyTypeObject) { + size_t col = row.get_column_index(lastProp.name.UTF8String); + if (row.is_null_link(col)) { + [cachedObjects removeObjectForKey:key]; + return nil; + } + + RLMObjectBase *value = cachedObjects[key]; + if (value && value->_row.get_index() == row.get_link(col)) { + return value; + } + value = getSuper(); + if (!cachedObjects) { + cachedObjects = [NSMutableDictionary new]; + } + cachedObjects[key] = value; + return value; + } + + return getSuper(); +} + +RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, + RLMClassInfo& objectSchema) { + if (info) { + return info; + } + + for (RLMObservationInfo *info : objectSchema.observedObjects) { + if (info->isForRow(row)) { + return info; + } + } + + return nullptr; +} + +void RLMClearTable(RLMClassInfo &objectSchema) { + for (auto info : objectSchema.observedObjects) { + info->willChange(RLMInvalidatedKey); + } + + RLMTrackDeletions(objectSchema.realm, ^{ + objectSchema.table()->clear(); + + for (auto info : objectSchema.observedObjects) { + info->prepareForInvalidation(); + } + }); + + for (auto info : reverse(objectSchema.observedObjects)) { + info->didChange(RLMInvalidatedKey); + } + + objectSchema.observedObjects.clear(); +} + +void RLMTrackDeletions(__unsafe_unretained RLMRealm *const realm, dispatch_block_t block) { + std::vector *> observers; + + // Build up an array of observation info arrays which is indexed by table + // index (the object schemata may be in an entirely different order) + for (auto& info : realm->_info) { + if (info.second.observedObjects.empty()) { + continue; + } + size_t ndx = info.second.table()->get_index_in_group(); + if (ndx >= observers.size()) { + observers.resize(std::max(observers.size() * 2, ndx + 1)); + } + observers[ndx] = &info.second.observedObjects; + } + + // No need for change tracking if no objects are observed + if (observers.empty()) { + block(); + return; + } + + struct change { + RLMObservationInfo *info; + __unsafe_unretained NSString *property; + NSMutableIndexSet *indexes; + }; + + std::vector changes; + std::vector invalidated; + + // This callback is called by core with a list of row deletions and + // resulting link nullifications immediately before things are deleted and nullified + realm.group.set_cascade_notification_handler([&](realm::Group::CascadeNotification const& cs) { + for (auto const& link : cs.links) { + size_t table_ndx = link.origin_table->get_index_in_group(); + if (table_ndx >= observers.size() || !observers[table_ndx]) { + // The modified table has no observers + continue; + } + + for (auto observer : *observers[table_ndx]) { + if (!observer->isForRow(link.origin_row_ndx)) { + continue; + } + + NSString *name = observer->columnName(link.origin_col_ndx); + if (observer->getRow().get_table()->get_column_type(link.origin_col_ndx) != type_LinkList) { + changes.push_back({observer, name}); + continue; + } + + auto c = find_if(begin(changes), end(changes), [&](auto const& c) { + return c.info == observer && c.property == name; + }); + if (c == end(changes)) { + changes.push_back({observer, name, [NSMutableIndexSet new]}); + c = prev(end(changes)); + } + + // We know what row index is being removed from the LinkView, + // but what we actually want is the indexes in the LinkView that + // are going away + auto linkview = observer->getRow().get_linklist(link.origin_col_ndx); + size_t start = 0, index; + while ((index = linkview->find(link.old_target_row_ndx, start)) != realm::not_found) { + [c->indexes addIndex:index]; + start = index + 1; + } + } + } + + for (auto const& row : cs.rows) { + if (row.table_ndx >= observers.size() || !observers[row.table_ndx]) { + // The modified table has no observers + continue; + } + + for (auto observer : *observers[row.table_ndx]) { + if (observer->isForRow(row.row_ndx)) { + invalidated.push_back(observer); + break; + } + } + } + + // The relative order of these loops is very important + for (auto info : invalidated) { + info->willChange(RLMInvalidatedKey); + } + for (auto const& change : changes) { + change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes); + } + for (auto info : invalidated) { + info->prepareForInvalidation(); + } + }); + + try { + block(); + } + catch (...) { + realm.group.set_cascade_notification_handler(nullptr); + throw; + } + + for (auto const& change : reverse(changes)) { + change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes); + } + for (auto info : reverse(invalidated)) { + info->didChange(RLMInvalidatedKey); + } + + realm.group.set_cascade_notification_handler(nullptr); +} + +namespace { +template +void forEach(realm::BindingContext::ObserverState const& state, Func&& func) { + for (size_t i = 0, size = state.changes.size(); i < size; ++i) { + if (state.changes[i].kind != realm::BindingContext::ColumnInfo::Kind::None) { + func(i, state.changes[i], static_cast(state.info)); + } + } +} +} + +std::vector RLMGetObservedRows(RLMSchemaInfo const& schema) { + std::vector observers; + for (auto& table : schema) { + for (auto info : table.second.observedObjects) { + auto const& row = info->getRow(); + if (!row.is_attached()) + continue; + observers.push_back({ + row.get_table()->get_index_in_group(), + row.get_index(), + info}); + } + } + sort(begin(observers), end(observers)); + return observers; +} + +static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) { + switch (kind) { + case realm::BindingContext::ColumnInfo::Kind::None: + case realm::BindingContext::ColumnInfo::Kind::SetAll: + return NSKeyValueChangeSetting; + case realm::BindingContext::ColumnInfo::Kind::Set: + return NSKeyValueChangeReplacement; + case realm::BindingContext::ColumnInfo::Kind::Insert: + return NSKeyValueChangeInsertion; + case realm::BindingContext::ColumnInfo::Kind::Remove: + return NSKeyValueChangeRemoval; + } +} + +static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) { + if (in.empty()) { + return nil; + } + + [out removeAllIndexes]; + for (auto range : in) { + [out addIndexesInRange:{range.first, range.second - range.first}]; + } + return out; +} + +void RLMWillChange(std::vector const& observed, + std::vector const& invalidated) { + for (auto info : invalidated) { + static_cast(info)->willChange(RLMInvalidatedKey); + } + if (!observed.empty()) { + NSMutableIndexSet *indexes = [NSMutableIndexSet new]; + for (auto const& o : observed) { + forEach(o, [&](size_t, auto const& change, RLMObservationInfo *info) { + info->willChange(info->columnName(change.initial_column_index), + convert(change.kind), convert(change.indices, indexes)); + }); + } + } + for (auto info : invalidated) { + static_cast(info)->prepareForInvalidation(); + } +} + +void RLMDidChange(std::vector const& observed, + std::vector const& invalidated) { + if (!observed.empty()) { + // Loop in reverse order to avoid O(N^2) behavior in Foundation + NSMutableIndexSet *indexes = [NSMutableIndexSet new]; + for (auto const& o : reverse(observed)) { + forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) { + info->didChange(info->columnName(i), convert(change.kind), convert(change.indices, indexes)); + }); + } + } + for (auto const& info : reverse(invalidated)) { + static_cast(info)->didChange(RLMInvalidatedKey); + } +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMOptionalBase.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMOptionalBase.mm new file mode 100644 index 0000000..8aad1c8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMOptionalBase.mm @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMAccessor.h" +#import "RLMOptionalBase.h" +#import "RLMObject_Private.h" +#import "RLMObjectStore.h" +#import "RLMProperty_Private.h" +#import "RLMUtil.hpp" + +#import + +@interface RLMOptionalBase () +@property (nonatomic) id unmanagedValue; +@end + +@implementation RLMOptionalBase + +- (instancetype)init { + return self; +} + +- (id)underlyingValue { + if ((_object && _object->_realm) || _object.isInvalidated) { + return RLMDynamicGet(_object, _property); + } + else { + return _unmanagedValue; + } +} + +- (void)setUnderlyingValue:(id)underlyingValue { + if ((_object && _object->_realm) || _object.isInvalidated) { + if (_property.isPrimary) { + @throw RLMException(@"Primary key can't be changed after an object is inserted."); + } + RLMDynamicSet(_object, _property, underlyingValue, RLMCreationOptionsNone); + } + else { + NSString *propertyName = _property.name; + [_object willChangeValueForKey:propertyName]; + _unmanagedValue = underlyingValue; + [_object didChangeValueForKey:propertyName]; + } +} + +- (BOOL)isKindOfClass:(Class)aClass { + return [self.underlyingValue isKindOfClass:aClass] || RLMIsKindOfClass(object_getClass(self), aClass); +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { + return [self.underlyingValue methodSignatureForSelector:sel]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + [invocation invokeWithTarget:self.underlyingValue]; +} + +- (id)forwardingTargetForSelector:(__unused SEL)sel { + return self.underlyingValue; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + if (id val = self.underlyingValue) { + return [val respondsToSelector:aSelector]; + } + return NO; +} + +- (void)doesNotRecognizeSelector:(SEL)aSelector { + [self.underlyingValue doesNotRecognizeSelector:aSelector]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMPredicateUtil.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMPredicateUtil.mm new file mode 100644 index 0000000..247850b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMPredicateUtil.mm @@ -0,0 +1,124 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMPredicateUtil.hpp" + +#include + +// NSConditionalExpressionType is new in OS X 10.11 and iOS 9.0 +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CONDITIONAL_EXPRESSION_DECLARED (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CONDITIONAL_EXPRESSION_DECLARED (__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) +#else +#define CONDITIONAL_EXPRESSION_DECLARED 0 +#endif + +#if !CONDITIONAL_EXPRESSION_DECLARED + +#define NSConditionalExpressionType 20 + +@interface NSExpression (NewIn1011And90) ++ (NSExpression *)expressionForConditional:(NSPredicate *)predicate trueExpression:(NSExpression *)trueExpression falseExpression:(NSExpression *)falseExpression; +- (NSExpression *)trueExpression; +- (NSExpression *)falseExpression; +@end + +#endif + +namespace { + +struct PredicateExpressionTransformer { + PredicateExpressionTransformer(ExpressionVisitor visitor) : m_visitor(visitor) { } + + NSExpression *visit(NSExpression *expression) const; + NSPredicate *visit(NSPredicate *predicate) const; + + ExpressionVisitor m_visitor; +}; + +NSExpression *PredicateExpressionTransformer::visit(NSExpression *expression) const { + expression = m_visitor(expression); + + switch (expression.expressionType) { + case NSFunctionExpressionType: { + NSMutableArray *arguments = [NSMutableArray array]; + for (NSExpression *argument in expression.arguments) { + [arguments addObject:visit(argument)]; + } + if (expression.operand) { + return [NSExpression expressionForFunction:visit(expression.operand) selectorName:expression.function arguments:arguments]; + } else { + return [NSExpression expressionForFunction:expression.function arguments:arguments]; + } + } + + case NSUnionSetExpressionType: + return [NSExpression expressionForUnionSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + case NSIntersectSetExpressionType: + return [NSExpression expressionForIntersectSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + case NSMinusSetExpressionType: + return [NSExpression expressionForMinusSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + + case NSSubqueryExpressionType: { + NSExpression *collection = expression.collection; + // NSExpression.collection is declared as id, but appears to always hold an NSExpression for subqueries. + REALM_ASSERT([collection isKindOfClass:[NSExpression class]]); + return [NSExpression expressionForSubquery:visit(collection) usingIteratorVariable:expression.variable predicate:visit(expression.predicate)]; + } + + case NSAggregateExpressionType: { + NSMutableArray *subexpressions = [NSMutableArray array]; + for (NSExpression *subexpression in expression.collection) { + [subexpressions addObject:visit(subexpression)]; + } + return [NSExpression expressionForAggregate:subexpressions]; + } + + case NSConditionalExpressionType: + return [NSExpression expressionForConditional:visit(expression.predicate) trueExpression:visit(expression.trueExpression) falseExpression:visit(expression.falseExpression)]; + + default: + // The remaining expression types do not contain nested expressions or predicates. + return expression; + } +} + +NSPredicate *PredicateExpressionTransformer::visit(NSPredicate *predicate) const { + if ([predicate isKindOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *compoundPredicate = (NSCompoundPredicate *)predicate; + NSMutableArray *subpredicates = [NSMutableArray array]; + for (NSPredicate *subpredicate in compoundPredicate.subpredicates) { + [subpredicates addObject:visit(subpredicate)]; + } + return [[NSCompoundPredicate alloc] initWithType:compoundPredicate.compoundPredicateType subpredicates:subpredicates]; + } + if ([predicate isKindOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *comparisonPredicate = (NSComparisonPredicate *)predicate; + NSExpression *leftExpression = visit(comparisonPredicate.leftExpression); + NSExpression *rightExpression = visit(comparisonPredicate.rightExpression); + return [NSComparisonPredicate predicateWithLeftExpression:leftExpression rightExpression:rightExpression modifier:comparisonPredicate.comparisonPredicateModifier type:comparisonPredicate.predicateOperatorType options:comparisonPredicate.options]; + } + return predicate; +} + +} // anonymous namespace + +NSPredicate *transformPredicate(NSPredicate *predicate, ExpressionVisitor visitor) { + PredicateExpressionTransformer transformer(visitor); + return transformer.visit(predicate); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMProperty.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMProperty.mm new file mode 100644 index 0000000..f95f494 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMProperty.mm @@ -0,0 +1,578 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMProperty_Private.hpp" + +#import "RLMArray.h" +#import "RLMListBase.h" +#import "RLMObject.h" +#import "RLMObject_Private.h" +#import "RLMOptionalBase.h" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import + +static_assert((int)RLMPropertyTypeInt == realm::type_Int, ""); +static_assert((int)RLMPropertyTypeBool == realm::type_Bool, ""); +static_assert((int)RLMPropertyTypeFloat == realm::type_Float, ""); +static_assert((int)RLMPropertyTypeDouble == realm::type_Double, ""); +static_assert((int)RLMPropertyTypeString == realm::type_String, ""); +static_assert((int)RLMPropertyTypeData == realm::type_Binary, ""); +static_assert((int)RLMPropertyTypeDate == realm::type_Timestamp, ""); +static_assert((int)RLMPropertyTypeObject == realm::type_Link, ""); +static_assert((int)RLMPropertyTypeArray == realm::type_LinkList, ""); + +BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType) { + return propertyType != RLMPropertyTypeArray && propertyType != RLMPropertyTypeLinkingObjects; +} + +BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) { + return propertyType == RLMPropertyTypeLinkingObjects; +} + +// Swift obeys the ARC naming conventions for method families (except for init) +// but the end result doesn't really work (using KVC on a method returning a +// retained value results in a leak, but not returning a retained value results +// in crashes). Objective-C makes properties with naming fitting the method +// families a compile error, so we just disallow them in Swift as well. +// http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families +void RLMValidateSwiftPropertyName(NSString *name) { + // To belong to a method family, the property name must begin with the family + // name followed by a non-lowercase letter (or nothing), with an optional + // leading underscore + const char *str = name.UTF8String; + if (str[0] == '_') + ++str; + auto nameSize = strlen(str); + + // Note that "init" is deliberately not in this list because Swift does not + // infer family membership for it. + for (auto family : {"alloc", "new", "copy", "mutableCopy"}) { + auto familySize = strlen(family); + if (nameSize < familySize || !std::equal(str, str + familySize, family)) { + continue; + } + if (familySize == nameSize || !islower(str[familySize])) { + @throw RLMException(@"Property names beginning with '%s' are not " + "supported. Swift follows ARC's ownership " + "rules for methods based on their name, which " + "results in memory leaks when accessing " + "properties which return retained values via KVC.", + family); + } + return; + } +} + +static bool rawTypeIsComputedProperty(NSString *rawType) { + return [rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"]; +} + +@implementation RLMProperty + ++ (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop { + return [[RLMProperty alloc] initWithName:@(prop.name.c_str()) + type:(RLMPropertyType)prop.type + objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil + linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil + indexed:prop.is_indexed + optional:prop.is_nullable]; +} + +- (instancetype)initWithName:(NSString *)name + type:(RLMPropertyType)type + objectClassName:(NSString *)objectClassName + linkOriginPropertyName:(NSString *)linkOriginPropertyName + indexed:(BOOL)indexed + optional:(BOOL)optional { + self = [super init]; + if (self) { + _name = name; + _type = type; + _objectClassName = objectClassName; + _linkOriginPropertyName = linkOriginPropertyName; + _indexed = indexed; + _optional = optional; + [self updateAccessors]; + } + + return self; +} + +- (void)setName:(NSString *)name { + _name = name; + [self updateAccessors]; +} + +- (void)updateAccessors { + // populate getter/setter names if generic + if (!_getterName) { + _getterName = _name; + } + if (!_setterName) { + // Objective-C setters only capitalize the first letter of the property name if it falls between 'a' and 'z' + int asciiCode = [_name characterAtIndex:0]; + BOOL shouldUppercase = asciiCode >= 'a' && asciiCode <= 'z'; + NSString *firstChar = [_name substringToIndex:1]; + firstChar = shouldUppercase ? firstChar.uppercaseString : firstChar; + _setterName = [NSString stringWithFormat:@"set%@%@:", firstChar, [_name substringFromIndex:1]]; + } + + _getterSel = NSSelectorFromString(_getterName); + _setterSel = NSSelectorFromString(_setterName); +} + +// determine RLMPropertyType from objc code - returns true if valid type was found/set +- (BOOL)setTypeFromRawType:(NSString *)rawType { + const char *code = rawType.UTF8String; + switch (*code) { + case 's': // short + case 'i': // int + case 'l': // long + case 'q': // long long + _type = RLMPropertyTypeInt; + return YES; + case 'f': + _type = RLMPropertyTypeFloat; + return YES; + case 'd': + _type = RLMPropertyTypeDouble; + return YES; + case 'c': // BOOL is stored as char - since rlm has no char type this is ok + case 'B': + _type = RLMPropertyTypeBool; + return YES; + case '@': + break; + default: + return NO; + } + + _optional = true; + static const char arrayPrefix[] = "@\"RLMArray<"; + static const int arrayPrefixLen = sizeof(arrayPrefix) - 1; + + static const char numberPrefix[] = "@\"NSNumber<"; + static const int numberPrefixLen = sizeof(numberPrefix) - 1; + + static const char linkingObjectsPrefix[] = "@\"RLMLinkingObjects"; + static const int linkingObjectsPrefixLen = sizeof(linkingObjectsPrefix) - 1; + + if (strcmp(code, "@\"NSString\"") == 0) { + _type = RLMPropertyTypeString; + } + else if (strcmp(code, "@\"NSDate\"") == 0) { + _type = RLMPropertyTypeDate; + } + else if (strcmp(code, "@\"NSData\"") == 0) { + _type = RLMPropertyTypeData; + } + else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) { + _optional = false; + // get object class from type string - @"RLMArray" + _type = RLMPropertyTypeArray; + _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen + length:strlen(code + arrayPrefixLen) - 2 // drop trailing >" + encoding:NSUTF8StringEncoding]; + + Class cls = [RLMSchema classForString:_objectClassName]; + if (!cls) { + @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. " + @"RLMArrays can only contain instances of RLMObject subclasses. " + @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName); + } + } + else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) { + // get number type from type string - @"NSNumber" + NSString *numberType = [[NSString alloc] initWithBytes:code + numberPrefixLen + length:strlen(code + numberPrefixLen) - 2 // drop trailing >" + encoding:NSUTF8StringEncoding]; + + if ([numberType isEqualToString:@"RLMInt"]) { + _type = RLMPropertyTypeInt; + } + else if ([numberType isEqualToString:@"RLMFloat"]) { + _type = RLMPropertyTypeFloat; + } + else if ([numberType isEqualToString:@"RLMDouble"]) { + _type = RLMPropertyTypeDouble; + } + else if ([numberType isEqualToString:@"RLMBool"]) { + _type = RLMPropertyTypeBool; + } + else { + @throw RLMException(@"Property '%@' is of type 'NSNumber<%@>' which is not a supported NSNumber object type. " + @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. " + @"See https://realm.io/docs/objc/latest for more information.", _name, numberType); + } + } + else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 && + (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) { + _type = RLMPropertyTypeLinkingObjects; + _optional = false; + + if (!_objectClassName || !_linkOriginPropertyName) { + @throw RLMException(@"Property '%@' is of type RLMLinkingObjects but +linkingObjectsProperties did not specify the class " + "or property that is the origin of the link.", _name); + } + + // If the property was declared with a protocol indicating the contained type, validate that it matches + // the class from the dictionary returned by +linkingObjectsProperties. + if (code[linkingObjectsPrefixLen] == '<') { + NSString *classNameFromProtocol = [[NSString alloc] initWithBytes:code + linkingObjectsPrefixLen + 1 + length:strlen(code + linkingObjectsPrefixLen) - 3 // drop trailing >" + encoding:NSUTF8StringEncoding]; + if (![_objectClassName isEqualToString:classNameFromProtocol]) { + @throw RLMException(@"Property '%@' was declared with type RLMLinkingObjects<%@>, but a conflicting " + "class name of '%@' was returned by +linkingObjectsProperties.", _name, + classNameFromProtocol, _objectClassName); + } + } + } + else if (strcmp(code, "@\"NSNumber\"") == 0) { + @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: NSNumber.", _name); + } + else if (strcmp(code, "@\"RLMArray\"") == 0) { + @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray.", _name); + } + else { + NSString *className; + Class cls = nil; + if (code[1] == '\0') { + className = @"id"; + } + else { + // for objects strip the quotes and @ + className = [rawType substringWithRange:NSMakeRange(2, rawType.length-3)]; + cls = [RLMSchema classForString:className]; + } + + if (!cls) { + @throw RLMException(@"Property '%@' is declared as '%@', which is not a supported RLMObject property type. " + @"All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, RLMLinkingObjects, or subclasses of RLMObject. " + @"See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.", _name, className); + } + + _type = RLMPropertyTypeObject; + _optional = true; + _objectClassName = [cls className] ?: className; + } + return YES; +} + +- (void)parseObjcProperty:(objc_property_t)property readOnly:(bool *)readOnly rawType:(NSString **)rawType { + unsigned int count; + objc_property_attribute_t *attrs = property_copyAttributeList(property, &count); + + for (size_t i = 0; i < count; ++i) { + switch (*attrs[i].name) { + case 'T': + *rawType = @(attrs[i].value); + break; + case 'R': + *readOnly = true; + break; + case 'N': + // nonatomic + break; + case 'D': + // dynamic + break; + case 'G': + _getterName = @(attrs[i].value); + break; + case 'S': + _setterName = @(attrs[i].value); + break; + default: + break; + } + } + free(attrs); +} + +- (instancetype)initSwiftPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property + instance:(RLMObject *)obj { + self = [super init]; + if (!self) { + return nil; + } + + RLMValidateSwiftPropertyName(name); + + _name = name; + _indexed = indexed; + + if (linkPropertyDescriptor) { + _objectClassName = [linkPropertyDescriptor.objectClass className]; + _linkOriginPropertyName = linkPropertyDescriptor.propertyName; + } + + NSString *rawType; + bool readOnly = false; + [self parseObjcProperty:property readOnly:&readOnly rawType:&rawType]; + if (readOnly) { + return nil; + } + + id propertyValue = [obj valueForKey:_name]; + + // FIXME: temporarily workaround added since Objective-C generics used in Swift show up as `@` + // * broken starting in Swift 3.0 Xcode 8 b1 + // * tested to still be broken in Swift 3.0 Xcode 8 b6 + // * if the Realm Objective-C Swift tests pass with this removed, it's been fixed + // * once it has been fixed, remove this entire conditional block (contents included) entirely + // * Bug Report: SR-2031 https://bugs.swift.org/browse/SR-2031 + if ([rawType isEqualToString:@"@"]) { + if (propertyValue) { + rawType = [NSString stringWithFormat:@"@\"%@\"", [propertyValue class]]; + } else if (linkPropertyDescriptor) { + // we're going to naively assume that the user used the correct type since we can't check it + rawType = @"@\"RLMLinkingObjects\""; + } + } + + // convert array types to objc variant + if ([rawType isEqualToString:@"@\"RLMArray\""]) { + rawType = [NSString stringWithFormat:@"@\"RLMArray<%@>\"", [propertyValue objectClassName]]; + } + else if ([rawType isEqualToString:@"@\"NSNumber\""]) { + const char *numberType = [propertyValue objCType]; + if (!numberType) { + @throw RLMException(@"Can't persist NSNumber without default value: use a Swift-native number type or provide a default value."); + } + switch (*numberType) { + case 'i': + case 'l': + case 'q': + rawType = @"@\"NSNumber\""; + break; + case 'f': + rawType = @"@\"NSNumber\""; + break; + case 'd': + rawType = @"@\"NSNumber\""; + break; + case 'B': + case 'c': + rawType = @"@\"NSNumber\""; + break; + default: + @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType); + } + } + + auto throwForPropertyName = ^(NSString *propertyName){ + @throw RLMException(@"Can't persist property '%@' with incompatible type. " + "Add to Object.ignoredProperties() class method to ignore.", + propertyName); + }; + + if (![self setTypeFromRawType:rawType]) { + throwForPropertyName(self.name); + } + + if ([rawType isEqualToString:@"c"]) { + // Check if it's a BOOL or Int8 by trying to set it to 2 and seeing if + // it actually sets it to 1. + [obj setValue:@2 forKey:name]; + NSNumber *value = [obj valueForKey:name]; + _type = value.intValue == 2 ? RLMPropertyTypeInt : RLMPropertyTypeBool; + } + + // update getter/setter names + [self updateAccessors]; + + return self; +} + +- (instancetype)initWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property +{ + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _indexed = indexed; + + if (linkPropertyDescriptor) { + _objectClassName = [linkPropertyDescriptor.objectClass className]; + _linkOriginPropertyName = linkPropertyDescriptor.propertyName; + } + + NSString *rawType; + bool isReadOnly = false; + [self parseObjcProperty:property readOnly:&isReadOnly rawType:&rawType]; + bool isComputedProperty = rawTypeIsComputedProperty(rawType); + if (isReadOnly && !isComputedProperty) { + return nil; + } + + if (![self setTypeFromRawType:rawType]) { + @throw RLMException(@"Can't persist property '%@' with incompatible type. " + "Add to ignoredPropertyNames: method to ignore.", self.name); + } + + if (!isReadOnly && isComputedProperty) { + @throw RLMException(@"Property '%@' must be declared as readonly as %@ properties cannot be written to.", + self.name, RLMTypeToString(_type)); + } + + // update getter/setter names + [self updateAccessors]; + + return self; +} + +- (instancetype)initSwiftListPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(NSString *)objectClassName { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = RLMPropertyTypeArray; + _objectClassName = objectClassName; + _swiftIvar = ivar; + + // no obj-c property for generic lists, and thus no getter/setter names + + return self; +} + +- (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + ivar:(Ivar)ivar + propertyType:(RLMPropertyType)propertyType { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = propertyType; + _indexed = indexed; + _swiftIvar = ivar; + _optional = true; + + // no obj-c property for generic optionals, and thus no getter/setter names + + return self; +} + +- (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(NSString *)objectClassName + linkOriginPropertyName:(NSString *)linkOriginPropertyName { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = RLMPropertyTypeLinkingObjects; + _objectClassName = objectClassName; + _linkOriginPropertyName = linkOriginPropertyName; + _swiftIvar = ivar; + + // no obj-c property for generic linking objects properties, and thus no getter/setter names + + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMProperty *prop = [[RLMProperty allocWithZone:zone] init]; + prop->_name = _name; + prop->_type = _type; + prop->_objectClassName = _objectClassName; + prop->_indexed = _indexed; + prop->_getterName = _getterName; + prop->_setterName = _setterName; + prop->_getterSel = _getterSel; + prop->_setterSel = _setterSel; + prop->_isPrimary = _isPrimary; + prop->_swiftIvar = _swiftIvar; + prop->_optional = _optional; + prop->_linkOriginPropertyName = _linkOriginPropertyName; + + return prop; +} + +- (RLMProperty *)copyWithNewName:(NSString *)name { + RLMProperty *prop = [self copy]; + prop.name = name; + return prop; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMProperty class]]) { + return NO; + } + + return [self isEqualToProperty:object]; +} + +- (BOOL)isEqualToProperty:(RLMProperty *)property { + return _type == property->_type + && _indexed == property->_indexed + && _isPrimary == property->_isPrimary + && _optional == property->_optional + && [_name isEqualToString:property->_name] + && (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName]) + && (_linkOriginPropertyName == property->_linkOriginPropertyName || [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ {\n\ttype = %@;\n\tobjectClassName = %@;\n\tlinkOriginPropertyName = %@;\n\tindexed = %@;\n\tisPrimary = %@;\n\toptional = %@;\n}", self.name, RLMTypeToString(self.type), self.objectClassName, self.linkOriginPropertyName, self.indexed ? @"YES" : @"NO", self.isPrimary ? @"YES" : @"NO", self.optional ? @"YES" : @"NO"]; +} + +- (realm::Property)objectStoreCopy { + realm::Property p; + p.name = _name.UTF8String; + p.type = (realm::PropertyType)_type; + p.object_type = _objectClassName ? _objectClassName.UTF8String : ""; + p.is_indexed = _indexed; + p.is_nullable = _optional; + p.link_origin_property_name = _linkOriginPropertyName ? _linkOriginPropertyName.UTF8String : ""; + return p; +} + +@end + +@implementation RLMPropertyDescriptor + ++ (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName +{ + RLMPropertyDescriptor *descriptor = [[RLMPropertyDescriptor alloc] init]; + descriptor->_objectClass = objectClass; + descriptor->_propertyName = propertyName; + return descriptor; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMQueryUtil.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMQueryUtil.mm new file mode 100644 index 0000000..1290147 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMQueryUtil.mm @@ -0,0 +1,1557 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMQueryUtil.hpp" + +#import "RLMArray.h" +#import "RLMObjectSchema_Private.h" +#import "RLMObject_Private.hpp" +#import "RLMPredicateUtil.hpp" +#import "RLMProperty_Private.h" +#import "RLMSchema.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "results.hpp" + +#include +#include +#include + +using namespace realm; + +NSString * const RLMPropertiesComparisonTypeMismatchException = @"RLMPropertiesComparisonTypeMismatchException"; +NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException = @"RLMUnsupportedTypesFoundInPropertyComparisonException"; + +NSString * const RLMPropertiesComparisonTypeMismatchReason = @"Property type mismatch between %@ and %@"; +NSString * const RLMUnsupportedTypesFoundInPropertyComparisonReason = @"Comparison between %@ and %@"; + +// small helper to create the many exceptions thrown when parsing predicates +static NSException *RLMPredicateException(NSString *name, NSString *format, ...) { + va_list args; + va_start(args, format); + NSString *reason = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + return [NSException exceptionWithName:name reason:reason userInfo:nil]; +} + +// check a precondition and throw an exception if it is not met +// this should be used iff the condition being false indicates a bug in the caller +// of the function checking its preconditions +static void RLMPrecondition(bool condition, NSString *name, NSString *format, ...) { + if (__builtin_expect(condition, 1)) { + return; + } + + va_list args; + va_start(args, format); + NSString *reason = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + @throw [NSException exceptionWithName:name reason:reason userInfo:nil]; +} + +// return the property for a validated column name +RLMProperty *RLMValidatedProperty(RLMObjectSchema *desc, NSString *columnName) { + RLMProperty *prop = desc[columnName]; + RLMPrecondition(prop, @"Invalid property name", + @"Property '%@' not found in object of type '%@'", columnName, desc.className); + return prop; +} + +namespace { +BOOL RLMPropertyTypeIsNumeric(RLMPropertyType propertyType) { + switch (propertyType) { + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + return YES; + default: + return NO; + } +} + + +// Declare an overload set using lambdas or other function objects. +// A minimal version of C++ Library Evolution Working Group proposal P0051R2. +// FIXME: Switch to realm::util::overload once https://github.com/realm/realm-core/pull/2539 is in a core release. + +template +struct Overloaded : Fn, Overloaded { + template + Overloaded(U&& fn, Rest&&... rest) : Fn(std::forward(fn)), Overloaded(std::forward(rest)...) { } + + using Fn::operator(); + using Overloaded::operator(); +}; + +template +struct Overloaded : Fn { + template + Overloaded(U&& fn) : Fn(std::forward(fn)) { } + + using Fn::operator(); +}; + +template +Overloaded overload(Fns&&... f) +{ + return Overloaded(std::forward(f)...); +} + + +// FIXME: TrueExpression and FalseExpression should be supported by core in some way + +struct TrueExpression : realm::Expression { + size_t find_first(size_t start, size_t end) const override + { + if (start != end) + return start; + + return realm::not_found; + } + void set_base_table(const Table*) override {} + void verify_column() const override {} + const Table* get_base_table() const override { return nullptr; } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new TrueExpression(*this)); + } +}; + +struct FalseExpression : realm::Expression { + size_t find_first(size_t, size_t) const override { return realm::not_found; } + void set_base_table(const Table*) override {} + void verify_column() const override {} + const Table* get_base_table() const override { return nullptr; } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new FalseExpression(*this)); + } +}; + + +// Equal and ContainsSubstring are used by QueryBuilder::add_string_constraint as the comparator +// for performing diacritic-insensitive comparisons. + +bool equal(CFStringCompareFlags options, StringData v1, StringData v2) +{ + if (v1.is_null() || v2.is_null()) { + return v1.is_null() == v2.is_null(); + } + + auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + + return CFStringCompare(s1.get(), s2.get(), options) == kCFCompareEqualTo; +} + +template +struct Equal { + using CaseSensitive = Equal; + using CaseInsensitive = Equal; + + bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const + { + REALM_ASSERT_DEBUG(v1_null == v1.is_null()); + REALM_ASSERT_DEBUG(v2_null == v2.is_null()); + + return equal(options, v1, v2); + } +}; + +bool contains_substring(CFStringCompareFlags options, StringData v1, StringData v2) +{ + if (v2.is_null()) { + // Everything contains NULL + return true; + } + + if (v1.is_null()) { + // NULL contains nothing (except NULL, handled above) + return false; + } + + if (v2.size() == 0) { + // Everything (except NULL, handled above) contains the empty string + return true; + } + + auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + + return CFStringFind(s1.get(), s2.get(), options).location != kCFNotFound; +} + +template +struct ContainsSubstring { + using CaseSensitive = ContainsSubstring; + using CaseInsensitive = ContainsSubstring; + + bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const + { + REALM_ASSERT_DEBUG(v1_null == v1.is_null()); + REALM_ASSERT_DEBUG(v2_null == v2.is_null()); + + return contains_substring(options, v1, v2); + } +}; + + +NSString *operatorName(NSPredicateOperatorType operatorType) +{ + switch (operatorType) { + case NSLessThanPredicateOperatorType: + return @"<"; + case NSLessThanOrEqualToPredicateOperatorType: + return @"<="; + case NSGreaterThanPredicateOperatorType: + return @">"; + case NSGreaterThanOrEqualToPredicateOperatorType: + return @">="; + case NSEqualToPredicateOperatorType: + return @"=="; + case NSNotEqualToPredicateOperatorType: + return @"!="; + case NSMatchesPredicateOperatorType: + return @"MATCHES"; + case NSLikePredicateOperatorType: + return @"LIKE"; + case NSBeginsWithPredicateOperatorType: + return @"BEGINSWITH"; + case NSEndsWithPredicateOperatorType: + return @"ENDSWITH"; + case NSInPredicateOperatorType: + return @"IN"; + case NSContainsPredicateOperatorType: + return @"CONTAINS"; + case NSBetweenPredicateOperatorType: + return @"BETWEEN"; + case NSCustomSelectorPredicateOperatorType: + return @"custom selector"; + } + + return [NSString stringWithFormat:@"unknown operator %lu", (unsigned long)operatorType]; +} + +Table& get_table(Group& group, RLMObjectSchema *objectSchema) +{ + return *ObjectStore::table_for_object_type(group, objectSchema.objectName.UTF8String); +} + +// A reference to a column within a query. Can be resolved to a Columns for use in query expressions. +class ColumnReference { +public: + ColumnReference(Query& query, Group& group, RLMSchema *schema, RLMProperty* property, const std::vector& links = {}) + : m_links(links), m_property(property), m_schema(schema), m_group(&group), m_query(&query), m_table(query.get_table().get()) + { + auto& table = walk_link_chain([](Table&, size_t, RLMPropertyType) { }); + m_index = table.get_column_index(m_property.name.UTF8String); + } + + template + auto resolve(SubQuery&&... subquery) const + { + static_assert(sizeof...(SubQuery) < 2, "resolve() takes at most one subquery"); + set_link_chain_on_table(); + if (type() != RLMPropertyTypeLinkingObjects) { + return m_table->template column(index(), std::forward(subquery)...); + } + else { + return resolve_backlink(std::forward(subquery)...); + } + } + + RLMProperty *property() const { return m_property; } + size_t index() const { return m_index; } + RLMPropertyType type() const { return property().type; } + Group& group() const { return *m_group; } + + RLMObjectSchema *link_target_object_schema() const + { + switch (type()) { + case RLMPropertyTypeObject: + case RLMPropertyTypeArray: + case RLMPropertyTypeLinkingObjects: + return m_schema[property().objectClassName]; + default: + REALM_ASSERT(false); + } + } + + bool has_links() const { return m_links.size(); } + + bool has_any_to_many_links() const { + return std::any_of(begin(m_links), end(m_links), [](RLMProperty *property) { + return property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects; + }); + } + + ColumnReference last_link_column() const { + REALM_ASSERT(!m_links.empty()); + return {*m_query, *m_group, m_schema, m_links.back(), {m_links.begin(), m_links.end() - 1}}; + } + + ColumnReference column_ignoring_links(Query& query) const { + return {query, *m_group, m_schema, m_property}; + } + +private: + template + auto resolve_backlink(SubQuery&&... subquery) const + { + // We actually just want `if constexpr (std::is_same::value) { ... }`, + // so fake it by tag-dispatching on the conditional + return do_resolve_backlink(std::is_same(), std::forward(subquery)...); + } + + template + auto do_resolve_backlink(std::true_type, SubQuery&&... subquery) const + { + return with_link_origin(m_property, [&](Table& table, size_t col) { + return m_table->template column(table, col, std::forward(subquery)...); + }); + } + + template + Columns do_resolve_backlink(std::false_type, SubQuery&&...) const + { + // This can't actually happen as we only call resolve_backlink() if + // it's RLMPropertyTypeLinkingObjects + __builtin_unreachable(); + } + + template + Table& walk_link_chain(Func&& func) const + { + auto table = m_query->get_table().get(); + for (const auto& link : m_links) { + if (link.type != RLMPropertyTypeLinkingObjects) { + auto index = table->get_column_index(link.name.UTF8String); + func(*table, index, link.type); + table = table->get_link_target(index).get(); + } + else { + with_link_origin(link, [&](Table& link_origin_table, size_t link_origin_column) { + func(link_origin_table, link_origin_column, link.type); + table = &link_origin_table; + }); + } + } + return *table; + } + + template + auto with_link_origin(RLMProperty *prop, Func&& func) const + { + RLMObjectSchema *link_origin_schema = m_schema[prop.objectClassName]; + Table& link_origin_table = get_table(*m_group, link_origin_schema); + size_t link_origin_column = link_origin_table.get_column_index(prop.linkOriginPropertyName.UTF8String); + return func(link_origin_table, link_origin_column); + } + + void set_link_chain_on_table() const + { + walk_link_chain([&](Table& current_table, size_t column, RLMPropertyType type) { + if (type == RLMPropertyTypeLinkingObjects) { + m_table->backlink(current_table, column); + } + else { + m_table->link(column); + } + }); + } + + std::vector m_links; + RLMProperty *m_property; + RLMSchema *m_schema; + Group *m_group; + Query *m_query; + Table *m_table; + size_t m_index; +}; + +class CollectionOperation { +public: + enum Type { + Count, + Minimum, + Maximum, + Sum, + Average, + }; + + CollectionOperation(Type type, ColumnReference link_column, util::Optional column) + : m_type(type) + , m_link_column(std::move(link_column)) + , m_column(std::move(column)) + { + RLMPrecondition(m_link_column.type() == RLMPropertyTypeArray || m_link_column.type() == RLMPropertyTypeLinkingObjects, + @"Invalid predicate", @"Collection operation can only be applied to a property of type RLMArray."); + + switch (m_type) { + case Count: + RLMPrecondition(!m_column, @"Invalid predicate", @"Result of @count does not have any properties."); + break; + case Minimum: + case Maximum: + case Sum: + case Average: + RLMPrecondition(m_column && RLMPropertyTypeIsNumeric(m_column->type()), @"Invalid predicate", + @"%@ can only be applied to a numeric property.", name_for_type(m_type)); + break; + } + } + + CollectionOperation(NSString *operationName, ColumnReference link_column, util::Optional column = util::none) + : CollectionOperation(type_for_name(operationName), std::move(link_column), std::move(column)) + { + } + + Type type() const { return m_type; } + const ColumnReference& link_column() const { return m_link_column; } + const ColumnReference& column() const { return *m_column; } + + void validate_comparison(id value) const { + switch (m_type) { + case Count: + case Average: + RLMPrecondition([value isKindOfClass:[NSNumber class]], @"Invalid operand", + @"%@ can only be compared with a numeric value.", name_for_type(m_type)); + break; + case Minimum: + case Maximum: + case Sum: + RLMPrecondition(RLMIsObjectValidForProperty(value, m_column->property()), @"Invalid operand", + @"%@ on a property of type %@ cannot be compared with '%@'", + name_for_type(m_type), RLMTypeToString(m_column->type()), value); + break; + } + } + + void validate_comparison(const ColumnReference& column) const { + switch (m_type) { + case Count: + RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand", + @"%@ can only be compared with a numeric value.", name_for_type(m_type)); + break; + case Average: + case Minimum: + case Maximum: + case Sum: + RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand", + @"%@ on a property of type %@ cannot be compared with property of type '%@'", + name_for_type(m_type), RLMTypeToString(m_column->type()), RLMTypeToString(column.type())); + break; + } + } + +private: + static Type type_for_name(NSString *name) { + if ([name isEqualToString:@"@count"]) { + return Count; + } + if ([name isEqualToString:@"@min"]) { + return Minimum; + } + if ([name isEqualToString:@"@max"]) { + return Maximum; + } + if ([name isEqualToString:@"@sum"]) { + return Sum; + } + if ([name isEqualToString:@"@avg"]) { + return Average; + } + @throw RLMPredicateException(@"Invalid predicate", @"Unsupported collection operation '%@'", name); + } + + static NSString *name_for_type(Type type) { + switch (type) { + case Count: return @"@count"; + case Minimum: return @"@min"; + case Maximum: return @"@max"; + case Sum: return @"@sum"; + case Average: return @"@avg"; + } + } + + Type m_type; + ColumnReference m_link_column; + util::Optional m_column; +}; + +class QueryBuilder { +public: + QueryBuilder(Query& query, Group& group, RLMSchema *schema) + : m_query(query), m_group(group), m_schema(schema) { } + + void apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema); + + + void apply_collection_operator_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred); + void apply_value_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred); + void apply_column_expression(RLMObjectSchema *desc, NSString *leftKeyPath, NSString *rightKeyPath, NSComparisonPredicate *predicate); + void apply_subquery_count_expression(RLMObjectSchema *objectSchema, NSExpression *subqueryExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + void apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + void apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + + + template + void add_numeric_constraint(RLMPropertyType datatype, + NSPredicateOperatorType operatorType, + A&& lhs, B&& rhs); + + template + void add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs); + + template + void add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + Columns &&column, + T value); + + void add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + StringData value, + Columns&& column); + + template + void add_constraint(RLMPropertyType type, + NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + L lhs, R rhs); + template + void do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, T... values); + void do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null); + + void add_between_constraint(const ColumnReference& column, id value); + + template + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, T value); + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value); + void add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column); + void add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&); + + void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, RLMObject *obj); + void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, realm::null); + template + void add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column); + void add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&); + + template + void add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values); + template + void add_collection_operation_constraint(NSPredicateOperatorType operatorType, + CollectionOperation collectionOperation, T... values); + + + CollectionOperation collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath); + ColumnReference column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPath, bool isAggregate); + +private: + Query& m_query; + Group& m_group; + RLMSchema *m_schema; +}; + +// add a clause for numeric constraints based on operator type +template +void QueryBuilder::add_numeric_constraint(RLMPropertyType datatype, + NSPredicateOperatorType operatorType, + A&& lhs, B&& rhs) +{ + switch (operatorType) { + case NSLessThanPredicateOperatorType: + m_query.and_query(lhs < rhs); + break; + case NSLessThanOrEqualToPredicateOperatorType: + m_query.and_query(lhs <= rhs); + break; + case NSGreaterThanPredicateOperatorType: + m_query.and_query(lhs > rhs); + break; + case NSGreaterThanOrEqualToPredicateOperatorType: + m_query.and_query(lhs >= rhs); + break; + case NSEqualToPredicateOperatorType: + m_query.and_query(lhs == rhs); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(lhs != rhs); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for type %@", operatorName(operatorType), RLMTypeToString(datatype)); + } +} + +template +void QueryBuilder::add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + m_query.and_query(lhs == rhs); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(lhs != rhs); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for bool type", operatorName(operatorType)); + } +} + +template +void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + Columns &&column, + T value) { + bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption); + bool diacriticSensitive = !(predicateOptions & NSDiacriticInsensitivePredicateOption); + + if (diacriticSensitive) { + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + m_query.and_query(column.begins_with(value, caseSensitive)); + break; + case NSEndsWithPredicateOperatorType: + m_query.and_query(column.ends_with(value, caseSensitive)); + break; + case NSContainsPredicateOperatorType: + m_query.and_query(column.contains(value, caseSensitive)); + break; + case NSEqualToPredicateOperatorType: + m_query.and_query(column.equal(value, caseSensitive)); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(column.not_equal(value, caseSensitive)); + break; + case NSLikePredicateOperatorType: + m_query.and_query(column.like(value, caseSensitive)); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for string type", operatorName(operatorType)); + } + return; + } + + auto as_subexpr = overload([](StringData value) { return make_subexpr(value); }, + [](const Columns& c) { return c.clone(); }); + auto left = as_subexpr(column); + auto right = as_subexpr(value); + + auto add_constraint = [&](auto comparator) mutable { + using Comparator = decltype(comparator); + using CompareCS = Compare; + using CompareCI = Compare; + + if (caseSensitive) { + m_query.and_query(make_expression(std::move(left), std::move(right))); + } + else { + m_query.and_query(make_expression(std::move(left), std::move(right))); + } + }; + + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + add_constraint(ContainsSubstring{}); + break; + case NSEndsWithPredicateOperatorType: + add_constraint(ContainsSubstring{}); + break; + case NSContainsPredicateOperatorType: + add_constraint(ContainsSubstring{}); + break; + case NSNotEqualToPredicateOperatorType: + m_query.Not(); + REALM_FALLTHROUGH; + case NSEqualToPredicateOperatorType: + add_constraint(Equal{}); + break; + case NSLikePredicateOperatorType: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator 'LIKE' not supported with diacritic-insensitive modifier."); + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for string type", operatorName(operatorType)); + } +} + +void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + StringData value, + Columns&& column) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + case NSNotEqualToPredicateOperatorType: + add_string_constraint(operatorType, predicateOptions, std::move(column), value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' is not supported for string type with key path on right side of operator", + operatorName(operatorType)); + } +} + +id value_from_constant_expression_or_value(id value) { + if (NSExpression *exp = RLMDynamicCast(value)) { + RLMPrecondition(exp.expressionType == NSConstantValueExpressionType, + @"Invalid value", + @"Expressions within predicate aggregates must be constant values"); + return exp.constantValue; + } + return value; +} + +void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, id *to) { + NSArray *array = RLMDynamicCast(value); + RLMPrecondition(array, @"Invalid value", @"object must be of type NSArray for BETWEEN operations"); + RLMPrecondition(array.count == 2, @"Invalid value", @"NSArray object must contain exactly two objects for BETWEEN operations"); + + *from = value_from_constant_expression_or_value(array.firstObject); + *to = value_from_constant_expression_or_value(array.lastObject); + RLMPrecondition(RLMIsObjectValidForProperty(*from, prop) && RLMIsObjectValidForProperty(*to, prop), + @"Invalid value", + @"NSArray objects must be of type %@ for BETWEEN operations", RLMTypeToString(prop.type)); +} + +void QueryBuilder::add_between_constraint(const ColumnReference& column, id value) { + if (column.has_any_to_many_links()) { + auto link_column = column.last_link_column(); + Query subquery = get_table(m_group, link_column.link_target_object_schema()).where(); + QueryBuilder(subquery, m_group, m_schema).add_between_constraint(column.column_ignoring_links(subquery), value); + + m_query.and_query(link_column.resolve(std::move(subquery)).count() > 0); + return; + } + + id from, to; + validate_and_extract_between_range(value, column.property(), &from, &to); + + RLMPropertyType type = column.type(); + + m_query.group(); + add_constraint(type, NSGreaterThanOrEqualToPredicateOperatorType, 0, column, from); + add_constraint(type, NSLessThanOrEqualToPredicateOperatorType, 0, column, to); + m_query.end_group(); +} + +template +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, + T value) { + RLMPrecondition(!column.has_links(), @"Unsupported operator", @"NSData properties cannot be queried over an object link."); + + size_t index = column.index(); + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + m_query.begins_with(index, value); + break; + case NSEndsWithPredicateOperatorType: + m_query.ends_with(index, value); + break; + case NSContainsPredicateOperatorType: + m_query.contains(index, value); + break; + case NSEqualToPredicateOperatorType: + m_query.equal(index, value); + break; + case NSNotEqualToPredicateOperatorType: + m_query.not_equal(index, value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for binary type", operatorName(operatorType)); + } +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value) { + add_binary_constraint(operatorType, column, RLMBinaryDataForNSData(value)); +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + case NSNotEqualToPredicateOperatorType: + add_binary_constraint(operatorType, column, value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' is not supported for binary type with key path on right side of operator", + operatorName(operatorType)); + } +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) { + @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two NSData properties are not supported"); +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, RLMObject *obj) { + RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType, + @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison"); + + if (operatorType == NSEqualToPredicateOperatorType) { + m_query.and_query(column.resolve() == obj->_row); + } + else { + m_query.and_query(column.resolve() != obj->_row); + } +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, + realm::null) { + RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType, + @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison"); + + if (operatorType == NSEqualToPredicateOperatorType) { + m_query.and_query(column.resolve() == null()); + } + else { + m_query.and_query(column.resolve() != null()); + } +} + +template +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column) { + // Link constraints only support the equal-to and not-equal-to operators. The order of operands + // is not important for those comparisons so we can delegate to the other implementation. + add_link_constraint(operatorType, column, obj); +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) { + // This is not actually reachable as this case is caught earlier, but this + // overload is needed for the code to compile + @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two RLMArray properties are not supported"); +} + + +// iterate over an array of subpredicates, using @func to build a query from each +// one and ORing them together +template +void process_or_group(Query &query, id array, Func&& func) { + RLMPrecondition([array conformsToProtocol:@protocol(NSFastEnumeration)], + @"Invalid value", @"IN clause requires an array of items"); + + query.group(); + + bool first = true; + for (id item in array) { + if (!first) { + query.Or(); + } + first = false; + + func(item); + } + + if (first) { + // Queries can't be empty, so if there's zero things in the OR group + // validation will fail. Work around this by adding an expression which + // will never find any rows in a table. + query.and_query(std::unique_ptr(new FalseExpression)); + } + + query.end_group(); +} + +template +RequestedType convert(id value); + +template <> +Timestamp convert(id value) { + return RLMTimestampForNSDate(value); +} + +template <> +bool convert(id value) { + return [value boolValue]; +} + +template <> +Double convert(id value) { + return [value doubleValue]; +} + +template <> +Float convert(id value) { + return [value floatValue]; +} + +template <> +Int convert(id value) { + return [value longLongValue]; +} + +template <> +String convert(id value) { + return RLMStringDataWithNSString(value); +} + +template +realm::null value_of_type(realm::null) { + return realm::null(); +} + +template +auto value_of_type(id value) { + return ::convert(value); +} + +template +auto value_of_type(const ColumnReference& column) { + return column.resolve(); +} + + +template +void QueryBuilder::do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, T... values) +{ + static_assert(sizeof...(T) == 2, "do_add_constraint accepts only two values as arguments"); + + switch (type) { + case RLMPropertyTypeBool: + add_bool_constraint(operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeDate: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeDouble: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeFloat: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeInt: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeString: + add_string_constraint(operatorType, predicateOptions, value_of_type(values)...); + break; + case RLMPropertyTypeData: + add_binary_constraint(operatorType, values...); + break; + case RLMPropertyTypeObject: + case RLMPropertyTypeArray: + case RLMPropertyTypeLinkingObjects: + add_link_constraint(operatorType, values...); + break; + default: + @throw RLMPredicateException(@"Unsupported predicate value type", + @"Object type %@ not supported", RLMTypeToString(type)); + } +} + +void QueryBuilder::do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null) +{ + // This is not actually reachable as this case is caught earlier, but this + // overload is needed for the code to compile + @throw RLMPredicateException(@"Invalid predicate expressions", + @"Predicate expressions must compare a keypath and another keypath or a constant value"); +} + +bool is_nsnull(id value) { + return !value || value == NSNull.null; +} + +template +bool is_nsnull(T) { + return false; +} + +template +void QueryBuilder::add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, L lhs, R rhs) +{ + // The expression operators are only overloaded for realm::null on the rhs + RLMPrecondition(!is_nsnull(lhs), @"Unsupported operator", + @"Nil is only supported on the right side of operators"); + + if (is_nsnull(rhs)) { + do_add_constraint(type, operatorType, predicateOptions, lhs, realm::null()); + } + else { + do_add_constraint(type, operatorType, predicateOptions, lhs, rhs); + } +} + +struct KeyPath { + std::vector links; + RLMProperty *property; + bool containsToManyRelationship; +}; + +KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, NSString *keyPath) +{ + RLMProperty *property; + std::vector links; + + bool keyPathContainsToManyRelationship = false; + + NSUInteger start = 0, length = keyPath.length, end = NSNotFound; + do { + end = [keyPath rangeOfString:@"." options:0 range:{start, length - start}].location; + NSString *propertyName = [keyPath substringWithRange:{start, end == NSNotFound ? length - start : end - start}]; + property = objectSchema[propertyName]; + RLMPrecondition(property, @"Invalid property name", + @"Property '%@' not found in object of type '%@'", propertyName, objectSchema.className); + + if (property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects) + keyPathContainsToManyRelationship = true; + + if (end != NSNotFound) { + RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects, + @"Invalid value", @"Property '%@' is not a link in object of type '%@'", propertyName, objectSchema.className); + + links.push_back(property); + REALM_ASSERT(property.objectClassName); + objectSchema = schema[property.objectClassName]; + } + + start = end + 1; + } while (end != NSNotFound); + + return {std::move(links), property, keyPathContainsToManyRelationship}; +} + +ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPathString, bool isAggregate) +{ + auto keyPath = key_path_from_string(m_schema, objectSchema, keyPathString); + + if (isAggregate && !keyPath.containsToManyRelationship) { + @throw RLMPredicateException(@"Invalid predicate", + @"Aggregate operations can only be used on key paths that include an array property"); + } else if (!isAggregate && keyPath.containsToManyRelationship) { + @throw RLMPredicateException(@"Invalid predicate", + @"Key paths that include an array property must use aggregate operations"); + } + + return ColumnReference(m_query, m_group, m_schema, keyPath.property, std::move(keyPath.links)); +} + +void validate_property_value(const ColumnReference& column, + __unsafe_unretained id const value, + __unsafe_unretained NSString *const err, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained NSString *const keyPath) { + RLMProperty *prop = column.property(); + if (prop.type == RLMPropertyTypeArray || prop.type == RLMPropertyTypeLinkingObjects) { + RLMPrecondition([RLMObjectBaseObjectSchema(RLMDynamicCast(value)).className isEqualToString:prop.objectClassName], + @"Invalid value", err, prop.objectClassName, keyPath, objectSchema.className, value); + } + else { + RLMPrecondition(RLMIsObjectValidForProperty(value, prop), + @"Invalid value", err, RLMTypeToString(prop.type), keyPath, objectSchema.className, value); + } + if (RLMObjectBase *obj = RLMDynamicCast(value)) { + RLMPrecondition(!obj->_row.is_attached() || &column.group() == &obj->_realm.group, + @"Invalid value origin", @"Object must be from the Realm being queried"); + } +} + +template +struct ValueOfTypeWithCollectionOperationHelper; + +template <> +struct ValueOfTypeWithCollectionOperationHelper { + static auto convert(const CollectionOperation& operation) + { + assert(operation.type() == CollectionOperation::Count); + return operation.link_column().resolve().count(); + } +}; + +#define VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(OperationType, function) \ +template \ +struct ValueOfTypeWithCollectionOperationHelper { \ + static auto convert(const CollectionOperation& operation) \ + { \ + REALM_ASSERT(operation.type() == OperationType); \ + auto targetColumn = operation.link_column().resolve().template column(operation.column().index()); \ + return targetColumn.function(); \ + } \ +} \ + +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Minimum, min); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Maximum, max); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Sum, sum); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Average, average); +#undef VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER + +template +auto value_of_type_with_collection_operation(T&& value) { + return value_of_type(std::forward(value)); +} + +template +auto value_of_type_with_collection_operation(CollectionOperation operation) { + using helper = ValueOfTypeWithCollectionOperationHelper; + return helper::convert(operation); +} + +template +void QueryBuilder::add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values) +{ + switch (propertyType) { + case RLMPropertyTypeInt: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + case RLMPropertyTypeFloat: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + case RLMPropertyTypeDouble: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + default: + REALM_ASSERT(false && "Only numeric property types should hit this path."); + } +} + +template +void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType, + CollectionOperation collectionOperation, T... values) +{ + static_assert(sizeof...(T) == 2, "add_collection_operation_constraint accepts only two values as arguments"); + + switch (collectionOperation.type()) { + case CollectionOperation::Count: + add_numeric_constraint(RLMPropertyTypeInt, operatorType, + value_of_type_with_collection_operation(values)...); + break; + case CollectionOperation::Minimum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Maximum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Sum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Average: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + } +} + +bool key_path_contains_collection_operator(NSString *keyPath) { + return [keyPath rangeOfString:@"@"].location != NSNotFound; +} + +NSString *get_collection_operation_name_from_key_path(NSString *keyPath, NSString **leadingKeyPath, NSString **trailingKey) { + NSRange at = [keyPath rangeOfString:@"@"]; + if (at.location == NSNotFound || at.location >= keyPath.length - 1) { + @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath); + } + + if (at.location == 0 || [keyPath characterAtIndex:at.location - 1] != '.') { + @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath); + } + + NSRange trailingKeyRange = [keyPath rangeOfString:@"." options:0 range:{at.location, keyPath.length - at.location} locale:nil]; + + *leadingKeyPath = [keyPath substringToIndex:at.location - 1]; + if (trailingKeyRange.location == NSNotFound) { + *trailingKey = nil; + return [keyPath substringFromIndex:at.location]; + } else { + *trailingKey = [keyPath substringFromIndex:trailingKeyRange.location + 1]; + return [keyPath substringWithRange:{at.location, trailingKeyRange.location - at.location}]; + } +} + +CollectionOperation QueryBuilder::collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath) { + NSString *leadingKeyPath; + NSString *trailingKey; + NSString *collectionOperationName = get_collection_operation_name_from_key_path(keyPath, &leadingKeyPath, &trailingKey); + + ColumnReference linkColumn = column_reference_from_key_path(desc, leadingKeyPath, true); + util::Optional column; + if (trailingKey) { + RLMPrecondition([trailingKey rangeOfString:@"."].location == NSNotFound, @"Invalid key path", + @"Right side of collection operator may only have a single level key"); + NSString *fullKeyPath = [leadingKeyPath stringByAppendingFormat:@".%@", trailingKey]; + column = column_reference_from_key_path(desc, fullKeyPath, true); + } + + return {collectionOperationName, std::move(linkColumn), std::move(column)}; +} + +void QueryBuilder::apply_collection_operator_expression(RLMObjectSchema *desc, + NSString *keyPath, id value, + NSComparisonPredicate *pred) { + CollectionOperation operation = collection_operation_from_key_path(desc, keyPath); + operation.validate_comparison(value); + + if (pred.leftExpression.expressionType == NSKeyPathExpressionType) { + add_collection_operation_constraint(pred.predicateOperatorType, operation, operation, value); + } else { + add_collection_operation_constraint(pred.predicateOperatorType, operation, value, operation); + } +} + +void QueryBuilder::apply_value_expression(RLMObjectSchema *desc, + NSString *keyPath, id value, + NSComparisonPredicate *pred) +{ + if (key_path_contains_collection_operator(keyPath)) { + apply_collection_operator_expression(desc, keyPath, value, pred); + return; + } + + bool isAny = pred.comparisonPredicateModifier == NSAnyPredicateModifier; + ColumnReference column = column_reference_from_key_path(desc, keyPath, isAny); + + // check to see if this is a between query + if (pred.predicateOperatorType == NSBetweenPredicateOperatorType) { + add_between_constraint(std::move(column), value); + return; + } + + // turn "key.path IN collection" into ored together ==. "collection IN key.path" is handled elsewhere. + if (pred.predicateOperatorType == NSInPredicateOperatorType) { + process_or_group(m_query, value, [&](id item) { + id normalized = value_from_constant_expression_or_value(item); + validate_property_value(column, normalized, + @"Expected object of type %@ in IN clause for property '%@' on object of type '%@', but received: %@", desc, keyPath); + add_constraint(column.type(), NSEqualToPredicateOperatorType, pred.options, column, normalized); + }); + return; + } + + validate_property_value(column, value, @"Expected object of type %@ for property '%@' on object of type '%@', but received: %@", desc, keyPath); + if (pred.leftExpression.expressionType == NSKeyPathExpressionType) { + add_constraint(column.type(), pred.predicateOperatorType, pred.options, std::move(column), value); + } else { + add_constraint(column.type(), pred.predicateOperatorType, pred.options, value, std::move(column)); + } +} + +void QueryBuilder::apply_column_expression(RLMObjectSchema *desc, + NSString *leftKeyPath, NSString *rightKeyPath, + NSComparisonPredicate *predicate) +{ + bool left_key_path_contains_collection_operator = key_path_contains_collection_operator(leftKeyPath); + bool right_key_path_contains_collection_operator = key_path_contains_collection_operator(rightKeyPath); + if (left_key_path_contains_collection_operator && right_key_path_contains_collection_operator) { + @throw RLMPredicateException(@"Unsupported predicate", @"Key paths including aggregate operations cannot be compared with other aggregate operations."); + } + + if (left_key_path_contains_collection_operator) { + CollectionOperation left = collection_operation_from_key_path(desc, leftKeyPath); + ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, false); + left.validate_comparison(right); + add_collection_operation_constraint(predicate.predicateOperatorType, left, left, std::move(right)); + return; + } + if (right_key_path_contains_collection_operator) { + ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, false); + CollectionOperation right = collection_operation_from_key_path(desc, rightKeyPath); + right.validate_comparison(left); + add_collection_operation_constraint(predicate.predicateOperatorType, right, std::move(left), right); + return; + } + + bool isAny = false; + ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, isAny); + ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, isAny); + + // NOTE: It's assumed that column type must match and no automatic type conversion is supported. + RLMPrecondition(left.type() == right.type(), + RLMPropertiesComparisonTypeMismatchException, + RLMPropertiesComparisonTypeMismatchReason, + RLMTypeToString(left.type()), + RLMTypeToString(right.type())); + + // TODO: Should we handle special case where left row is the same as right row (tautology) + add_constraint(left.type(), predicate.predicateOperatorType, predicate.options, + std::move(left), std::move(right)); +} + +// Identify expressions of the form [SELF valueForKeyPath:] +bool is_self_value_for_key_path_function_expression(NSExpression *expression) +{ + if (expression.expressionType != NSFunctionExpressionType) + return false; + + if (expression.operand.expressionType != NSEvaluatedObjectExpressionType) + return false; + + return [expression.function isEqualToString:@"valueForKeyPath:"]; +} + +// -[NSPredicate predicateWithSubtitutionVariables:] results in function expressions of the form [SELF valueForKeyPath:] +// that apply_predicate cannot handle. Replace such expressions with equivalent NSKeyPathExpressionType expressions. +NSExpression *simplify_self_value_for_key_path_function_expression(NSExpression *expression) { + if (is_self_value_for_key_path_function_expression(expression)) { + if (NSString *keyPath = [expression.arguments.firstObject keyPath]) { + return [NSExpression expressionForKeyPath:keyPath]; + } + } + return expression; +} + +void QueryBuilder::apply_subquery_count_expression(RLMObjectSchema *objectSchema, + NSExpression *subqueryExpression, NSPredicateOperatorType operatorType, NSExpression *right) { + if (right.expressionType != NSConstantValueExpressionType || ![right.constantValue isKindOfClass:[NSNumber class]]) { + @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY(…).@count is only supported when compared with a constant number."); + } + int64_t value = [right.constantValue integerValue]; + + ColumnReference collectionColumn = column_reference_from_key_path(objectSchema, [subqueryExpression.collection keyPath], true); + RLMObjectSchema *collectionMemberObjectSchema = m_schema[collectionColumn.property().objectClassName]; + + // Eliminate references to the iteration variable in the subquery. + NSPredicate *subqueryPredicate = [subqueryExpression.predicate predicateWithSubstitutionVariables:@{ subqueryExpression.variable : [NSExpression expressionForEvaluatedObject] }]; + subqueryPredicate = transformPredicate(subqueryPredicate, simplify_self_value_for_key_path_function_expression); + + Query subquery = RLMPredicateToQuery(subqueryPredicate, collectionMemberObjectSchema, m_schema, m_group); + add_numeric_constraint(RLMPropertyTypeInt, operatorType, + collectionColumn.resolve(std::move(subquery)).count(), value); +} + +void QueryBuilder::apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right) { + if (![functionExpression.function isEqualToString:@"valueForKeyPath:"] || functionExpression.arguments.count != 1) { + @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported on the result of a SUBQUERY.", functionExpression.function); + } + + NSExpression *keyPathExpression = functionExpression.arguments.firstObject; + if ([keyPathExpression.keyPath isEqualToString:@"@count"]) { + apply_subquery_count_expression(objectSchema, functionExpression.operand, operatorType, right); + } else { + @throw RLMPredicateException(@"Invalid predicate", @"SUBQUERY is only supported when immediately followed by .@count that is compared with a constant number."); + } +} + +void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right) { + if (functionExpression.operand.expressionType == NSSubqueryExpressionType) { + apply_function_subquery_expression(objectSchema, functionExpression, operatorType, right); + } else { + @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported.", functionExpression.function); + } +} + + +void QueryBuilder::apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema) +{ + // Compound predicates. + if ([predicate isMemberOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *comp = (NSCompoundPredicate *)predicate; + + switch ([comp compoundPredicateType]) { + case NSAndPredicateType: + if (comp.subpredicates.count) { + // Add all of the subpredicates. + m_query.group(); + for (NSPredicate *subp in comp.subpredicates) { + apply_predicate(subp, objectSchema); + } + m_query.end_group(); + } else { + // NSCompoundPredicate's documentation states that an AND predicate with no subpredicates evaluates to TRUE. + m_query.and_query(std::unique_ptr(new TrueExpression)); + } + break; + + case NSOrPredicateType: { + // Add all of the subpredicates with ors inbetween. + process_or_group(m_query, comp.subpredicates, [&](__unsafe_unretained NSPredicate *const subp) { + apply_predicate(subp, objectSchema); + }); + break; + } + + case NSNotPredicateType: + // Add the negated subpredicate + m_query.Not(); + apply_predicate(comp.subpredicates.firstObject, objectSchema); + break; + + default: + @throw RLMPredicateException(@"Invalid compound predicate type", + @"Only support AND, OR and NOT predicate types"); + } + } + else if ([predicate isMemberOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *compp = (NSComparisonPredicate *)predicate; + + // check modifier + RLMPrecondition(compp.comparisonPredicateModifier != NSAllPredicateModifier, + @"Invalid predicate", @"ALL modifier not supported"); + + NSExpressionType exp1Type = compp.leftExpression.expressionType; + NSExpressionType exp2Type = compp.rightExpression.expressionType; + + if (compp.comparisonPredicateModifier == NSAnyPredicateModifier) { + // for ANY queries + RLMPrecondition(exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType, + @"Invalid predicate", + @"Predicate with ANY modifier must compare a KeyPath with RLMArray with a value"); + } + + if (compp.predicateOperatorType == NSBetweenPredicateOperatorType || compp.predicateOperatorType == NSInPredicateOperatorType) { + // Inserting an array via %@ gives NSConstantValueExpressionType, but including it directly gives NSAggregateExpressionType + if (exp1Type == NSKeyPathExpressionType && (exp2Type == NSAggregateExpressionType || exp2Type == NSConstantValueExpressionType)) { + // "key.path IN %@", "key.path IN {…}", "key.path BETWEEN %@", or "key.path BETWEEN {…}". + exp2Type = NSConstantValueExpressionType; + } + else if (compp.predicateOperatorType == NSInPredicateOperatorType && exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) { + // "%@ IN key.path" is equivalent to "ANY key.path IN %@". Rewrite the former into the latter. + compp = [NSComparisonPredicate predicateWithLeftExpression:compp.rightExpression rightExpression:compp.leftExpression + modifier:NSAnyPredicateModifier type:NSEqualToPredicateOperatorType options:0]; + exp1Type = NSKeyPathExpressionType; + exp2Type = NSConstantValueExpressionType; + } + else { + if (compp.predicateOperatorType == NSBetweenPredicateOperatorType) { + @throw RLMPredicateException(@"Invalid predicate", + @"Predicate with BETWEEN operator must compare a KeyPath with an aggregate with two values"); + } + else if (compp.predicateOperatorType == NSInPredicateOperatorType) { + @throw RLMPredicateException(@"Invalid predicate", + @"Predicate with IN operator must compare a KeyPath with an aggregate"); + } + } + } + + if (exp1Type == NSKeyPathExpressionType && exp2Type == NSKeyPathExpressionType) { + // both expression are KeyPaths + apply_column_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.keyPath, compp); + } + else if (exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType) { + // comparing keypath to value + apply_value_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.constantValue, compp); + } + else if (exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) { + // comparing value to keypath + apply_value_expression(objectSchema, compp.rightExpression.keyPath, compp.leftExpression.constantValue, compp); + } + else if (exp1Type == NSFunctionExpressionType) { + apply_function_expression(objectSchema, compp.leftExpression, compp.predicateOperatorType, compp.rightExpression); + } + else if (exp1Type == NSSubqueryExpressionType) { + // The subquery expressions that we support are handled by the NSFunctionExpressionType case above. + @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY is only supported when immediately followed by .@count."); + } + else { + @throw RLMPredicateException(@"Invalid predicate expressions", + @"Predicate expressions must compare a keypath and another keypath or a constant value"); + } + } + else if ([predicate isEqual:[NSPredicate predicateWithValue:YES]]) { + m_query.and_query(std::unique_ptr(new TrueExpression)); + } else if ([predicate isEqual:[NSPredicate predicateWithValue:NO]]) { + m_query.and_query(std::unique_ptr(new FalseExpression)); + } + else { + // invalid predicate type + @throw RLMPredicateException(@"Invalid predicate", + @"Only support compound, comparison, and constant predicates"); + } +} + +std::vector RLMValidatedColumnIndicesForSort(RLMClassInfo& classInfo, NSString *keyPathString) +{ + RLMPrecondition([keyPathString rangeOfString:@"@"].location == NSNotFound, @"Invalid key path for sort", + @"Cannot sort on '%@': sorting on key paths that include collection operators is not supported.", + keyPathString); + auto keyPath = key_path_from_string(classInfo.realm.schema, classInfo.rlmObjectSchema, keyPathString); + + RLMPrecondition(!keyPath.containsToManyRelationship, @"Invalid key path for sort", + @"Cannot sort on '%@': sorting on key paths that include a to-many relationship is not supported.", + keyPathString); + + switch (keyPath.property.type) { + case RLMPropertyTypeBool: + case RLMPropertyTypeDate: + case RLMPropertyTypeDouble: + case RLMPropertyTypeFloat: + case RLMPropertyTypeInt: + case RLMPropertyTypeString: + break; + + default: + @throw RLMPredicateException(@"Invalid sort property type", + @"Cannot sort on key path '%@' on object of type '%s': sorting is only supported on bool, date, double, float, integer, and string properties, but property is of type %@.", + keyPathString, classInfo.rlmObjectSchema.className, RLMTypeToString(keyPath.property.type)); + } + + std::vector columnIndices; + columnIndices.reserve(keyPath.links.size() + 1); + + auto currentClassInfo = &classInfo; + for (RLMProperty *link : keyPath.links) { + auto tableColumn = currentClassInfo->tableColumn(link); + currentClassInfo = ¤tClassInfo->linkTargetType(link.index); + columnIndices.push_back(tableColumn); + } + columnIndices.push_back(currentClassInfo->tableColumn(keyPath.property)); + + return columnIndices; +} + +} // namespace + +realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, + RLMSchema *schema, Group &group) +{ + auto query = get_table(group, objectSchema).where(); + + // passing a nil predicate is a no-op + if (!predicate) { + return query; + } + + @autoreleasepool { + QueryBuilder(query, group, schema).apply_predicate(predicate, objectSchema); + } + + // Test the constructed query in core + std::string validateMessage = query.validate(); + RLMPrecondition(validateMessage.empty(), @"Invalid query", @"%.*s", + (int)validateMessage.size(), validateMessage.c_str()); + return query; +} + +realm::SortDescriptor RLMSortDescriptorFromDescriptors(RLMClassInfo& classInfo, NSArray *descriptors) { + std::vector> columnIndices; + std::vector ascending; + columnIndices.reserve(descriptors.count); + ascending.reserve(descriptors.count); + + for (RLMSortDescriptor *descriptor in descriptors) { + columnIndices.push_back(RLMValidatedColumnIndicesForSort(classInfo, descriptor.keyPath)); + ascending.push_back(descriptor.ascending); + } + + return {*classInfo.table(), std::move(columnIndices), std::move(ascending)}; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealm.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealm.mm new file mode 100644 index 0000000..eb799d0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealm.mm @@ -0,0 +1,838 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMRealm_Private.hpp" + +#import "RLMAnalytics.hpp" +#import "RLMArray_Private.hpp" +#import "RLMMigration_Private.h" +#import "RLMObject_Private.h" +#import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObservation.hpp" +#import "RLMProperty.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMRealmUtil.hpp" +#import "RLMSchema_Private.hpp" +#import "RLMSyncManager_Private.h" +#import "RLMSyncUtil_Private.hpp" +#import "RLMThreadSafeReference_Private.hpp" +#import "RLMUpdateChecker.hpp" +#import "RLMUtil.hpp" + +#include "impl/realm_coordinator.hpp" +#include "object_store.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" + +#include +#include +#include + +#import "sync/sync_session.hpp" + +using namespace realm; +using util::File; + +@interface RLMRealmNotificationToken : RLMNotificationToken +@property (nonatomic, strong) RLMRealm *realm; +@property (nonatomic, copy) RLMNotificationBlock block; +@end + +@interface RLMRealm () +@property (nonatomic, strong) NSHashTable *notificationHandlers; +- (void)sendNotifications:(RLMNotification)notification; +@end + +void RLMDisableSyncToDisk() { + realm::disable_sync_to_disk(); +} + +@implementation RLMRealmNotificationToken +- (void)stop { + [_realm verifyThread]; + [_realm.notificationHandlers removeObject:self]; + _realm = nil; + _block = nil; +} + +- (void)suppressNextNotification { + // Temporarily replace the block with one which restores the old block + // rather than producing a notification. + + // This briefly creates a retain cycle but it's fine because the block will + // be synchronously called shortly after this method is called. Unlike with + // collection notifications, this does not have to go through the object + // store or do fancy things to handle transaction coalescing because it's + // called synchronously by the obj-c code and not by the object store. + auto notificationBlock = _block; + _block = ^(RLMNotification, RLMRealm *) { + _block = notificationBlock; + }; +} + +- (void)dealloc { + if (_realm || _block) { + NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold " + @"on to the RLMNotificationToken returned from addNotificationBlock and call " + @"-[RLMNotificationToken stop] when you no longer wish to receive RLMRealm notifications."); + } +} +@end + +static bool shouldForciblyDisableEncryption() { + static bool disableEncryption = getenv("REALM_DISABLE_ENCRYPTION"); + return disableEncryption; +} + +NSData *RLMRealmValidatedEncryptionKey(NSData *key) { + if (shouldForciblyDisableEncryption()) { + return nil; + } + + if (key) { + if (key.length != 64) { + @throw RLMException(@"Encryption key must be exactly 64 bytes long"); + } +#if TARGET_OS_WATCH + @throw RLMException(@"Cannot open an encrypted Realm on watchOS."); +#endif + } + + return key; +} + +@implementation RLMRealm { + NSHashTable *_collectionEnumerators; + bool _sendingNotifications; +} + ++ (BOOL)isCoreDebug { + return realm::Version::has_feature(realm::feature_Debug); +} + ++ (void)initialize { + static bool initialized; + if (initialized) { + return; + } + initialized = true; + + RLMCheckForUpdates(); + RLMSendAnalytics(); +} + +- (instancetype)initPrivate { + self = [super init]; + return self; +} + +- (BOOL)isEmpty { + return realm::ObjectStore::is_empty(self.group); +} + +- (void)verifyThread { + try { + _realm->verify_thread(); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +- (BOOL)inWriteTransaction { + return _realm->is_in_transaction(); +} + +- (realm::Group &)group { + return _realm->read_group(); +} + +- (BOOL)autorefresh { + return _realm->auto_refresh(); +} + +- (void)setAutorefresh:(BOOL)autorefresh { + _realm->set_auto_refresh(autorefresh); +} + ++ (instancetype)defaultRealm { + return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil]; +} + ++ (instancetype)realmWithURL:(NSURL *)fileURL { + RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration]; + configuration.fileURL = fileURL; + return [RLMRealm realmWithConfiguration:configuration error:nil]; +} + ++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration + callbackQueue:(dispatch_queue_t)callbackQueue + callback:(RLMAsyncOpenRealmCallback)callback { + __block NSError *error = nil; + RLMRealm *realmStrongRef = nil; + bool hasSyncConfig = (configuration.config.sync_config != nullptr); + if (hasSyncConfig) { + realmStrongRef = [RLMRealm uncachedSchemalessRealmWithConfiguration:configuration error:&error]; + if (error) { + dispatch_async(callbackQueue, ^{ + callback(nil, error); + }); + return; + } + } + static dispatch_queue_t queue = dispatch_queue_create("io.realm.asyncOpenDispatchQueue", DISPATCH_QUEUE_CONCURRENT); + dispatch_async(queue, ^{ + @autoreleasepool { + RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:&error]; + if (!realm || error) { + dispatch_async(callbackQueue, ^{ + callback(nil, error); + }); + return; + } + auto session = sync_session_for_realm(realm); + if (!hasSyncConfig || !session) { + // Default behavior: just dispatch onto the destination queue and open the Realm. + dispatch_async(callbackQueue, ^{ + @autoreleasepool { + NSError *error = nil; + RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error]; + callback(localRealm, error); + } + }); + return; + } + session->wait_for_download_completion([=](std::error_code error) { + dispatch_async(callbackQueue, ^{ + (void)realmStrongRef; + NSError *err = nil; + if (error == std::error_code{}) { + // Success + @autoreleasepool { + // Try opening the Realm on the destination queue. + RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&err]; + callback(localRealm, err); + } + } else { + // Failure + // FIXME: we need a less ad-hoc way to turn error codes into NSErrors. + err = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientInternalError + userInfo:@{@"underlying": @(error.value()), + @"message": @(error.message().c_str())}]; + callback(nil, err); + } + }); + }); + } + }); +} + +// ARC tries to eliminate calls to autorelease when the value is then immediately +// returned, but this results in significantly different semantics between debug +// and release builds for RLMRealm, so force it to always autorelease. +static id RLMAutorelease(__unsafe_unretained id value) { + // +1 __bridge_retained, -1 CFAutorelease + return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil; +} + ++ (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema { + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; + realm->_realm = sharedRealm; + realm->_dynamic = YES; + realm->_schema = schema; + realm->_info = RLMSchemaInfo(realm); + return RLMAutorelease(realm); +} + +REALM_NOINLINE void RLMRealmTranslateException(NSError **error) { + try { + throw; + } + catch (RealmFileException const& ex) { + switch (ex.kind()) { + case RealmFileException::Kind::PermissionDenied: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFilePermissionDenied, ex), error); + break; + case RealmFileException::Kind::IncompatibleLockFile: { + NSString *err = @"Realm file is currently open in another process " + "which cannot share access with this process. All " + "processes sharing a single file must be the same " + "architecture. For sharing files between the Realm " + "Browser and an iOS simulator, this means that you " + "must use a 64-bit simulator."; + RLMSetErrorOrThrow(RLMMakeError(RLMErrorIncompatibleLockFile, + File::PermissionDenied(err.UTF8String, ex.path())), error); + break; + } + case RealmFileException::Kind::NotFound: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileNotFound, ex), error); + break; + case RealmFileException::Kind::Exists: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileExists, ex), error); + break; + case RealmFileException::Kind::BadHistoryError: { + NSString *err = @"Realm file's history format is incompatible with the " + "settings in the configuration object being used to open " + "the Realm. Note that Realms configured for sync cannot be " + "opened as non-synced Realms, and vice versa. Otherwise, the " + "file may be corrupt."; + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess, + File::AccessError(err.UTF8String, ex.path())), error); + break; + } + case RealmFileException::Kind::AccessError: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess, ex), error); + break; + case RealmFileException::Kind::FormatUpgradeRequired: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileFormatUpgradeRequired, ex), error); + break; + default: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), error); + break; + } + } + catch (AddressSpaceExhausted const &ex) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorAddressSpaceExhausted, ex), error); + } + catch (SchemaMismatchException const& ex) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorSchemaMismatch, ex), error); + } + catch (std::system_error const& ex) { + RLMSetErrorOrThrow(RLMMakeError(ex), error); + } + catch (const std::exception &exp) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error); + } +} + ++ (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + bool dynamic = configuration.dynamic; + bool cache = configuration.cache; + bool readOnly = configuration.readOnly; + + { + Realm::Config& config = configuration.config; + + // try to reuse existing realm first + if (cache || dynamic) { + if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) { + auto const& old_config = realm->_realm->config(); + if (old_config.read_only() != config.read_only()) { + @throw RLMException(@"Realm at path '%s' already opened with different read permissions", config.path.c_str()); + } + if (old_config.in_memory != config.in_memory) { + @throw RLMException(@"Realm at path '%s' already opened with different inMemory settings", config.path.c_str()); + } + if (realm->_dynamic != dynamic) { + @throw RLMException(@"Realm at path '%s' already opened with different dynamic settings", config.path.c_str()); + } + if (old_config.encryption_key != config.encryption_key) { + @throw RLMException(@"Realm at path '%s' already opened with different encryption key", config.path.c_str()); + } + return RLMAutorelease(realm); + } + } + } + + configuration = [configuration copy]; + Realm::Config& config = configuration.config; + + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; + realm->_dynamic = dynamic; + + // protects the realm cache and accessors cache + static std::mutex& initLock = *new std::mutex(); + std::lock_guard lock(initLock); + + try { + realm->_realm = Realm::get_shared_realm(config); + } + catch (...) { + RLMRealmTranslateException(error); + return nil; + } + + // if we have a cached realm on another thread we can skip a few steps and + // just grab its schema + @autoreleasepool { + // ensure that cachedRealm doesn't end up in this thread's autorelease pool + if (auto cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) { + realm->_realm->set_schema_subset(cachedRealm->_realm->schema()); + realm->_schema = cachedRealm.schema; + realm->_info = cachedRealm->_info.clone(cachedRealm->_realm->schema(), realm); + } + } + + if (realm->_schema) { } + else if (dynamic) { + realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()]; + realm->_info = RLMSchemaInfo(realm); + } + else { + // set/align schema or perform migration if needed + RLMSchema *schema = configuration.customSchema ?: RLMSchema.sharedSchema; + + Realm::MigrationFunction migrationFunction; + auto migrationBlock = configuration.migrationBlock; + if (migrationBlock && configuration.schemaVersion > 0) { + migrationFunction = [=](SharedRealm old_realm, SharedRealm realm, Schema& mutableSchema) { + RLMSchema *oldSchema = [RLMSchema dynamicSchemaFromObjectStoreSchema:old_realm->schema()]; + RLMRealm *oldRealm = [RLMRealm realmWithSharedRealm:old_realm schema:oldSchema]; + + // The destination RLMRealm can't just use the schema from the + // SharedRealm because it doesn't have information about whether or + // not a class was defined in Swift, which effects how new objects + // are created + RLMRealm *newRealm = [RLMRealm realmWithSharedRealm:realm schema:schema.copy]; + + [[[RLMMigration alloc] initWithRealm:newRealm oldRealm:oldRealm schema:mutableSchema] execute:migrationBlock]; + + oldRealm->_realm = nullptr; + newRealm->_realm = nullptr; + }; + } + + try { + realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version, + std::move(migrationFunction)); + } + catch (...) { + RLMRealmTranslateException(error); + return nil; + } + + realm->_schema = schema; + realm->_info = RLMSchemaInfo(realm); + RLMRealmCreateAccessors(realm.schema); + + if (!readOnly) { + // initializing the schema started a read transaction, so end it + [realm invalidate]; + } + } + + if (cache) { + RLMCacheRealm(config.path, realm); + } + + if (!readOnly) { + realm->_realm->m_binding_context = RLMCreateBindingContext(realm); + realm->_realm->m_binding_context->realm = realm->_realm; + } + + return RLMAutorelease(realm); +} + ++ (instancetype)uncachedSchemalessRealmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; + try { + realm->_realm = Realm::get_shared_realm(configuration.config); + } + catch (...) { + RLMRealmTranslateException(error); + return nil; + } + return realm; +} + ++ (void)resetRealmState { + RLMClearRealmCache(); + realm::_impl::RealmCoordinator::clear_cache(); + [RLMRealmConfiguration resetRealmConfigurationState]; +} + +- (void)verifyNotificationsAreSupported { + [self verifyThread]; + if (_realm->config().read_only()) { + @throw RLMException(@"Read-only Realms do not change and do not have change notifications"); + } + if (!_realm->can_deliver_notifications()) { + @throw RLMException(@"Can only add notification blocks from within runloops."); + } +} + +- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block { + if (!block) { + @throw RLMException(@"The notification block should not be nil"); + } + [self verifyNotificationsAreSupported]; + + _realm->read_group(); + + if (!_notificationHandlers) { + _notificationHandlers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; + } + + RLMRealmNotificationToken *token = [[RLMRealmNotificationToken alloc] init]; + token.realm = self; + token.block = block; + [_notificationHandlers addObject:token]; + return token; +} + +- (void)sendNotifications:(RLMNotification)notification { + NSAssert(!_realm->config().read_only(), @"Read-only realms do not have notifications"); + if (_sendingNotifications) { + return; + } + NSUInteger count = _notificationHandlers.count; + if (count == 0) { + return; + } + + _sendingNotifications = true; + auto cleanup = realm::util::make_scope_exit([&]() noexcept { + _sendingNotifications = false; + }); + + // call this realm's notification blocks + if (count == 1) { + if (auto block = [_notificationHandlers.anyObject block]) { + block(notification, self); + } + } + else { + for (RLMRealmNotificationToken *token in _notificationHandlers.allObjects) { + if (auto block = token.block) { + block(notification, self); + } + } + } +} + +- (RLMRealmConfiguration *)configuration { + RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init]; + configuration.config = _realm->config(); + configuration.dynamic = _dynamic; + configuration.customSchema = _schema; + return configuration; +} + +- (void)beginWriteTransaction { + try { + _realm->begin_transaction(); + } + catch (std::exception &ex) { + @throw RLMException(ex); + } +} + +- (void)commitWriteTransaction { + [self commitWriteTransaction:nil]; +} + +- (BOOL)commitWriteTransaction:(NSError **)outError { + try { + _realm->commit_transaction(); + return YES; + } + catch (...) { + RLMRealmTranslateException(outError); + return NO; + } +} + +- (BOOL)commitWriteTransactionWithoutNotifying:(NSArray *)tokens error:(NSError **)error { + for (RLMNotificationToken *token in tokens) { + if (token.realm != self) { + @throw RLMException(@"Incorrect Realm: only notifications for the Realm being modified can be skipped."); + } + [token suppressNextNotification]; + } + + try { + _realm->commit_transaction(); + return YES; + } + catch (...) { + RLMRealmTranslateException(error); + return NO; + } +} + +- (void)transactionWithBlock:(void(^)(void))block { + [self transactionWithBlock:block error:nil]; +} + +- (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError { + [self beginWriteTransaction]; + block(); + if (_realm->is_in_transaction()) { + return [self commitWriteTransaction:outError]; + } + return YES; +} + +- (void)cancelWriteTransaction { + try { + _realm->cancel_transaction(); + } + catch (std::exception &ex) { + @throw RLMException(ex); + } +} + +- (void)invalidate { + if (_realm->is_in_transaction()) { + NSLog(@"WARNING: An RLMRealm instance was invalidated during a write " + "transaction and all pending changes have been rolled back."); + } + + [self detachAllEnumerators]; + + for (auto& objectInfo : _info) { + for (RLMObservationInfo *info : objectInfo.second.observedObjects) { + info->willChange(RLMInvalidatedKey); + } + } + + _realm->invalidate(); + + for (auto& objectInfo : _info) { + for (RLMObservationInfo *info : objectInfo.second.observedObjects) { + info->didChange(RLMInvalidatedKey); + } + objectInfo.second.releaseTable(); + } +} + +- (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference { + return [reference resolveReferenceInRealm:self]; +} + +/** + Replaces all string columns in this Realm with a string enumeration column and compacts the + database file. + + Cannot be called from a write transaction. + + Compaction will not occur if other `RLMRealm` instances exist. + + While compaction is in progress, attempts by other threads or processes to open the database will + wait. + + Be warned that resource requirements for compaction is proportional to the amount of live data in + the database. + + Compaction works by writing the database contents to a temporary database file and then replacing + the database with the temporary one. The name of the temporary file is formed by appending + `.tmp_compaction_space` to the name of the database. + + @return YES if the compaction succeeded. + */ +- (BOOL)compact { + // compact() automatically ends the read transaction, but we need to clean + // up cached state and send invalidated notifications when that happens, so + // explicitly end it first unless we're in a write transaction (in which + // case compact() will throw an exception) + if (!_realm->is_in_transaction()) { + [self invalidate]; + } + + try { + return _realm->compact(); + } + catch (std::exception const& ex) { + @throw RLMException(ex); + } +} + +- (void)dealloc { + if (_realm) { + if (_realm->is_in_transaction()) { + [self cancelWriteTransaction]; + NSLog(@"WARNING: An RLMRealm instance was deallocated during a write transaction and all " + "pending changes have been rolled back. Make sure to retain a reference to the " + "RLMRealm for the duration of the write transaction."); + } + } +} + +- (BOOL)refresh { + return _realm->refresh(); +} + +- (void)addObject:(__unsafe_unretained RLMObject *const)object { + RLMAddObjectToRealm(object, self, false); +} + +- (void)addObjects:(id)array { + for (RLMObject *obj in array) { + if (![obj isKindOfClass:[RLMObject class]]) { + @throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.", + NSStringFromClass(obj.class)); + } + [self addObject:obj]; + } +} + +- (void)addOrUpdateObject:(RLMObject *)object { + // verify primary key + if (!object.objectSchema.primaryKeyProperty) { + @throw RLMException(@"'%@' does not have a primary key and can not be updated", object.objectSchema.className); + } + + RLMAddObjectToRealm(object, self, true); +} + +- (void)addOrUpdateObjectsFromArray:(id)array { + for (RLMObject *obj in array) { + [self addOrUpdateObject:obj]; + } +} + +- (void)deleteObject:(RLMObject *)object { + RLMDeleteObjectFromRealm(object, self); +} + +- (void)deleteObjects:(id)array { + if ([array respondsToSelector:@selector(realm)] && [array respondsToSelector:@selector(deleteObjectsFromRealm)]) { + if (self != (RLMRealm *)[array realm]) { + @throw RLMException(@"Can only delete objects from the Realm they belong to."); + } + [array deleteObjectsFromRealm]; + } + else if ([array conformsToProtocol:@protocol(NSFastEnumeration)]) { + for (id obj in array) { + if ([obj isKindOfClass:RLMObjectBase.class]) { + RLMDeleteObjectFromRealm(obj, self); + } + } + } + else { + @throw RLMException(@"Invalid array type - container must be an RLMArray, RLMArray, or NSArray of RLMObjects"); + } +} + +- (void)deleteAllObjects { + RLMDeleteAllObjectsFromRealm(self); +} + +- (RLMResults *)allObjects:(NSString *)objectClassName { + return RLMGetObjects(self, objectClassName, nil); +} + +- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objects:objectClassName where:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat args:(va_list)args { + return [self objects:objectClassName withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (RLMResults *)objects:(NSString *)objectClassName withPredicate:(NSPredicate *)predicate { + return RLMGetObjects(self, objectClassName, predicate); +} + +- (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey { + return RLMGetObject(self, className, primaryKey); +} + ++ (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error { + try { + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; + config.fileURL = fileURL; + config.encryptionKey = RLMRealmValidatedEncryptionKey(key); + + uint64_t version = Realm::get_schema_version(config.config); + if (version == realm::ObjectStore::NotVersioned) { + RLMSetErrorOrThrow([NSError errorWithDomain:RLMErrorDomain code:RLMErrorFail userInfo:@{NSLocalizedDescriptionKey:@"Cannot open an uninitialized realm in read-only mode"}], error); + } + return version; + } + catch (std::exception &exp) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error); + return RLMNotVersioned; + } +} + ++ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration { + // Preserves backwards compatibility + NSError *error; + [self performMigrationForConfiguration:configuration error:&error]; + return error; +} + ++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + if (RLMGetAnyCachedRealmForPath(configuration.config.path)) { + @throw RLMException(@"Cannot migrate Realms that are already open."); + } + + NSError *localError; // Prevents autorelease + BOOL success; + @autoreleasepool { + success = [RLMRealm realmWithConfiguration:configuration error:&localError] != nil; + } + if (!success && error) { + *error = localError; // Must set outside pool otherwise will free anyway + } + return success; +} + +- (RLMObject *)createObject:(NSString *)className withValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue(self, className, value, false); +} + +- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error { + key = RLMRealmValidatedEncryptionKey(key); + NSString *path = fileURL.path; + + try { + _realm->write_copy(path.UTF8String, {static_cast(key.bytes), key.length}); + return YES; + } + catch (...) { + __autoreleasing NSError *dummyError; + if (!error) { + error = &dummyError; + } + RLMRealmTranslateException(error); + return NO; + } + + return NO; +} + +- (void)registerEnumerator:(RLMFastEnumerator *)enumerator { + if (!_collectionEnumerators) { + _collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; + } + [_collectionEnumerators addObject:enumerator]; +} + +- (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator { + [_collectionEnumerators removeObject:enumerator]; +} + +- (void)detachAllEnumerators { + for (RLMFastEnumerator *enumerator in _collectionEnumerators) { + [enumerator detach]; + } + _collectionEnumerators = nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm new file mode 100644 index 0000000..5e7339b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMRealmConfiguration+Sync.h" + +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMSyncManager_Private.h" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync/sync_config.hpp" +#import "sync/sync_manager.hpp" + +@implementation RLMRealmConfiguration (Sync) + +#pragma mark - API + +- (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration { + if (self.config.should_compact_on_launch_function) { + @throw RLMException(@"Cannot set `syncConfiguration` when `shouldCompactOnLaunch` is set."); + } + RLMSyncUser *user = syncConfiguration.user; + if (user.state == RLMSyncUserStateError) { + @throw RLMException(@"Cannot set a sync configuration which has an errored-out user."); + } + + NSURL *realmURL = syncConfiguration.realmURL; + // Ensure sync manager is initialized, if it hasn't already been. + [RLMSyncManager sharedManager]; + NSAssert(user.identity, @"Cannot call this method on a user that doesn't have an identity."); + if (syncConfiguration.customFileURL) { + self.config.path = syncConfiguration.customFileURL.path.UTF8String; + } else { + self.config.path = SyncManager::shared().path_for_realm([user.identity UTF8String], + [realmURL.absoluteString UTF8String]); + } + self.config.in_memory = false; + self.config.sync_config = std::make_shared([syncConfiguration rawConfiguration]); + self.config.schema_mode = realm::SchemaMode::Additive; + if (!self.config.encryption_key.empty()) { + auto& sync_encryption_key = self.config.sync_config->realm_encryption_key; + sync_encryption_key = std::array(); + std::copy_n(self.config.encryption_key.begin(), 64, sync_encryption_key->begin()); + } +} + +- (RLMSyncConfiguration *)syncConfiguration { + if (!self.config.sync_config) { + return nil; + } + realm::SyncConfig& sync_config = *self.config.sync_config; + return [[RLMSyncConfiguration alloc] initWithRawConfig:sync_config]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm new file mode 100644 index 0000000..9868fdd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm @@ -0,0 +1,299 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMRealmConfiguration_Private.h" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMRealm_Private.h" +#import "RLMSchema_Private.hpp" +#import "RLMUtil.hpp" + +#import "schema.hpp" +#import "shared_realm.hpp" +#import "sync/sync_config.hpp" + +static NSString *const c_RLMRealmConfigurationProperties[] = { + @"fileURL", + @"inMemoryIdentifier", + @"encryptionKey", + @"readOnly", + @"schemaVersion", + @"migrationBlock", + @"deleteRealmIfMigrationNeeded", + @"shouldCompactOnLaunch", + @"dynamic", + @"customSchema", +}; + +static NSString *const c_defaultRealmFileName = @"default.realm"; +RLMRealmConfiguration *s_defaultConfiguration; + +NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *bundleIdentifier) { + return [RLMDefaultDirectoryForBundleIdentifier(bundleIdentifier) + stringByAppendingPathComponent:fileName]; +} + +NSString *RLMRealmPathForFile(NSString *fileName) { + static NSString *directory = RLMDefaultDirectoryForBundleIdentifier(nil); + return [directory stringByAppendingPathComponent:fileName]; +} + +@implementation RLMRealmConfiguration { + realm::Realm::Config _config; +} + +- (realm::Realm::Config&)config { + return _config; +} + ++ (instancetype)defaultConfiguration { + return [[self rawDefaultConfiguration] copy]; +} + ++ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration { + if (!configuration) { + @throw RLMException(@"Cannot set the default configuration to nil."); + } + @synchronized(c_defaultRealmFileName) { + s_defaultConfiguration = [configuration copy]; + } +} + ++ (RLMRealmConfiguration *)rawDefaultConfiguration { + @synchronized(c_defaultRealmFileName) { + if (!s_defaultConfiguration) { + s_defaultConfiguration = [[RLMRealmConfiguration alloc] init]; + } + } + return s_defaultConfiguration; +} + ++ (void)resetRealmConfigurationState { + @synchronized(c_defaultRealmFileName) { + s_defaultConfiguration = nil; + } +} + +- (instancetype)init { + self = [super init]; + if (self) { + static NSURL *defaultRealmURL = [NSURL fileURLWithPath:RLMRealmPathForFile(c_defaultRealmFileName)]; + self.fileURL = defaultRealmURL; + self.schemaVersion = 0; + self.cache = YES; + + // We have our own caching of RLMRealm instances, so the ObjectStore + // cache is at best pointless, and may result in broken behavior when + // a realm::Realm instance outlives the RLMRealm (due to collection + // notifiers being in the middle of running when the RLMRealm is + // dealloced) and then reused for a new RLMRealm + _config.cache = false; + } + + return self; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init]; + configuration->_config = _config; + configuration->_cache = _cache; + configuration->_dynamic = _dynamic; + configuration->_migrationBlock = _migrationBlock; + configuration->_shouldCompactOnLaunch = _shouldCompactOnLaunch; + configuration->_customSchema = _customSchema; + return configuration; +} + +- (NSString *)description { + NSMutableString *string = [NSMutableString stringWithFormat:@"%@ {\n", self.class]; + for (NSString *key : c_RLMRealmConfigurationProperties) { + NSString *description = [[self valueForKey:key] description]; + description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]; + + [string appendFormat:@"\t%@ = %@;\n", key, description]; + } + return [string stringByAppendingString:@"}"]; +} + +static void RLMNSStringToStdString(std::string &out, NSString *in) { + out.resize([in maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + if (out.empty()) { + return; + } + + NSUInteger size = out.size(); + [in getBytes:&out[0] + maxLength:size + usedLength:&size + encoding:NSUTF8StringEncoding + options:0 range:{0, in.length} remainingRange:nullptr]; + out.resize(size); +} + +- (NSURL *)fileURL { + if (_config.in_memory || _config.sync_config) { + return nil; + } + return [NSURL fileURLWithPath:@(_config.path.c_str())]; +} + +- (void)setFileURL:(NSURL *)fileURL { + NSString *path = fileURL.path; + if (path.length == 0) { + @throw RLMException(@"Realm path must not be empty"); + } + _config.sync_config = nullptr; + + RLMNSStringToStdString(_config.path, path); + _config.in_memory = false; +} + +- (NSString *)inMemoryIdentifier { + if (!_config.in_memory) { + return nil; + } + return [@(_config.path.c_str()) lastPathComponent]; +} + +- (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier { + if (inMemoryIdentifier.length == 0) { + @throw RLMException(@"In-memory identifier must not be empty"); + } + _config.sync_config = nullptr; + + RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]); + _config.in_memory = true; +} + +- (NSData *)encryptionKey { + return _config.encryption_key.empty() ? nil : [NSData dataWithBytes:_config.encryption_key.data() length:_config.encryption_key.size()]; +} + +- (void)setEncryptionKey:(NSData * __nullable)encryptionKey { + if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) { + auto bytes = static_cast(key.bytes); + _config.encryption_key.assign(bytes, bytes + key.length); + if (_config.sync_config) { + auto& sync_encryption_key = self.config.sync_config->realm_encryption_key; + sync_encryption_key = std::array(); + std::copy_n(_config.encryption_key.begin(), 64, sync_encryption_key->begin()); + } + } + else { + _config.encryption_key.clear(); + if (_config.sync_config) + _config.sync_config->realm_encryption_key = realm::util::none; + } +} + +- (BOOL)readOnly { + return _config.read_only(); +} + +- (void)setReadOnly:(BOOL)readOnly { + if (readOnly) { + if (self.deleteRealmIfMigrationNeeded) { + @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set."); + } else if (self.shouldCompactOnLaunch) { + @throw RLMException(@"Cannot set `readOnly` when `shouldCompactOnLaunch` is set."); + } + _config.schema_mode = realm::SchemaMode::ReadOnly; + } + else if (self.readOnly) { + _config.schema_mode = realm::SchemaMode::Automatic; + } +} + +- (uint64_t)schemaVersion { + return _config.schema_version; +} + +- (void)setSchemaVersion:(uint64_t)schemaVersion { + if (schemaVersion == RLMNotVersioned) { + @throw RLMException(@"Cannot set schema version to %llu (RLMNotVersioned)", RLMNotVersioned); + } + _config.schema_version = schemaVersion; +} + +- (BOOL)deleteRealmIfMigrationNeeded { + return _config.schema_mode == realm::SchemaMode::ResetFile; +} + +- (void)setDeleteRealmIfMigrationNeeded:(BOOL)deleteRealmIfMigrationNeeded { + if (deleteRealmIfMigrationNeeded) { + if (self.readOnly) { + @throw RLMException(@"Cannot set `deleteRealmIfMigrationNeeded` when `readOnly` is set."); + } + _config.schema_mode = realm::SchemaMode::ResetFile; + } + else if (self.deleteRealmIfMigrationNeeded) { + _config.schema_mode = realm::SchemaMode::Automatic; + } +} + +- (NSArray *)objectClasses { + return [_customSchema.objectSchema valueForKeyPath:@"objectClass"]; +} + +- (void)setObjectClasses:(NSArray *)objectClasses { + self.customSchema = [RLMSchema schemaWithObjectClasses:objectClasses]; +} + +- (void)setDynamic:(bool)dynamic { + _dynamic = dynamic; + self.cache = !dynamic; +} + +- (bool)disableFormatUpgrade { + return _config.disable_format_upgrade; +} + +- (void)setDisableFormatUpgrade:(bool)disableFormatUpgrade { + _config.disable_format_upgrade = disableFormatUpgrade; +} + +- (realm::SchemaMode)schemaMode { + return _config.schema_mode; +} + +- (void)setSchemaMode:(realm::SchemaMode)mode { + _config.schema_mode = mode; +} + +- (NSString *)pathOnDisk { + return @(_config.path.c_str()); +} + +- (void)setShouldCompactOnLaunch:(RLMShouldCompactOnLaunchBlock)shouldCompactOnLaunch { + if (shouldCompactOnLaunch) { + if (self.readOnly) { + @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `readOnly` is set."); + } else if (_config.sync_config) { + @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `syncConfiguration` is set."); + } + _config.should_compact_on_launch_function = [=](size_t totalBytes, size_t usedBytes) { + return shouldCompactOnLaunch(totalBytes, usedBytes); + }; + } + else { + _config.should_compact_on_launch_function = nullptr; + } + _shouldCompactOnLaunch = shouldCompactOnLaunch; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmUtil.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmUtil.mm new file mode 100644 index 0000000..9836df5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMRealmUtil.mm @@ -0,0 +1,143 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMRealmUtil.hpp" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMUtil.hpp" + +#import +#import + +#import "binding_context.hpp" + +#import +#import +#import +#import +#import +#import + +// Global realm state +static std::mutex& s_realmCacheMutex = *new std::mutex(); +static std::map& s_realmsPerPath = *new std::map(); + +void RLMCacheRealm(std::string const& path, __unsafe_unretained RLMRealm *const realm) { + std::lock_guard lock(s_realmCacheMutex); + NSMapTable *realms = s_realmsPerPath[path]; + if (!realms) { + s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory + valueOptions:NSPointerFunctionsWeakMemory]; + } + [realms setObject:realm forKey:(__bridge id)pthread_self()]; +} + +RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) { + std::lock_guard lock(s_realmCacheMutex); + return [s_realmsPerPath[path] objectEnumerator].nextObject; +} + +RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) { + std::lock_guard lock(s_realmCacheMutex); + return [s_realmsPerPath[path] objectForKey:(__bridge id)pthread_self()]; +} + +void RLMClearRealmCache() { + std::lock_guard lock(s_realmCacheMutex); + s_realmsPerPath.clear(); +} + +namespace { +class RLMNotificationHelper : public realm::BindingContext { +public: + RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { } + + bool can_deliver_notifications() const noexcept override { + // The main thread may not be in a run loop yet if we're called from + // something like `applicationDidFinishLaunching:`, but it presumably will + // be in the future + if ([NSThread isMainThread]) { + return true; + } + // Current mode indicates why the current callout from the runloop was made, + // and is null if a runloop callout isn't currently being processed + if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) { + CFRelease(mode); + return true; + } + return false; + } + + void changes_available() override { + @autoreleasepool { + auto realm = _realm; + if (realm && !realm.autorefresh) { + [realm sendNotifications:RLMRealmRefreshRequiredNotification]; + } + } + } + + std::vector get_observed_rows() override { + @autoreleasepool { + if (auto realm = _realm) { + [realm detachAllEnumerators]; + return RLMGetObservedRows(realm->_info); + } + return {}; + } + } + + void will_change(std::vector const& observed, std::vector const& invalidated) override { + @autoreleasepool { + RLMWillChange(observed, invalidated); + } + } + + void did_change(std::vector const& observed, std::vector const& invalidated, bool version_changed) override { + try { + @autoreleasepool { + RLMDidChange(observed, invalidated); + if (version_changed) { + [_realm sendNotifications:RLMRealmDidChangeNotification]; + } + } + } + catch (...) { + // This can only be called during a write transaction if it was + // called due to the transaction beginning, so cancel it to ensure + // exceptions thrown here behave the same as exceptions thrown when + // actually beginning the write + if (_realm.inWriteTransaction) { + [_realm cancelWriteTransaction]; + } + throw; + } + } + +private: + // This is owned by the realm, so it needs to not retain the realm + __weak RLMRealm *const _realm; +}; +} // anonymous namespace + + +std::unique_ptr RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) { + return std::unique_ptr(new RLMNotificationHelper(realm)); +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMResults.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMResults.mm new file mode 100644 index 0000000..a2d1f30 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMResults.mm @@ -0,0 +1,482 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMResults_Private.h" + +#import "RLMArray_Private.hpp" +#import "RLMCollection_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMThreadSafeReference_Private.hpp" +#import "RLMUtil.hpp" + +#import "results.hpp" + +#import +#import +#import + +using namespace realm; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincomplete-implementation" +@implementation RLMNotificationToken +@end +#pragma clang diagnostic pop + +@interface RLMResults () +@end + +// +// RLMResults implementation +// +@implementation RLMResults { + realm::Results _results; + RLMRealm *_realm; + RLMClassInfo *_info; +} + +- (instancetype)initPrivate { + self = [super init]; + return self; +} + +static void assertKeyPathIsNotNested(NSString *keyPath) { + if ([keyPath rangeOfString:@"."].location != NSNotFound) { + @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); + } +} + +[[gnu::noinline]] +[[noreturn]] +static void throwError(NSString *aggregateMethod) { + try { + throw; + } + catch (realm::InvalidTransactionException const&) { + @throw RLMException(@"Cannot modify Results outside of a write transaction"); + } + catch (realm::IncorrectThreadException const&) { + @throw RLMException(@"Realm accessed from incorrect thread"); + } + catch (realm::Results::InvalidatedException const&) { + @throw RLMException(@"RLMResults has been invalidated"); + } + catch (realm::Results::DetatchedAccessorException const&) { + @throw RLMException(@"Object has been invalidated"); + } + catch (realm::Results::IncorrectTableException const& e) { + @throw RLMException(@"Object type '%s' does not match RLMResults type '%s'.", + e.actual.data(), e.expected.data()); + } + catch (realm::Results::OutOfBoundsIndexException const& e) { + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + e.requested, e.valid_count); + } + catch (realm::Results::UnsupportedColumnTypeException const& e) { + @throw RLMException(@"%@ is not supported for %@ property '%s'", + aggregateMethod, + RLMTypeToString((RLMPropertyType)e.column_type), + e.column_name.data()); + } +} + +template +static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) { + try { + return f(); + } + catch (...) { + throwError(aggregateMethod); + } +} + ++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info + results:(realm::Results)results { + RLMResults *ar = [[self alloc] initPrivate]; + ar->_results = std::move(results); + ar->_realm = info.realm; + ar->_info = &info; + return ar; +} + ++ (instancetype)emptyDetachedResults { + return [[self alloc] initPrivate]; +} + +static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMResults *const ar) { + ar->_realm->_realm->verify_thread(); + ar->_realm->_realm->verify_in_write(); +} + +- (BOOL)isInvalidated { + return translateErrors([&] { return !_results.is_valid(); }); +} + +- (NSUInteger)count { + return translateErrors([&] { return _results.size(); }); +} + +- (NSString *)objectClassName { + return RLMStringDataToNSString(_results.get_object_type()); +} + +- (RLMClassInfo *)objectInfo { + return _info; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unused __unsafe_unretained id [])buffer + count:(NSUInteger)len { + if (!_info) { + return 0; + } + + __autoreleasing RLMFastEnumerator *enumerator; + if (state->state == 0) { + enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_info]; + state->extra[0] = (long)enumerator; + state->extra[1] = self.count; + } + else { + enumerator = (__bridge id)(void *)state->extra[0]; + } + + return [enumerator countByEnumeratingWithState:state count:len]; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; + va_end(args); + return index; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args { + return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat + arguments:args]]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + if (_results.get_mode() == Results::Mode::Empty) { + return NSNotFound; + } + + Query query = translateErrors([&] { return _results.get_query(); }); + query.and_query(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)); + query.sync_view_if_needed(); + +#if REALM_VER_MAJOR >= 2 + size_t indexInTable; + if (const auto& sort = _results.get_sort()) { + // A sort order is specified so we need to return the first match given that ordering. + TableView table_view = query.find_all(); + table_view.sort(sort); + if (!table_view.size()) { + return NSNotFound; + } + indexInTable = table_view.get_source_ndx(0); + } else { + indexInTable = query.find(); + } + if (indexInTable == realm::not_found) { + return NSNotFound; + } + return RLMConvertNotFound(_results.index_of(indexInTable)); +#else + TableView table_view; + if (const auto& sort = _results.get_sort()) { + // A sort order is specified so we need to return the first match given that ordering. + table_view = query.find_all(); + table_view.sort(sort); + } else { + table_view = query.find_all(0, -1, 1); + } + if (!table_view.size()) { + return NSNotFound; + } + return _results.index_of(table_view.get_source_ndx(0)); +#endif +} + +- (id)objectAtIndex:(NSUInteger)index { + return translateErrors([&] { + return RLMCreateObjectAccessor(_realm, *_info, _results.get(index)); + }); +} + +- (id)firstObject { + auto row = translateErrors([&] { return _results.first(); }); + return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; +} + +- (id)lastObject { + auto row = translateErrors([&] { return _results.last(); }); + return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + if (!object || (!object->_realm && !object.invalidated)) { + return NSNotFound; + } + + return translateErrors([&] { + return RLMConvertNotFound(_results.index_of(object->_row)); + }); +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if ([keyPath characterAtIndex:0] == '@') { + if ([keyPath isEqualToString:@"@count"]) { + return @(self.count); + } + NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; + NSUInteger keyPathLength = keyPath.length; + NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength; + NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)]; + SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]); + BOOL isValidOperator = [self respondsToSelector:opSelector]; + if (!isValidOperator) { + @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath); + } + else if (separatorIndex >= keyPathLength - 1) { + @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'", operatorName, keyPath); + } + NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1]; + if (isValidOperator) { + return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath); + } + } + return [super valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + return translateErrors([&] { + return RLMCollectionValueForKey(self, key); + }); +} + +- (void)setValue:(id)value forKey:(NSString *)key { + translateErrors([&] { RLMResultsValidateInWriteTransaction(self); }); + RLMCollectionSetValueForKey(self, key, value); +} + +- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath method:(util::Optional (Results::*)(size_t))method + methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty { + assertKeyPathIsNotNested(keyPath); + return [self aggregate:keyPath method:method methodName:methodName returnNilForEmpty:returnNilForEmpty]; +} + +- (NSNumber *)_minForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min" returnNilForEmpty:YES]; +} + +- (NSNumber *)_maxForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max" returnNilForEmpty:YES]; +} + +- (NSNumber *)_sumForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum" returnNilForEmpty:NO]; +} + +- (NSNumber *)_avgForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::average methodName:@"@avg" returnNilForEmpty:YES]; +} + +- (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath { + assertKeyPathIsNotNested(keyPath); + return translateErrors([&] { + return RLMCollectionValueForKey(self, keyPath); + }); +} + +- (NSArray *)_distinctUnionOfObjectsForKeyPath:(NSString *)keyPath { + return [NSSet setWithArray:[self _unionOfObjectsForKeyPath:keyPath]].allObjects; +} + +- (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath { + assertKeyPathIsNotNested(keyPath); + if ([keyPath isEqualToString:@"self"]) { + @throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'."); + } + + return translateErrors([&] { + NSArray *nestedResults = RLMCollectionValueForKey(self, keyPath); + NSMutableArray *flatArray = [NSMutableArray arrayWithCapacity:nestedResults.count]; + for (id array in nestedResults) { + NSArray *nsArray = RLMCollectionValueForKey(array, @"self"); + [flatArray addObjectsFromArray:nsArray]; + } + return flatArray; + }); +} + +- (NSArray *)_distinctUnionOfArraysForKeyPath:(__unused NSString *)keyPath { + return [NSSet setWithArray:[self _unionOfArraysForKeyPath:keyPath]].allObjects; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Empty) { + return self; + } + auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group); + return [RLMResults resultsWithObjectInfo:*_info results:_results.filter(std::move(query))]; + }); +} + +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending { + return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]]; +} + +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending { + return [self sortedResultsUsingKeyPath:property ascending:ascending]; +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { + if (properties.count == 0) { + return self; + } + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Empty) { + return self; + } + + return [RLMResults resultsWithObjectInfo:*_info results:_results.sort(RLMSortDescriptorFromDescriptors(*_info, properties))]; + }); +} + +- (id)objectAtIndexedSubscript:(NSUInteger)index { + return [self objectAtIndex:index]; +} + +- (id)aggregate:(NSString *)property method:(util::Optional (Results::*)(size_t))method + methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty { + if (_results.get_mode() == Results::Mode::Empty) { + return returnNilForEmpty ? nil : @0; + } + size_t column = _info->tableColumn(property); + auto value = translateErrors([&] { return (_results.*method)(column); }, methodName); + if (!value) { + return nil; + } + return RLMMixedToObjc(*value); +} + +- (id)minOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::min methodName:@"minOfProperty" returnNilForEmpty:YES]; +} + +- (id)maxOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::max methodName:@"maxOfProperty" returnNilForEmpty:YES]; +} + +- (id)sumOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::sum methodName:@"sumOfProperty" returnNilForEmpty:NO]; +} + +- (id)averageOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::average methodName:@"averageOfProperty" returnNilForEmpty:YES]; +} + +- (void)deleteObjectsFromRealm { + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Table) { + RLMResultsValidateInWriteTransaction(self); + RLMClearTable(*_info); + } + else { + RLMTrackDeletions(_realm, [&] { _results.clear(); }); + } + }); +} + +- (NSString *)description { + return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth); +} + +- (NSUInteger)indexInSource:(NSUInteger)index { + return translateErrors([&] { return _results.get(index).get_index(); }); +} + +- (realm::TableView)tableView { + return translateErrors([&] { return _results.get_tableview(); }); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block { + [_realm verifyNotificationsAreSupported]; + return RLMAddNotificationBlock(self, _results, block, true); +} +#pragma clang diagnostic pop + +- (BOOL)isAttached +{ + return !!_realm; +} + +#pragma mark - Thread Confined Protocol Conformance + +- (std::unique_ptr)makeThreadSafeReference { + return std::make_unique>(_realm->_realm->obtain_thread_safe_reference(_results)); +} + +- (id)objectiveCMetadata { + return nil; +} + ++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference + metadata:(__unused id)metadata + realm:(RLMRealm *)realm { + REALM_ASSERT_DEBUG(dynamic_cast *>(reference.get())); + auto results_reference = static_cast *>(reference.get()); + + Results results = realm->_realm->resolve_thread_safe_reference(std::move(*results_reference)); + + return [RLMResults resultsWithObjectInfo:realm->_info[RLMStringDataToNSString(results.get_object_type())] + results:std::move(results)]; +} + +@end + +@implementation RLMLinkingObjects +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSchema.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSchema.mm new file mode 100644 index 0000000..0c7b965 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSchema.mm @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMSchema_Private.h" + +#import "RLMAccessor.h" +#import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "schema.hpp" + +#import + +#import +#include + +using namespace realm; + +const uint64_t RLMNotVersioned = realm::ObjectStore::NotVersioned; + +// RLMSchema private properties +@interface RLMSchema () +@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName; +@end + +static RLMSchema *s_sharedSchema = [[RLMSchema alloc] init]; +static NSMutableDictionary *s_localNameToClass = [[NSMutableDictionary alloc] init]; +static NSMutableDictionary *s_privateObjectSubclasses = [[NSMutableDictionary alloc] init]; + +static enum class SharedSchemaState { + Uninitialized, + Initializing, + Initialized +} s_sharedSchemaState = SharedSchemaState::Uninitialized; + +@implementation RLMSchema { + NSArray *_objectSchema; + realm::Schema _objectStoreSchema; +} + +// Caller must @synchronize on s_localNameToClass +static RLMObjectSchema *RLMRegisterClass(Class cls) { + if (RLMObjectSchema *schema = s_privateObjectSubclasses[[cls className]]) { + return schema; + } + + auto prevState = s_sharedSchemaState; + s_sharedSchemaState = SharedSchemaState::Initializing; + RLMObjectSchema *schema = [RLMObjectSchema schemaForObjectClass:cls]; + s_sharedSchemaState = prevState; + + // set unmanaged class on shared shema for unmanaged object creation + schema.unmanagedClass = RLMUnmanagedAccessorClassForObjectClass(schema.objectClass, schema); + + // override sharedSchema class methods for performance + RLMReplaceSharedSchemaMethod(cls, schema); + + s_privateObjectSubclasses[schema.className] = schema; + if ([cls shouldIncludeInDefaultSchema] && prevState != SharedSchemaState::Initialized) { + s_sharedSchema.objectSchemaByName[schema.className] = schema; + } + + return schema; +} + +// Caller must @synchronize on s_localNameToClass +static void RLMRegisterClassLocalNames(Class *classes, NSUInteger count) { + for (NSUInteger i = 0; i < count; i++) { + Class cls = classes[i]; + if (!RLMIsObjectSubclass(cls)) { + continue; + } + + NSString *className = NSStringFromClass(cls); + if ([className hasPrefix:@"RLM:"]) { + continue; + } + + if ([RLMSwiftSupport isSwiftClassName:className]) { + className = [RLMSwiftSupport demangleClassName:className]; + } + // NSStringFromClass demangles the names for top-level Swift classes + // but not for nested classes. _T indicates it's a Swift symbol, t + // indicates it's a type, and C indicates it's a class. + else if ([className hasPrefix:@"_TtC"]) { + @throw RLMException(@"RLMObject subclasses cannot be nested within other declarations. Please move %@ to global scope.", className); + } + + if (Class existingClass = s_localNameToClass[className]) { + if (existingClass != cls) { + @throw RLMException(@"RLMObject subclasses with the same name cannot be included twice in the same target. " + @"Please make sure '%@' is only linked once to your current target.", className); + } + continue; + } + + s_localNameToClass[className] = cls; + RLMReplaceClassNameMethod(cls, className); + } +} + +- (instancetype)init { + self = [super init]; + if (self) { + _objectSchemaByName = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSArray *)objectSchema { + if (!_objectSchema) { + _objectSchema = [_objectSchemaByName allValues]; + } + return _objectSchema; +} + +- (void)setObjectSchema:(NSArray *)objectSchema { + _objectSchema = objectSchema; + _objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:objectSchema.count]; + for (RLMObjectSchema *object in objectSchema) { + [_objectSchemaByName setObject:object forKey:object.className]; + } +} + +- (RLMObjectSchema *)schemaForClassName:(NSString *)className { + if (RLMObjectSchema *schema = _objectSchemaByName[className]) { + return schema; // fast path for already-initialized schemas + } else if (Class cls = [RLMSchema classForString:className]) { + [cls sharedSchema]; // initialize the schema + return _objectSchemaByName[className]; // try again + } else { + return nil; + } +} + +- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)className { + RLMObjectSchema *schema = [self schemaForClassName:className]; + if (!schema) { + @throw RLMException(@"Object type '%@' not managed by the Realm", className); + } + return schema; +} + ++ (instancetype)schemaWithObjectClasses:(NSArray *)classes { + NSUInteger count = classes.count; + auto classArray = std::make_unique<__unsafe_unretained Class[]>(count); + [classes getObjects:classArray.get() range:NSMakeRange(0, count)]; + + RLMSchema *schema = [[self alloc] init]; + @synchronized(s_localNameToClass) { + RLMRegisterClassLocalNames(classArray.get(), count); + + schema->_objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:count]; + for (Class cls in classes) { + if (!RLMIsObjectSubclass(cls)) { + @throw RLMException(@"Can't add non-Object type '%@' to a schema.", cls); + } + schema->_objectSchemaByName[[cls className]] = RLMRegisterClass(cls); + } + } + + NSMutableArray *errors = [NSMutableArray new]; + // Verify that all of the targets of links are included in the class list + [schema->_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(id, RLMObjectSchema *objectSchema, BOOL *) { + for (RLMProperty *prop in objectSchema.properties) { + if (prop.type != RLMPropertyTypeObject && prop.type != RLMPropertyTypeArray) { + continue; + } + if (!schema->_objectSchemaByName[prop.objectClassName]) { + [errors addObject:[NSString stringWithFormat:@"- '%@.%@' links to class '%@', which is missing from the list of classes managed by the Realm", objectSchema.className, prop.name, prop.objectClassName]]; + } + } + }]; + if (errors.count) { + @throw RLMException(@"Invalid class subset list:\n%@", [errors componentsJoinedByString:@"\n"]); + } + + return schema; +} + ++ (RLMObjectSchema *)sharedSchemaForClass:(Class)cls { + @synchronized(s_localNameToClass) { + // We create instances of Swift objects during schema init, and they + // obviously need to not also try to initialize the schema + if (s_sharedSchemaState == SharedSchemaState::Initializing) { + return nil; + } + + RLMRegisterClassLocalNames(&cls, 1); + return RLMRegisterClass(cls); + } +} + ++ (instancetype)partialSharedSchema { + return s_sharedSchema; +} + +// schema based on runtime objects ++ (instancetype)sharedSchema { + @synchronized(s_localNameToClass) { + // We replace this method with one which just returns s_sharedSchema + // once initialization is complete, but we still need to check if it's + // already complete because it may have been done by another thread + // while we were waiting for the lock + if (s_sharedSchemaState == SharedSchemaState::Initialized) { + return s_sharedSchema; + } + + if (s_sharedSchemaState == SharedSchemaState::Initializing) { + @throw RLMException(@"Illegal recursive call of +[%@ %@]. Note: Properties of Swift `Object` classes must not be prepopulated with queried results from a Realm.", self, NSStringFromSelector(_cmd)); + } + + s_sharedSchemaState = SharedSchemaState::Initializing; + try { + // Make sure we've discovered all classes + { + unsigned int numClasses; + using malloc_ptr = std::unique_ptr<__unsafe_unretained Class[], decltype(&free)>; + malloc_ptr classes(objc_copyClassList(&numClasses), &free); + RLMRegisterClassLocalNames(classes.get(), numClasses); + } + + [s_localNameToClass enumerateKeysAndObjectsUsingBlock:^(NSString *, Class cls, BOOL *) { + RLMRegisterClass(cls); + }]; + } + catch (...) { + s_sharedSchemaState = SharedSchemaState::Uninitialized; + throw; + } + + // Replace this method with one that doesn't need to acquire a lock + Class metaClass = objc_getMetaClass(class_getName(self)); + IMP imp = imp_implementationWithBlock(^{ return s_sharedSchema; }); + class_replaceMethod(metaClass, @selector(sharedSchema), imp, "@@:"); + + s_sharedSchemaState = SharedSchemaState::Initialized; + } + + return s_sharedSchema; +} + +// schema based on tables in a realm ++ (instancetype)dynamicSchemaFromObjectStoreSchema:(Schema const&)objectStoreSchema { + // cache descriptors for all subclasses of RLMObject + NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:objectStoreSchema.size()]; + for (auto &objectSchema : objectStoreSchema) { + RLMObjectSchema *schema = [RLMObjectSchema objectSchemaForObjectStoreSchema:objectSchema]; + [schemaArray addObject:schema]; + } + + // set class array and mapping + RLMSchema *schema = [RLMSchema new]; + schema.objectSchema = schemaArray; + return schema; +} + ++ (Class)classForString:(NSString *)className { + if (Class cls = s_localNameToClass[className]) { + return cls; + } + + if (Class cls = NSClassFromString(className)) { + return RLMIsObjectSubclass(cls) ? cls : nil; + } + + // className might be the local name of a Swift class we haven't registered + // yet, so scan them all then recheck + { + unsigned int numClasses; + std::unique_ptr<__unsafe_unretained Class[], decltype(&free)> classes(objc_copyClassList(&numClasses), &free); + RLMRegisterClassLocalNames(classes.get(), numClasses); + } + + return s_localNameToClass[className]; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMSchema *schema = [[RLMSchema allocWithZone:zone] init]; + schema->_objectSchemaByName = [[NSMutableDictionary allocWithZone:zone] + initWithDictionary:_objectSchemaByName copyItems:YES]; + return schema; +} + +- (BOOL)isEqualToSchema:(RLMSchema *)schema { + if (_objectSchemaByName.count != schema->_objectSchemaByName.count) { + return NO; + } + __block BOOL matches = YES; + [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RLMObjectSchema *objectSchema, BOOL *stop) { + if (![schema->_objectSchemaByName[name] isEqualToObjectSchema:objectSchema]) { + *stop = YES; + matches = NO; + } + }]; + return matches; +} + +- (NSString *)description { + NSMutableString *objectSchemaString = [NSMutableString string]; + NSArray *sort = @[[NSSortDescriptor sortDescriptorWithKey:@"className" ascending:YES]]; + for (RLMObjectSchema *objectSchema in [self.objectSchema sortedArrayUsingDescriptors:sort]) { + [objectSchemaString appendFormat:@"\t%@\n", + [objectSchema.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + return [NSString stringWithFormat:@"Schema {\n%@}", objectSchemaString]; +} + +- (Schema)objectStoreCopy { + if (_objectStoreSchema.size() == 0) { + std::vector schema; + schema.reserve(_objectSchemaByName.count); + [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:[&](NSString *, RLMObjectSchema *objectSchema, BOOL *) { + schema.push_back(objectSchema.objectStoreCopy); + }]; + _objectStoreSchema = std::move(schema); + } + return _objectStoreSchema; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSwiftSupport.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSwiftSupport.m new file mode 100644 index 0000000..e16c79e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSwiftSupport.m @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMSwiftSupport.h" + +@implementation RLMSwiftSupport + ++ (BOOL)isSwiftClassName:(NSString *)className { + return [className rangeOfString:@"."].location != NSNotFound; +} + ++ (NSString *)demangleClassName:(NSString *)className { + return [className substringFromIndex:[className rangeOfString:@"."].location + 1]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm new file mode 100644 index 0000000..8625f5e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm @@ -0,0 +1,170 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncConfiguration_Private.hpp" + +#import "RLMSyncManager_Private.h" +#import "RLMSyncSession_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync/sync_manager.hpp" +#import "sync/sync_config.hpp" + +#import + +using namespace realm; + +namespace { +using ProtocolError = realm::sync::ProtocolError; + +RLMSyncSystemErrorKind errorKindForSyncError(SyncError error) { + if (error.is_client_reset_requested()) { + return RLMSyncSystemErrorKindClientReset; + } else if (error.error_code == ProtocolError::bad_authentication) { + return RLMSyncSystemErrorKindUser; + } else if (error.is_session_level_protocol_error()) { + return RLMSyncSystemErrorKindSession; + } else if (error.is_connection_level_protocol_error()) { + return RLMSyncSystemErrorKindConnection; + } else if (error.is_client_error()) { + return RLMSyncSystemErrorKindClient; + } else { + return RLMSyncSystemErrorKindUnknown; + } +} +} + +static BOOL isValidRealmURL(NSURL *url) { + NSString *scheme = [url scheme]; + if (![scheme isEqualToString:@"realm"] && ![scheme isEqualToString:@"realms"]) { + return NO; + } + return YES; +} + +@interface RLMSyncConfiguration () { + std::unique_ptr _config; +} + +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + customFileURL:(nullable NSURL *)customFileURL + stopPolicy:(RLMSyncStopPolicy)stopPolicy + errorHandler:(std::function)errorHandler; +@end + +@implementation RLMSyncConfiguration + +@dynamic stopPolicy; + +- (instancetype)initWithRawConfig:(realm::SyncConfig)config { + if (self = [super init]) { + _config = std::make_unique(config); + } + return self; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMSyncConfiguration class]]) { + return NO; + } + RLMSyncConfiguration *that = (RLMSyncConfiguration *)object; + return [self.realmURL isEqual:that.realmURL] + && [self.user isEqual:that.user] + && self.stopPolicy == that.stopPolicy; +} + +- (realm::SyncConfig)rawConfiguration { + return *_config; +} + +- (RLMSyncUser *)user { + return [[RLMSyncUser alloc] initWithSyncUser:_config->user]; +} + +- (RLMSyncStopPolicy)stopPolicy { + return translateStopPolicy(_config->stop_policy); +} + +- (void)setStopPolicy:(RLMSyncStopPolicy)stopPolicy { + _config->stop_policy = translateStopPolicy(stopPolicy); +} + +- (NSURL *)realmURL { + NSString *rawStringURL = @(_config->realm_url.c_str()); + return [NSURL URLWithString:rawStringURL]; +} + +- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url { + return [self initWithUser:user + realmURL:url + customFileURL:nil + stopPolicy:RLMSyncStopPolicyAfterChangesUploaded + errorHandler:nullptr]; +} + +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + customFileURL:(nullable NSURL *)customFileURL + stopPolicy:(RLMSyncStopPolicy)stopPolicy + errorHandler:(std::function)errorHandler { + if (self = [super init]) { + if (!isValidRealmURL(url)) { + @throw RLMException(@"The provided URL (%@) was not a valid Realm URL.", [url absoluteString]); + } + auto bindHandler = [=](auto&, + const SyncConfig& config, + const std::shared_ptr& session) { + [user _bindSessionWithConfig:config + session:session + completion:[RLMSyncManager sharedManager].sessionCompletionNotifier]; + }; + if (!errorHandler) { + errorHandler = [=](std::shared_ptr errored_session, + SyncError error) { + RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:error.user_info.size()]; + for (auto& pair : error.user_info) { + userInfo[@(pair.first.c_str())] = @(pair.second.c_str()); + } + // FIXME: how should the binding respond if the `is_fatal` bool is true? + [[RLMSyncManager sharedManager] _fireErrorWithCode:error.error_code.value() + message:@(error.message.c_str()) + isFatal:error.is_fatal + session:session + userInfo:userInfo + errorClass:errorKindForSyncError(error)]; + }; + } + + _config = std::make_unique(SyncConfig{ + [user _syncUser], + [[url absoluteString] UTF8String], + translateStopPolicy(stopPolicy), + std::move(bindHandler), + std::move(errorHandler) + }); + self.customFileURL = customFileURL; + return self; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncCredentials.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncCredentials.m new file mode 100644 index 0000000..03669e6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncCredentials.m @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncCredentials.h" +#import "RLMSyncUtil_Private.h" + +/// A Twitter account as an identity provider. +//extern RLMIdentityProvider const RLMIdentityProviderTwitter; + +RLMIdentityProvider const RLMIdentityProviderDebug = @"debug"; +RLMIdentityProvider const RLMIdentityProviderRealm = @"realm"; +RLMIdentityProvider const RLMIdentityProviderUsernamePassword = @"password"; +RLMIdentityProvider const RLMIdentityProviderFacebook = @"facebook"; +RLMIdentityProvider const RLMIdentityProviderTwitter = @"twitter"; +RLMIdentityProvider const RLMIdentityProviderGoogle = @"google"; +RLMIdentityProvider const RLMIdentityProviderCloudKit = @"cloudkit"; + +@interface RLMSyncCredentials () + +- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)token + provider:(RLMIdentityProvider)provider + userInfo:(NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readwrite) RLMSyncCredentialsToken token; +@property (nonatomic, readwrite) RLMIdentityProvider provider; +@property (nonatomic, readwrite) NSDictionary *userInfo; + +@end + +@implementation RLMSyncCredentials + ++ (instancetype)credentialsWithFacebookToken:(RLMSyncCredentialsToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderFacebook userInfo:nil]; +} + ++ (instancetype)credentialsWithGoogleToken:(RLMSyncCredentialsToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderGoogle userInfo:nil]; +} + ++ (instancetype)credentialsWithCloudKitToken:(RLMSyncCredentialsToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderCloudKit userInfo:nil]; +} + ++ (instancetype)credentialsWithUsername:(NSString *)username + password:(NSString *)password + register:(BOOL)shouldRegister { + return [[self alloc] initWithCustomToken:username + provider:RLMIdentityProviderUsernamePassword + userInfo:@{kRLMSyncPasswordKey: password, + kRLMSyncRegisterKey: @(shouldRegister)}]; +} + ++ (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity { + return [[self alloc] initWithCustomToken:accessToken + provider:RLMIdentityProviderAccessToken + userInfo:@{kRLMSyncIdentityKey: identity}]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMSyncCredentials class]]) { + return NO; + } + RLMSyncCredentials *that = (RLMSyncCredentials *)object; + return ([self.token isEqualToString:that.token] + && [self.provider isEqualToString:that.provider] + && [self.userInfo isEqual:that.userInfo]); +} + +- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)token + provider:(RLMIdentityProvider)provider + userInfo:(NSDictionary *)userInfo { + if (self = [super init]) { + self.token = token; + self.provider = provider; + self.userInfo = userInfo; + return self; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncErrorResponseModel.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncErrorResponseModel.m new file mode 100644 index 0000000..5e75139 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncErrorResponseModel.m @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncErrorResponseModel.h" + +static const NSString *const kRLMSyncErrorStatusKey = @"status"; +static const NSString *const kRLMSyncErrorCodeKey = @"code"; +static const NSString *const kRLMSyncErrorTitleKey = @"title"; +static const NSString *const kRLMSyncErrorHintKey = @"hint"; + +@interface RLMSyncErrorResponseModel () + +@property (nonatomic, readwrite) NSInteger status; +@property (nonatomic, readwrite) NSInteger code; +@property (nonatomic, readwrite) NSString *title; +@property (nonatomic, readwrite) NSString *hint; + +@end + +@implementation RLMSyncErrorResponseModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorStatusKey, status); + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorCodeKey, code); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorTitleKey, title); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorHintKey, hint); + return self; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncManager.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncManager.mm new file mode 100644 index 0000000..ea3d4bc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncManager.mm @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncManager_Private.h" + +#import "RLMRealmConfiguration+Sync.h" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncSession_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync/sync_config.hpp" +#import "sync/sync_manager.hpp" +#import "sync/sync_session.hpp" + +using namespace realm; +using Level = realm::util::Logger::Level; + +namespace { + +Level levelForSyncLogLevel(RLMSyncLogLevel logLevel) { + switch (logLevel) { + case RLMSyncLogLevelOff: return Level::off; + case RLMSyncLogLevelFatal: return Level::fatal; + case RLMSyncLogLevelError: return Level::error; + case RLMSyncLogLevelWarn: return Level::warn; + case RLMSyncLogLevelInfo: return Level::info; + case RLMSyncLogLevelDetail: return Level::detail; + case RLMSyncLogLevelDebug: return Level::debug; + case RLMSyncLogLevelTrace: return Level::trace; + case RLMSyncLogLevelAll: return Level::all; + } + REALM_UNREACHABLE(); // Unrecognized log level. +} + +RLMSyncLogLevel logLevelForLevel(Level logLevel) { + switch (logLevel) { + case Level::off: return RLMSyncLogLevelOff; + case Level::fatal: return RLMSyncLogLevelFatal; + case Level::error: return RLMSyncLogLevelError; + case Level::warn: return RLMSyncLogLevelWarn; + case Level::info: return RLMSyncLogLevelInfo; + case Level::detail: return RLMSyncLogLevelDetail; + case Level::debug: return RLMSyncLogLevelDebug; + case Level::trace: return RLMSyncLogLevelTrace; + case Level::all: return RLMSyncLogLevelAll; + } + REALM_UNREACHABLE(); // Unrecognized log level. +} + +struct CocoaSyncLogger : public realm::util::RootLogger { + void do_log(Level, std::string message) override { + NSLog(@"Sync: %@", RLMStringDataToNSString(message)); + } +}; + +struct CocoaSyncLoggerFactory : public realm::SyncLoggerFactory { + std::unique_ptr make_logger(realm::util::Logger::Level level) override { + auto logger = std::make_unique(); + logger->set_level_threshold(level); + return std::move(logger); + } +} s_syncLoggerFactory; + +} // anonymous namespace + +@interface RLMSyncManager () +- (instancetype)initWithCustomRootDirectory:(nullable NSURL *)rootDirectory NS_DESIGNATED_INITIALIZER; +@end + +@implementation RLMSyncManager + +static RLMSyncManager *s_sharedManager = nil; +static dispatch_once_t s_onceToken; + ++ (instancetype)sharedManager { + dispatch_once(&s_onceToken, ^{ + s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil]; + }); + return s_sharedManager; +} + +- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory { + if (self = [super init]) { + // Initialize the sync engine. + SyncManager::shared().set_logger_factory(s_syncLoggerFactory); + bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground(); + auto mode = should_encrypt ? SyncManager::MetadataMode::Encryption : SyncManager::MetadataMode::NoEncryption; + rootDirectory = rootDirectory ?: [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)]; + SyncManager::shared().configure_file_system(rootDirectory.path.UTF8String, mode, none, true); + return self; + } + return nil; +} + +- (NSString *)appID { + if (!_appID) { + _appID = [[NSBundle mainBundle] bundleIdentifier] ?: @"(none)"; + } + return _appID; +} + +#pragma mark - Passthrough properties + +- (RLMSyncLogLevel)logLevel { + return logLevelForLevel(realm::SyncManager::shared().log_level()); +} + +- (void)setLogLevel:(RLMSyncLogLevel)logLevel { + realm::SyncManager::shared().set_log_level(levelForSyncLogLevel(logLevel)); +} + +- (BOOL)disableSSLValidation { + return realm::SyncManager::shared().client_should_validate_ssl(); +} + +- (void)setDisableSSLValidation:(BOOL)disableSSLValidation { + realm::SyncManager::shared().set_client_should_validate_ssl(!disableSSLValidation); +} + +#pragma mark - Private API + +- (void)_fireError:(NSError *)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.errorHandler) { + self.errorHandler(error, nil); + } + }); +} + +- (void)_fireErrorWithCode:(int)errorCode + message:(NSString *)message + isFatal:(BOOL)fatal + session:(RLMSyncSession *)session + userInfo:(NSDictionary *)userInfo + errorClass:(RLMSyncSystemErrorKind)errorClass { + NSError *error = nil; + NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy]; + mutableUserInfo[@"description"] = message; + mutableUserInfo[@"error"] = @(errorCode); + mutableUserInfo[@"underlying_class"] = @(errorClass); + + switch (errorClass) { + case RLMSyncSystemErrorKindClientReset: { + // Client reset is a special case; the application can respond to it to a greater degree than + // it can for most other errors. + mutableUserInfo[kRLMSyncPathOfRealmBackupCopyKey] = userInfo[@(realm::SyncError::c_recovery_file_path_key)]; + std::string original_path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String]; + mutableUserInfo[kRLMSyncInitiateClientResetBlockKey] = ^{ + SyncManager::shared().immediately_run_file_actions(original_path); + }; + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientResetError + userInfo:mutableUserInfo]; + break; + } + case RLMSyncSystemErrorKindUser: + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientUserError + userInfo:mutableUserInfo]; + break; + case RLMSyncSystemErrorKindSession: + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:mutableUserInfo]; + break; + case RLMSyncSystemErrorKindConnection: + case RLMSyncSystemErrorKindClient: + // Report the error. There's nothing the user can do about it, though. + if (fatal) { + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientInternalError + userInfo:mutableUserInfo]; + } + break; + case RLMSyncSystemErrorKindUnknown: + break; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self.errorHandler || !error) { + return; + } + self.errorHandler(error, session); + }); +} + +- (NSArray *)_allUsers { + NSMutableArray *buffer = [NSMutableArray array]; + for (auto user : SyncManager::shared().all_logged_in_users()) { + [buffer addObject:[[RLMSyncUser alloc] initWithSyncUser:std::move(user)]]; + } + return buffer; +} + ++ (void)resetForTesting { + SyncManager::shared().reset_for_testing(); +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermission.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermission.m new file mode 100644 index 0000000..ee3b459 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermission.m @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermission_Private.h" + +@implementation RLMSyncPermission + ++ (NSArray *)requiredProperties { + return @[@"updatedAt", @"userId", @"path"]; +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + ++ (NSString *)_realmObjectName { + return @"Permission"; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionChange.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionChange.m new file mode 100644 index 0000000..55ba2fe --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionChange.m @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionChange_Private.h" +#import "RLMSyncUtil_Private.h" + +@implementation RLMSyncPermissionChange + ++ (instancetype)permissionChangeWithRealmURL:(NSString *)realmURL + userID:(NSString *)userID + read:(nullable NSNumber *)mayRead + write:(nullable NSNumber *)mayWrite + manage:(nullable NSNumber *)mayManage { + RLMSyncPermissionChange *permissionChange = [RLMSyncPermissionChange new]; + permissionChange.realmUrl = realmURL; + permissionChange.userId = userID; + permissionChange.mayRead = mayRead; + permissionChange.mayWrite = mayWrite; + permissionChange.mayManage = mayManage; + return permissionChange; +} + ++ (NSArray *)requiredProperties { + return @[@"id", @"createdAt", @"updatedAt", @"realmUrl", @"userId"]; +} + ++ (NSDictionary *)defaultPropertyValues { + NSDate *now = [NSDate date]; + return @{ + @"id": [NSUUID UUID].UUIDString, + @"createdAt": now, + @"updatedAt": now, + @"realmUrl": @"*", + @"userId": @"*" + }; +} + ++ (nullable NSString *)primaryKey { + return @"id"; +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + +- (RLMSyncManagementObjectStatus)status { + return RLMMakeSyncManagementObjectStatus(self.statusCode); +} + ++ (NSString *)_realmObjectName { + return @"PermissionChange"; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOffer.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOffer.m new file mode 100644 index 0000000..6f38a78 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOffer.m @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionOffer_Private.h" +#import "RLMSyncUtil_Private.h" + +@implementation RLMSyncPermissionOffer + ++ (instancetype)permissionOfferWithRealmURL:(NSString *)realmURL + expiresAt:(nullable NSDate *)expiresAt + read:(BOOL)mayRead + write:(BOOL)mayWrite + manage:(BOOL)mayManage { + RLMSyncPermissionOffer *permissionOffer = [RLMSyncPermissionOffer new]; + permissionOffer.realmUrl = realmURL; + permissionOffer.expiresAt = expiresAt; + permissionOffer.mayRead = mayRead; + permissionOffer.mayWrite = mayWrite; + permissionOffer.mayManage = mayManage; + return permissionOffer; +} + ++ (NSArray *)requiredProperties { + return @[@"id", @"createdAt", @"updatedAt", @"realmUrl"]; +} + ++ (NSArray *)indexedProperties { + return @[@"token"]; +} + ++ (NSDictionary *)defaultPropertyValues { + NSDate *now = [NSDate date]; + return @{ + @"id": [NSUUID UUID].UUIDString, + @"createdAt": now, + @"updatedAt": now, + @"realmUrl": @"" + }; +} + ++ (nullable NSString *)primaryKey { + return @"id"; +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + +- (RLMSyncManagementObjectStatus)status { + return RLMMakeSyncManagementObjectStatus(self.statusCode); +} + ++ (NSString *)_realmObjectName { + return @"PermissionOffer"; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m new file mode 100644 index 0000000..ab7b685 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionOfferResponse_Private.h" +#import "RLMSyncUtil_Private.h" + +@implementation RLMSyncPermissionOfferResponse + ++ (instancetype)permissionOfferResponseWithToken:(NSString *)token { + RLMSyncPermissionOfferResponse *permissionOfferResponse = [RLMSyncPermissionOfferResponse new]; + permissionOfferResponse.token = token; + return permissionOfferResponse; +} + ++ (NSArray *)requiredProperties { + return @[@"id", @"createdAt", @"updatedAt", @"token"]; +} + ++ (NSDictionary *)defaultPropertyValues { + NSDate *now = [NSDate date]; + return @{ + @"id": [NSUUID UUID].UUIDString, + @"createdAt": now, + @"updatedAt": now, + @"token": @"", + }; +} + ++ (nullable NSString *)primaryKey { + return @"id"; +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + +- (RLMSyncManagementObjectStatus)status { + return RLMMakeSyncManagementObjectStatus(self.statusCode); +} + ++ (NSString *)_realmObjectName { + return @"PermissionOfferResponse"; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSession.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSession.mm new file mode 100644 index 0000000..40ca6df --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSession.mm @@ -0,0 +1,184 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncSession_Private.hpp" + +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "sync/sync_session.hpp" + +using namespace realm; + +@interface RLMProgressNotificationToken() { + uint64_t _token; + std::weak_ptr _session; +} +@end + +@implementation RLMProgressNotificationToken + +- (void)suppressNextNotification { + // No-op, but implemented in case this token is passed to + // `-[RLMRealm commitWriteTransactionWithoutNotifying:]`. +} + +- (void)stop { + if (auto session = _session.lock()) { + session->unregister_progress_notifier(_token); + _session.reset(); + _token = 0; + } +} + +- (void)dealloc { + if (_token != 0) { + NSLog(@"RLMProgressNotificationToken released without unregistering a notification. " + @"You must hold on to the RLMProgressNotificationToken and call " + @"-[RLMProgressNotificationToken stop] when you no longer wish to receive " + @"progress update notifications."); + } +} + +- (nullable instancetype)initWithTokenValue:(uint64_t)token + session:(std::shared_ptr)session { + if (token == 0) { + return nil; + } + if (self = [super init]) { + _token = token; + _session = session; + return self; + } + return nil; +} + +@end + +@interface RLMSyncSession () +@property (class, nonatomic, readonly) dispatch_queue_t notificationsQueue; +@end + +@implementation RLMSyncSession + ++ (dispatch_queue_t)notificationsQueue { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("io.realm.sync.sessionsNotificationQueue", DISPATCH_QUEUE_SERIAL); + }); + return queue; +} + +- (instancetype)initWithSyncSession:(std::shared_ptr)session { + if (self = [super init]) { + _session = session; + return self; + } + return nil; +} + +- (RLMSyncConfiguration *)configuration { + if (auto session = _session.lock()) { + if (session->state() != SyncSession::PublicState::Error) { + return [[RLMSyncConfiguration alloc] initWithRawConfig:session->config()]; + } + } + return nil; +} + +- (NSURL *)realmURL { + if (auto session = _session.lock()) { + if (auto url = session->full_realm_url()) { + return [NSURL URLWithString:@(url->c_str())]; + } + } + return nil; +} + +- (RLMSyncUser *)parentUser { + if (auto session = _session.lock()) { + if (session->state() != SyncSession::PublicState::Error) { + return [[RLMSyncUser alloc] initWithSyncUser:session->user()]; + } + } + return nil; +} + +- (RLMSyncSessionState)state { + if (auto session = _session.lock()) { + if (session->state() == SyncSession::PublicState::Inactive) { + return RLMSyncSessionStateInactive; + } + if (session->state() != SyncSession::PublicState::Error) { + return RLMSyncSessionStateActive; + } + } + return RLMSyncSessionStateInvalid; +} + +- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(void))callback { + if (auto session = _session.lock()) { + if (session->state() == SyncSession::PublicState::Error) { + return NO; + } + queue = queue ?: dispatch_get_main_queue(); + session->wait_for_upload_completion([=](std::error_code) { // FIXME: report error to user + dispatch_async(queue, callback); + }); + return YES; + } + return NO; +} + +- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(void))callback { + if (auto session = _session.lock()) { + if (session->state() == SyncSession::PublicState::Error) { + return NO; + } + queue = queue ?: dispatch_get_main_queue(); + session->wait_for_download_completion([=](std::error_code) { // FIXME: report error to user + dispatch_async(queue, callback); + }); + return YES; + } + return NO; +} + +- (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction + mode:(RLMSyncProgress)mode + block:(RLMProgressNotificationBlock)block { + if (auto session = _session.lock()) { + if (session->state() == SyncSession::PublicState::Error) { + return nil; + } + dispatch_queue_t queue = RLMSyncSession.notificationsQueue; + auto notifier_direction = (direction == RLMSyncProgressDirectionUpload + ? SyncSession::NotifierType::upload + : SyncSession::NotifierType::download); + bool is_streaming = (mode == RLMSyncProgressReportIndefinitely); + uint64_t token = session->register_progress_notifier([=](uint64_t transferred, uint64_t transferrable) { + dispatch_async(queue, ^{ + block((NSUInteger)transferred, (NSUInteger)transferrable); + }); + }, notifier_direction, is_streaming); + return [[RLMProgressNotificationToken alloc] initWithTokenValue:token session:std::move(session)]; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm new file mode 100644 index 0000000..dae423e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm @@ -0,0 +1,261 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncSessionRefreshHandle.hpp" + +#import "RLMAuthResponseModel.h" +#import "RLMNetworkClient.h" +#import "RLMSyncManager_Private.h" +#import "RLMSyncUser_Private.hpp" +#import "RLMTokenModels.h" +#import "RLMUtil.hpp" + +#import "sync/sync_session.hpp" + +using namespace realm; + +@interface RLMSyncSessionRefreshHandle () { + std::weak_ptr _session; + std::shared_ptr _strongSession; +} + +@property (nonatomic, weak) RLMSyncUser *user; +@property (nonatomic, strong) NSString *pathToRealm; +@property (nonatomic) NSTimer *timer; + +@property (nonatomic) NSURL *realmURL; +@property (nonatomic, copy) RLMSyncBasicErrorReportingBlock completionBlock; + +@end + +@implementation RLMSyncSessionRefreshHandle + +- (instancetype)initWithRealmURL:(NSURL *)realmURL + user:(RLMSyncUser *)user + session:(std::shared_ptr)session + completionBlock:(RLMSyncBasicErrorReportingBlock)completionBlock { + if (self = [super init]) { + NSString *path = [realmURL path]; + self.pathToRealm = path; + self.user = user; + self.completionBlock = completionBlock; + self.realmURL = realmURL; + // For the initial bind, we want to prolong the session's lifetime. + _strongSession = std::move(session); + _session = _strongSession; + // Immediately fire off the network request. + [self _timerFired:nil]; + return self; + } + return nil; +} + +- (void)dealloc { + [self.timer invalidate]; +} + +- (void)invalidate { + _strongSession = nullptr; + [self.timer invalidate]; +} + ++ (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate { + static const NSTimeInterval refreshBuffer = 10; + NSDate *fireDate = [date dateByAddingTimeInterval:-refreshBuffer]; + // Only fire times in the future are valid. + return ([fireDate compare:nowDate] == NSOrderedDescending ? fireDate : nil); +} + +- (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires { + // Schedule the timer on the main queue. + // It's very likely that this method will be run on a side thread, for example + // on the thread that runs `NSURLSession`'s completion blocks. We can't be + // guaranteed that there's an existing runloop on those threads, and we don't want + // to create and start a new one if one doesn't already exist. + dispatch_async(dispatch_get_main_queue(), ^{ + [self.timer invalidate]; + NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires + nowDate:[NSDate date]]; + if (!fireDate) { + [self.user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + return; + } + self.timer = [[NSTimer alloc] initWithFireDate:fireDate + interval:0 + target:self + selector:@selector(_timerFired:) + userInfo:nil + repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; + }); +} + +/// Handler for network requests whose responses successfully parse into an auth response model. +- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model strongUser:(RLMSyncUser *)user { + // Success + std::shared_ptr session = _session.lock(); + if (!session) { + // The session is dead or in a fatal error state. + [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + [self invalidate]; + return NO; + } + bool success = session->state() != SyncSession::PublicState::Error; + if (success) { + // Calculate the resolved path. + NSString *resolvedURLString = nil; + RLMServerPath resolvedPath = model.accessToken.tokenData.path; + // Munge the path back onto the original URL, because the `sync` API expects an entire URL. + NSURLComponents *urlBuffer = [NSURLComponents componentsWithURL:self.realmURL + resolvingAgainstBaseURL:YES]; + urlBuffer.path = resolvedPath; + resolvedURLString = [[urlBuffer URL] absoluteString]; + if (!resolvedURLString) { + @throw RLMException(@"Resolved path returned from the server was invalid (%@).", resolvedPath); + } + // Pass the token and resolved path to the underlying sync subsystem. + session->refresh_access_token([model.accessToken.token UTF8String], {resolvedURLString.UTF8String}); + success = session->state() != SyncSession::PublicState::Error; + if (success) { + // Schedule a refresh. If we're successful we must already have `bind()`ed the session + // initially, so we can null out the strong pointer. + _strongSession = nullptr; + NSDate *expires = [NSDate dateWithTimeIntervalSince1970:model.accessToken.tokenData.expires]; + [self scheduleRefreshTimer:expires]; + } else { + // The session is dead or in a fatal error state. + [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + [self invalidate]; + } + } + if (self.completionBlock) { + self.completionBlock(success ? nil : [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + } + return success; +} + +/// Handler for network requests that failed before the JSON parsing stage. +- (BOOL)_handleFailedRequest:(NSError *)error strongUser:(RLMSyncUser *)user { + NSError *syncError; + if ([error.domain isEqualToString:RLMSyncErrorDomain]) { + // Network client may return sync related error + syncError = error; + } else { + // Something else went wrong + syncError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncUnderlyingErrorKey: error}]; + } + + if (self.completionBlock) { + self.completionBlock(syncError); + } + [[RLMSyncManager sharedManager] _fireError:syncError]; + // Certain errors related to network connectivity should trigger a retry. + NSDate *nextTryDate = nil; + if (error.domain == NSURLErrorDomain) { + switch (error.code) { + case NSURLErrorCannotConnectToHost: + case NSURLErrorNotConnectedToInternet: + case NSURLErrorNetworkConnectionLost: + case NSURLErrorTimedOut: + case NSURLErrorDNSLookupFailed: + case NSURLErrorCannotFindHost: + // FIXME: 10 seconds is an arbitrarily chosen value, consider rationalizing it. + nextTryDate = [NSDate dateWithTimeIntervalSinceNow:10]; + break; + default: + break; + } + } + if (!nextTryDate) { + // This error isn't a network failure error. Just invalidate the refresh handle and stop. + [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + [self invalidate]; + return NO; + } + // If we tried to initially bind the session and failed, we'll try again. However, each + // subsequent attempt will use a weak pointer to avoid prolonging the session's lifetime + // unnecessarily. + _strongSession = nullptr; + [self scheduleRefreshTimer:nextTryDate]; + return NO; +} + +/// Callback handler for network requests. +- (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json { + RLMSyncUser *user = self.user; + if (!user) { + return NO; + } + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:YES + requireRefreshToken:NO]; + if (model) { + return [self _handleSuccessfulRequest:model strongUser:user]; + } + // Otherwise, malformed JSON + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + [self.timer invalidate]; + if (self.completionBlock) { + self.completionBlock(error); + } + [[RLMSyncManager sharedManager] _fireError:error]; + return NO; + } else { + REALM_ASSERT(error); + return [self _handleFailedRequest:error strongUser:user]; + } +} + +- (void)_timerFired:(__unused NSTimer *)timer { + RLMSyncUser *user = self.user; + if (!user) { + return; + } + RLMServerToken refreshToken = user._refreshToken; + if (!refreshToken) { + [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + [self.timer invalidate]; + return; + } + + NSDictionary *json = @{ + kRLMSyncProviderKey: @"realm", + kRLMSyncPathKey: self.pathToRealm, + kRLMSyncDataKey: refreshToken, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + }; + + __weak RLMSyncSessionRefreshHandle *weakSelf = self; + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + [weakSelf _onRefreshCompletionWithError:error json:json]; + }; + [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + server:user.authenticationServer + JSON:json + completion:handler]; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUser.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUser.mm new file mode 100644 index 0000000..ba43d06 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUser.mm @@ -0,0 +1,301 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncUser_Private.hpp" + +#import "RLMAuthResponseModel.h" +#import "RLMNetworkClient.h" +#import "RLMSyncManager_Private.h" +#import "RLMSyncSession_Private.hpp" +#import "RLMSyncSessionRefreshHandle.hpp" +#import "RLMTokenModels.h" +#import "RLMUtil.hpp" + +#import "sync/sync_manager.hpp" +#import "sync/sync_session.hpp" +#import "sync/sync_user.hpp" + +using namespace realm; + +@interface RLMSyncUser () { + std::shared_ptr _user; +} + +- (instancetype)initWithAuthServer:(nullable NSURL *)authServer NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readwrite) NSURL *authenticationServer; + +/** + All 'refresh handles' associated with Realms opened by this user. A refresh handle is + an object that encapsulates the concept of periodically refreshing the Realm's access + token before it expires. Tokens are indexed by their paths (e.g. `/~/path/to/realm`). + */ +@property (nonatomic) NSMutableDictionary *refreshHandles; + +@end + +@implementation RLMSyncUser + +#pragma mark - static API + ++ (NSDictionary *)allUsers { + NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers]; + return [NSDictionary dictionaryWithObjects:allUsers + forKeys:[allUsers valueForKey:@"identity"]]; +} + ++ (RLMSyncUser *)currentUser { + NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers]; + if (allUsers.count > 1) { + @throw RLMException(@"+currentUser cannot be called if more that one valid, logged-in user exists."); + } + return allUsers.firstObject; +} + +#pragma mark - API + +- (instancetype)initWithAuthServer:(nullable NSURL *)authServer { + if (self = [super init]) { + self.authenticationServer = authServer; + self.refreshHandles = [NSMutableDictionary dictionary]; + return self; + } + return nil; +} + +- (instancetype)initWithSyncUser:(std::shared_ptr)user { + NSString *rawServerURL = @(user->server_url().c_str()); + if (self = [self initWithAuthServer:[NSURL URLWithString:rawServerURL]]) { + _user = user; + return self; + } + return nil; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMSyncUser class]]) { + return NO; + } + return _user == ((RLMSyncUser *)object)->_user; +} + ++ (void)logInWithCredentials:(RLMSyncCredentials *)credential + authServerURL:(NSURL *)authServerURL + onCompletion:(RLMUserCompletionBlock)completion { + [self logInWithCredentials:credential + authServerURL:authServerURL + timeout:30 + onCompletion:completion]; +} + ++ (void)logInWithCredentials:(RLMSyncCredentials *)credential + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + onCompletion:(RLMUserCompletionBlock)completion { + RLMSyncUser *user = [[RLMSyncUser alloc] initWithAuthServer:authServerURL]; + [RLMSyncUser _performLogInForUser:user + credentials:credential + authServerURL:authServerURL + timeout:timeout + completionBlock:completion]; +} + +- (void)logOut { + if (!_user) { + return; + } + _user->log_out(); + for (id key in self.refreshHandles) { + [self.refreshHandles[key] invalidate]; + } + [self.refreshHandles removeAllObjects]; +} + +- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url { + if (!_user) { + return nil; + } + auto path = SyncManager::shared().path_for_realm(_user->identity(), [url.absoluteString UTF8String]); + if (auto session = _user->session_for_on_disk_path(path)) { + return [[RLMSyncSession alloc] initWithSyncSession:session]; + } + return nil; +} + +- (NSArray *)allSessions { + if (!_user) { + return @[]; + } + NSMutableArray *buffer = [NSMutableArray array]; + auto sessions = _user->all_sessions(); + for (auto session : sessions) { + [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]]; + } + return [buffer copy]; +} + +- (NSString *)identity { + if (!_user) { + return nil; + } + return @(_user->identity().c_str()); +} + +- (RLMSyncUserState)state { + if (!_user) { + return RLMSyncUserStateError; + } + switch (_user->state()) { + case SyncUser::State::Active: + return RLMSyncUserStateActive; + case SyncUser::State::LoggedOut: + return RLMSyncUserStateLoggedOut; + case SyncUser::State::Error: + return RLMSyncUserStateError; + } +} + +- (RLMRealm *)managementRealmWithError:(NSError **)error { + return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration managementConfigurationForUser:self] error:error]; +} + +- (RLMRealm *)permissionRealmWithError:(NSError **)error { + return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration permissionConfigurationForUser:self] error:error]; +} + +- (BOOL)isAdmin { + if (!_user) { + return NO; + } + return _user->is_admin(); +} + +#pragma mark - Private API + +- (void)_unregisterRefreshHandleForURLPath:(NSString *)path { + [self.refreshHandles removeObjectForKey:path]; +} + +- (NSString *)_refreshToken { + if (!_user) { + return nil; + } + return @(_user->refresh_token().c_str()); +} + +- (void)_bindSessionWithConfig:(const SyncConfig&)config + session:(std::shared_ptr)session + completion:(RLMSyncBasicErrorReportingBlock)completion { + // Create a refresh handle, and have it handle all the work. + NSURL *realmURL = [NSURL URLWithString:@(config.realm_url.c_str())]; + NSString *path = [realmURL path]; + REALM_ASSERT(realmURL && path); + [self.refreshHandles[path] invalidate]; + self.refreshHandles[path] = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL + user:self + session:std::move(session) + completionBlock:completion]; +} + +- (std::shared_ptr)_syncUser { + return _user; +} + ++ (void)_performLogInForUser:(RLMSyncUser *)user + credentials:(RLMSyncCredentials *)credentials + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + completionBlock:(RLMUserCompletionBlock)completion { + // Special credential login should be treated differently. + if (credentials.provider == RLMIdentityProviderAccessToken) { + [self _performLoginForDirectAccessTokenCredentials:credentials user:user completionBlock:completion]; + return; + } + + // Prepare login network request + NSMutableDictionary *json = [@{ + kRLMSyncProviderKey: credentials.provider, + kRLMSyncDataKey: credentials.token, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + } mutableCopy]; + NSMutableDictionary *info = [(credentials.userInfo ?: @{}) mutableCopy]; + + if ([info count] > 0) { + // Munge user info into the JSON request. + json[@"user_info"] = info; + } + + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:NO + requireRefreshToken:YES]; + if (!model) { + // Malformed JSON + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + completion(nil, error); + return; + } else { + std::string server_url = authServerURL.absoluteString.UTF8String; + auto sync_user = SyncManager::shared().get_user([model.refreshToken.tokenData.identity UTF8String], + [model.refreshToken.token UTF8String], + std::move(server_url)); + if (!sync_user) { + completion(nil, [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + return; + } + sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin); + user->_user = sync_user; + completion(user, nil); + } + } else { + // Something else went wrong + completion(nil, error); + } + }; + [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + server:authServerURL + JSON:json + timeout:timeout + completion:handler]; +} + ++ (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials + user:(RLMSyncUser *)user + completionBlock:(nonnull RLMUserCompletionBlock)completion { + NSString *identity = credentials.userInfo[kRLMSyncIdentityKey]; + NSAssert(identity != nil, @"Improperly created direct access token credential."); + auto sync_user = SyncManager::shared().get_user([identity UTF8String], + [credentials.token UTF8String], + none, + SyncUser::TokenType::Admin); + if (!sync_user) { + completion(nil, [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + return; + } + user->_user = sync_user; + completion(user, nil); +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUtil.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUtil.mm new file mode 100644 index 0000000..21874df --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMSyncUtil.mm @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncUtil_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMRealmConfiguration+Sync.h" +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMSyncPermission.h" +#import "RLMSyncPermissionChange.h" +#import "RLMSyncPermissionOffer.h" +#import "RLMSyncPermissionOfferResponse.h" + +#import "shared_realm.hpp" + +#import "sync/sync_user.hpp" + +static RLMRealmConfiguration *RLMRealmSpecialPurposeConfiguration(RLMSyncUser *user, NSString *realmName) { + NSURLComponents *components = [NSURLComponents componentsWithURL:user.authenticationServer resolvingAgainstBaseURL:NO]; + if ([components.scheme isEqualToString:@"https"]) { + components.scheme = @"realms"; + } else { + components.scheme = @"realm"; + } + components.path = [NSString stringWithFormat:@"/~/%@", realmName]; + NSURL *realmURL = components.URL; + RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:realmURL]; + RLMRealmConfiguration *config = [RLMRealmConfiguration new]; + config.syncConfiguration = syncConfig; + return config; +} + +@implementation RLMRealmConfiguration (RealmSync) ++ (instancetype)managementConfigurationForUser:(RLMSyncUser *)user { + RLMRealmConfiguration *config = RLMRealmSpecialPurposeConfiguration(user, @"__management"); + config.objectClasses = @[RLMSyncPermissionChange.class, RLMSyncPermissionOffer.class, RLMSyncPermissionOfferResponse.class]; + return config; +} + ++ (instancetype)permissionConfigurationForUser:(RLMSyncUser *)user { + RLMRealmConfiguration *config = RLMRealmSpecialPurposeConfiguration(user, @"__permission"); + config.objectClasses = @[RLMSyncPermission.class]; + return config; +} +@end + +RLMIdentityProvider const RLMIdentityProviderAccessToken = @"_access_token"; + +NSString *const RLMSyncErrorDomain = @"io.realm.sync"; + +NSString *const kRLMSyncPathOfRealmBackupCopyKey = @"recovered_realm_location_path"; +NSString *const kRLMSyncInitiateClientResetBlockKey = @"initiate_client_reset_block"; + +NSString *const kRLMSyncAppIDKey = @"app_id"; +NSString *const kRLMSyncDataKey = @"data"; +NSString *const kRLMSyncErrorJSONKey = @"json"; +NSString *const kRLMSyncErrorStatusCodeKey = @"statusCode"; +NSString *const kRLMSyncIdentityKey = @"identity"; +NSString *const kRLMSyncPasswordKey = @"password"; +NSString *const kRLMSyncPathKey = @"path"; +NSString *const kRLMSyncProviderKey = @"provider"; +NSString *const kRLMSyncRegisterKey = @"register"; +NSString *const kRLMSyncUnderlyingErrorKey = @"underlying_error"; + +namespace realm { + +SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy) { + switch (stopPolicy) { + case RLMSyncStopPolicyImmediately: return SyncSessionStopPolicy::Immediately; + case RLMSyncStopPolicyLiveIndefinitely: return SyncSessionStopPolicy::LiveIndefinitely; + case RLMSyncStopPolicyAfterChangesUploaded: return SyncSessionStopPolicy::AfterChangesUploaded; + } + REALM_UNREACHABLE(); // Unrecognized stop policy. +} + +RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy) +{ + switch (stop_policy) { + case SyncSessionStopPolicy::Immediately: return RLMSyncStopPolicyImmediately; + case SyncSessionStopPolicy::LiveIndefinitely: return RLMSyncStopPolicyLiveIndefinitely; + case SyncSessionStopPolicy::AfterChangesUploaded: return RLMSyncStopPolicyAfterChangesUploaded; + } + REALM_UNREACHABLE(); +} + +std::shared_ptr sync_session_for_realm(RLMRealm *realm) +{ + Realm::Config realmConfig = realm.configuration.config; + if (auto config = realmConfig.sync_config) { + std::shared_ptr user = config->user; + if (user && user->state() != SyncUser::State::Error) { + return user->session_for_on_disk_path(realmConfig.path); + } + } + return nullptr; +} + +} + +RLMSyncManagementObjectStatus RLMMakeSyncManagementObjectStatus(NSNumber *statusCode) { + if (!statusCode) { + return RLMSyncManagementObjectStatusNotProcessed; + } + if (statusCode.integerValue == 0) { + return RLMSyncManagementObjectStatusSuccess; + } + return RLMSyncManagementObjectStatusError; +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm new file mode 100644 index 0000000..7149fa4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMThreadSafeReference_Private.hpp" +#import "RLMUtil.hpp" + +template +static auto translateErrors(Function&& f) { + try { + return f(); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +@implementation RLMThreadSafeReference { + std::unique_ptr _reference; + id _metadata; + Class _type; +} + +- (instancetype)initWithThreadConfined:(id)threadConfined { + if (!(self = [super init])) { + return nil; + } + + REALM_ASSERT_DEBUG([threadConfined conformsToProtocol:@protocol(RLMThreadConfined)]); + if (![threadConfined conformsToProtocol:@protocol(RLMThreadConfined_Private)]) { + @throw RLMException(@"Illegal custom conformance to `RLMThreadConfined` by `%@`", threadConfined.class); + } else if (threadConfined.invalidated) { + @throw RLMException(@"Cannot construct reference to invalidated object"); + } else if (!threadConfined.realm) { + @throw RLMException(@"Cannot construct reference to unmanaged object, " + "which can be passed across threads directly"); + } + + translateErrors([&] { + _reference = [(id)threadConfined makeThreadSafeReference]; + _metadata = ((id)threadConfined).objectiveCMetadata; + }); + _type = threadConfined.class; + + return self; +} + ++ (instancetype)referenceWithThreadConfined:(id)threadConfined { + return [[self alloc] initWithThreadConfined:threadConfined]; +} + +- (id)resolveReferenceInRealm:(RLMRealm *)realm { + if (!_reference) { + @throw RLMException(@"Can only resolve a thread safe reference once."); + } + return translateErrors([&] { + return [_type objectWithThreadSafeReference:std::move(_reference) metadata:_metadata realm:realm]; + }); +} + +- (BOOL)isInvalidated { + return !_reference; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMTokenModels.m b/Desafio Mobile iOS/Pods/Realm/Realm/RLMTokenModels.m new file mode 100644 index 0000000..6a7bf9c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMTokenModels.m @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMTokenModels.h" +#import "RLMSyncUtil_Private.h" + +static const NSString *const kRLMSyncTokenDataKey = @"token_data"; +static const NSString *const kRLMSyncTokenKey = @"token"; +static const NSString *const kRLMSyncExpiresKey = @"expires"; +static const NSString *const kRLMSyncIsAdminKey = @"is_admin"; + +@interface RLMTokenDataModel () + +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) NSString *appID; +@property (nonatomic, readwrite) NSString *path; +@property (nonatomic, readwrite) NSTimeInterval expires; +@property (nonatomic, readwrite) BOOL isAdmin; +//@property (nonatomic, readwrite) NSArray *access; + +@end + +@implementation RLMTokenDataModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + self.isAdmin = NO; + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncIdentityKey, identity); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncAppIDKey, appID); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_OPTIONAL_BOOL(jsonDictionary, kRLMSyncIsAdminKey, isAdmin); + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncExpiresKey, expires); + return self; + } + return nil; +} + +@end + +@interface RLMTokenModel () + +@property (nonatomic, readwrite) NSString *token; +@property (nonatomic, nullable, readwrite) NSString *path; +@property (nonatomic, readwrite) RLMTokenDataModel *tokenData; + +@end + +@implementation RLMTokenModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncTokenKey, token); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncTokenDataKey, RLMTokenDataModel, tokenData); + return self; + } + return nil; +} + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMUpdateChecker.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMUpdateChecker.mm new file mode 100644 index 0000000..e282ee8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMUpdateChecker.mm @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMUpdateChecker.hpp" + +#import "RLMRealm.h" +#import "RLMUtil.hpp" + +#if TARGET_IPHONE_SIMULATOR && !defined(REALM_COCOA_VERSION) +#import "RLMVersion.h" +#endif + +void RLMCheckForUpdates() { +#if TARGET_IPHONE_SIMULATOR + if (getenv("REALM_DISABLE_UPDATE_CHECKER") || RLMIsRunningInPlayground()) { + return; + } + + auto handler = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error || ((NSHTTPURLResponse *)response).statusCode != 200) { + return; + } + + NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (![REALM_COCOA_VERSION isEqualToString:latestVersion]) { + NSLog(@"Version %@ of Realm is now available: https://github.com/realm/realm-cocoa/blob/v%@/CHANGELOG.md", latestVersion, latestVersion); + } + }; + + NSString *url = [NSString stringWithFormat:@"https://static.realm.io/update/cocoa?%@", REALM_COCOA_VERSION]; + [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url] completionHandler:handler] resume]; +#endif +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/RLMUtil.mm b/Desafio Mobile iOS/Pods/Realm/Realm/RLMUtil.mm new file mode 100644 index 0000000..a058086 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/RLMUtil.mm @@ -0,0 +1,418 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMUtil.hpp" + +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" + +#import "shared_realm.hpp" + +#import +#import + +#include +#include + +#if !defined(REALM_COCOA_VERSION) +#import "RLMVersion.h" +#endif + +static inline bool nsnumber_is_like_integer(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(bool) || + data_type == *@encode(char) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long); +} + +static inline bool nsnumber_is_like_bool(__unsafe_unretained NSNumber *const obj) +{ + // @encode(BOOL) is 'B' on iOS 64 and 'c' + // objcType is always 'c'. Therefore compare to "c". + if ([obj objCType][0] == 'c') { + return true; + } + + if (nsnumber_is_like_integer(obj)) { + int value = [obj intValue]; + return value == 0 || value == 1; + } + + return false; +} + +static inline bool nsnumber_is_like_float(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(float) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long) || + // A double is like float if it fits within float bounds + (data_type == *@encode(double) && ABS([obj doubleValue]) <= FLT_MAX); +} + +static inline bool nsnumber_is_like_double(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(double) || + data_type == *@encode(float) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long); +} + +BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const property) { + if (property.optional && !RLMCoerceToNil(obj)) { + return YES; + } + + switch (property.type) { + case RLMPropertyTypeString: + return [obj isKindOfClass:[NSString class]]; + case RLMPropertyTypeBool: + if ([obj isKindOfClass:[NSNumber class]]) { + return nsnumber_is_like_bool(obj); + } + return NO; + case RLMPropertyTypeDate: + return [obj isKindOfClass:[NSDate class]]; + case RLMPropertyTypeInt: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_integer(number); + } + return NO; + case RLMPropertyTypeFloat: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_float(number); + } + return NO; + case RLMPropertyTypeDouble: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_double(number); + } + return NO; + case RLMPropertyTypeData: + return [obj isKindOfClass:[NSData class]]; + case RLMPropertyTypeAny: + return NO; + case RLMPropertyTypeLinkingObjects: + return YES; + case RLMPropertyTypeObject: { + // only NSNull, nil, or objects which derive from RLMObject and match the given + // object class are valid + RLMObjectBase *objBase = RLMDynamicCast(obj); + return objBase && [objBase->_objectSchema.className isEqualToString:property.objectClassName]; + } + case RLMPropertyTypeArray: { + if (RLMArray *array = RLMDynamicCast(obj)) { + return [array.objectClassName isEqualToString:property.objectClassName]; + } + if (RLMListBase *list = RLMDynamicCast(obj)) { + return [list._rlmArray.objectClassName isEqualToString:property.objectClassName]; + } + if ([obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + // check each element for compliance + for (id el in (id)obj) { + RLMObjectBase *obj = RLMDynamicCast(el); + if (!obj || ![obj->_objectSchema.className isEqualToString:property.objectClassName]) { + return NO; + } + } + return YES; + } + if (!obj || obj == NSNull.null) { + return YES; + } + return NO; + } + } + @throw RLMException(@"Invalid RLMPropertyType specified"); +} + +void RLMValidateValueForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const prop) { + switch (prop.type) { + case RLMPropertyTypeString: + case RLMPropertyTypeBool: + case RLMPropertyTypeDate: + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + case RLMPropertyTypeData: + if (!RLMIsObjectValidForProperty(obj, prop)) { + @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); + } + break; + case RLMPropertyTypeObject: + break; + case RLMPropertyTypeArray: { + if (obj && obj != NSNull.null && ![obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + @throw RLMException(@"Array property value (%@) is not enumerable.", obj); + } + break; + } + case RLMPropertyTypeAny: + case RLMPropertyTypeLinkingObjects: + // It should not be possible to have either of these property types + // in the persisted properties array + REALM_UNREACHABLE(); + } +} + +NSDictionary *RLMDefaultValuesForObjectSchema(__unsafe_unretained RLMObjectSchema *const objectSchema) { + if (!objectSchema.isSwiftClass) { + return [objectSchema.objectClass defaultPropertyValues]; + } + + NSMutableDictionary *defaults = nil; + if ([objectSchema.objectClass isSubclassOfClass:RLMObject.class]) { + defaults = [NSMutableDictionary dictionaryWithDictionary:[objectSchema.objectClass defaultPropertyValues]]; + } + else { + defaults = [NSMutableDictionary dictionary]; + } + RLMObject *defaultObject = [[objectSchema.objectClass alloc] init]; + for (RLMProperty *prop in objectSchema.properties) { + if (!defaults[prop.name] && defaultObject[prop.name]) { + defaults[prop.name] = defaultObject[prop.name]; + } + } + return defaults; +} + +static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) { + NSMutableDictionary *userInfo = @{RLMRealmVersionKey: REALM_COCOA_VERSION, + RLMRealmCoreVersionKey: @REALM_VERSION}.mutableCopy; + if (additionalUserInfo != nil) { + [userInfo addEntriesFromDictionary:additionalUserInfo]; + } + NSException *e = [NSException exceptionWithName:RLMExceptionName + reason:reason + userInfo:userInfo]; + return e; +} + +NSException *RLMException(NSString *fmt, ...) { + va_list args; + va_start(args, fmt); + NSException *e = RLMException([[NSString alloc] initWithFormat:fmt arguments:args], @{}); + va_end(args); + return e; +} + +NSException *RLMException(std::exception const& exception) { + return RLMException(@"%s", exception.what()); +} + +NSError *RLMMakeError(RLMError code, std::exception const& exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + @"Error Code": @(code)}]; +} + +NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError& exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + NSFilePathErrorKey: @(exception.get_path().c_str()), + @"Error Code": @(code)}]; +} + +NSError *RLMMakeError(RLMError code, const realm::RealmFileException& exception) { + NSString *underlying = @(exception.underlying().c_str()); + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + NSFilePathErrorKey: @(exception.path().c_str()), + @"Error Code": @(code), + @"Underlying": underlying.length == 0 ? @"n/a" : underlying}]; +} + +NSError *RLMMakeError(std::system_error const& exception) { + BOOL isGenericCategoryError = (exception.code().category() == std::generic_category()); + NSString *category = @(exception.code().category().name()); + NSString *errorDomain = isGenericCategoryError ? NSPOSIXErrorDomain : RLMUnknownSystemErrorDomain; + + return [NSError errorWithDomain:errorDomain + code:exception.code().value() + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + @"Error Code": @(exception.code().value()), + @"Category": category}]; +} + +NSError *RLMMakeError(NSException *exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:0 + userInfo:@{NSLocalizedDescriptionKey: exception.reason}]; +} + +void RLMSetErrorOrThrow(NSError *error, NSError **outError) { + if (outError) { + *outError = error; + } + else { + NSString *msg = error.localizedDescription; + if (error.userInfo[NSFilePathErrorKey]) { + msg = [NSString stringWithFormat:@"%@: %@", error.userInfo[NSFilePathErrorKey], error.localizedDescription]; + } + @throw RLMException(msg, @{NSUnderlyingErrorKey: error}); + } +} + +// Determines if class1 descends from class2 +static inline BOOL RLMIsSubclass(Class class1, Class class2) { + class1 = class_getSuperclass(class1); + return RLMIsKindOfClass(class1, class2); +} + +static bool treatFakeObjectAsRLMObject = false; + +void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) { + treatFakeObjectAsRLMObject = flag; +} + +BOOL RLMIsObjectOrSubclass(Class klass) { + if (RLMIsKindOfClass(klass, RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return RLMIsKindOfClass(klass, FakeObjectClass); + } + return NO; +} + +BOOL RLMIsObjectSubclass(Class klass) { + if (RLMIsSubclass(class_getSuperclass(klass), RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return RLMIsSubclass(klass, FakeObjectClass); + } + return NO; +} + +BOOL RLMIsDebuggerAttached() +{ + int name[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid() + }; + + struct kinfo_proc info; + size_t info_size = sizeof(info); + if (sysctl(name, sizeof(name)/sizeof(name[0]), &info, &info_size, NULL, 0) == -1) { + NSLog(@"sysctl() failed: %s", strerror(errno)); + return false; + } + + return (info.kp_proc.p_flag & P_TRACED) != 0; +} + +BOOL RLMIsRunningInPlayground() { + return [[NSBundle mainBundle].bundleIdentifier hasPrefix:@"com.apple.dt.playground."]; +} + +id RLMMixedToObjc(realm::Mixed const& mixed) { + switch (mixed.get_type()) { + case realm::type_String: + return RLMStringDataToNSString(mixed.get_string()); + case realm::type_Int: + return @(mixed.get_int()); + case realm::type_Float: + return @(mixed.get_float()); + case realm::type_Double: + return @(mixed.get_double()); + case realm::type_Bool: + return @(mixed.get_bool()); + case realm::type_Timestamp: + return RLMTimestampToNSDate(mixed.get_timestamp()); + case realm::type_Binary: + return RLMBinaryDataToNSData(mixed.get_binary()); + case realm::type_Link: + case realm::type_LinkList: + default: + @throw RLMException(@"Invalid data type for RLMPropertyTypeAny property."); + } +} + +NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier) { +#if TARGET_OS_TV + (void)bundleIdentifier; + // tvOS prohibits writing to the Documents directory, so we use the Library/Caches directory instead. + return NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; +#elif TARGET_OS_IPHONE + (void)bundleIdentifier; + // On iOS the Documents directory isn't user-visible, so put files there + return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; +#else + // On OS X it is, so put files in Application Support. If we aren't running + // in a sandbox, put it in a subdirectory based on the bundle identifier + // to avoid accidentally sharing files between applications + NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0]; + if (![[NSProcessInfo processInfo] environment][@"APP_SANDBOX_CONTAINER_ID"]) { + if (!bundleIdentifier) { + bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; + } + if (!bundleIdentifier) { + bundleIdentifier = [NSBundle mainBundle].executablePath.lastPathComponent; + } + + path = [path stringByAppendingPathComponent:bundleIdentifier]; + + // create directory + [[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } + return path; +#endif +} diff --git a/Desafio Mobile iOS/Pods/Realm/Realm/Realm.modulemap b/Desafio Mobile iOS/Pods/Realm/Realm/Realm.modulemap new file mode 100644 index 0000000..ef2d102 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/Realm/Realm.modulemap @@ -0,0 +1,29 @@ +framework module Realm { + umbrella header "Realm.h" + + export * + module * { export * } + + explicit module Private { + header "RLMAccessor.h" + header "RLMArray_Private.h" + header "RLMListBase.h" + header "RLMObjectBase_Dynamic.h" + header "RLMObjectSchema_Private.h" + header "RLMObjectStore.h" + header "RLMObject_Private.h" + header "RLMOptionalBase.h" + header "RLMProperty_Private.h" + header "RLMRealmConfiguration_Private.h" + header "RLMRealm_Private.h" + header "RLMResults_Private.h" + header "RLMSchema_Private.h" + header "RLMSyncConfiguration_Private.h" + header "RLMSyncUtil_Private.h" + } + + explicit module Dynamic { + header "RLMRealm_Dynamic.h" + header "RLMObjectBase_Dynamic.h" + } +} diff --git a/Desafio Mobile iOS/Pods/Realm/build.sh b/Desafio Mobile iOS/Pods/Realm/build.sh new file mode 100755 index 0000000..15befb5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/build.sh @@ -0,0 +1,1434 @@ +#!/bin/sh + +################################################################################## +# Custom build tool for Realm Objective-C binding. +# +# (C) Copyright 2011-2015 by realm.io. +################################################################################## + +# Warning: pipefail is not a POSIX compatible option, but on OS X it works just fine. +# OS X uses a POSIX complain version of bash as /bin/sh, but apparently it does +# not strip away this feature. Also, this will fail if somebody forces the script +# to be run with zsh. +set -o pipefail +set -e + +source_root="$(dirname "$0")" + +# You can override the version of the core library +: ${REALM_CORE_VERSION:=$(sed -n 's/^REALM_CORE_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} # set to "current" to always use the current build + +: ${REALM_SYNC_VERSION:=$(sed -n 's/^REALM_SYNC_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} + +: ${REALM_OBJECT_SERVER_VERSION:=$(sed -n 's/^REALM_OBJECT_SERVER_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} + +# You can override the xcmode used +: ${XCMODE:=xcodebuild} # must be one of: xcodebuild (default), xcpretty, xctool + +# Provide a fallback value for TMPDIR, relevant for Xcode Bots +: ${TMPDIR:=$(getconf DARWIN_USER_TEMP_DIR)} + +PATH=/usr/libexec:$PATH + +if ! [ -z "${JENKINS_HOME}" ]; then + XCPRETTY_PARAMS="--no-utf --report junit --output build/reports/junit.xml" + CODESIGN_PARAMS="CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO" +fi + +usage() { +cat </dev/null + done +} + +download_object_server() { + local archive_name="realm-object-server-bundled_node_darwin-developer-$REALM_OBJECT_SERVER_VERSION.tar.gz" + curl -L -O "https://static.realm.io/downloads/object-server/$archive_name" + rm -rf sync + mkdir sync + tar xf $archive_name -C sync + rm $archive_name + cp Configuration/object-server-config.yml sync/object-server/configuration.yml + touch "sync/object-server/do_not_open_browser" +} + +download_common() { + local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path + + if [ "$download_type" == "core" ]; then + version=$REALM_CORE_VERSION + url="https://static.realm.io/downloads/core/realm-core-${version}.tar.xz" + elif [ "$download_type" == "sync" ]; then + version=$REALM_SYNC_VERSION + url="https://static.realm.io/downloads/sync/realm-sync-cocoa-${version}.tar.xz" + else + echo "Unknown dowload_type: $download_type" + exit 1 + fi + + echo "Downloading dependency: ${download_type} ${version}" + + if [ -z "$TMPDIR" ]; then + TMPDIR='/tmp' + fi + temp_dir=$(dirname "$TMPDIR/waste")/${download_type}_bin + mkdir -p "$temp_dir" + tar_path="${temp_dir}/${download_type}-${version}.tar.xz" + temp_path="${tar_path}.tmp" + + while [ 0 -lt $tries_left ] && [ ! -f "$tar_path" ]; do + if ! error=$(curl --fail --silent --show-error --location "$url" --output "$temp_path" 2>&1); then + tries_left=$[$tries_left-1] + else + mv "$temp_path" "$tar_path" + fi + done + + if [ ! -f "$tar_path" ]; then + printf "Downloading ${download_type} failed:\n\t$url\n\t$error\n" + exit 1 + fi + + ( + cd "$temp_dir" + rm -rf "$download_type" + tar xf "$tar_path" --xz + mv core "${download_type}-${version}" + ) + + rm -rf "${download_type}-${version}" core + mv "${temp_dir}/${download_type}-${version}" . + ln -s "${download_type}-${version}" core +} + +download_core() { + download_common "core" +} + +download_sync() { + download_common "sync" +} + +###################################### +# Variables +###################################### + +COMMAND="$1" + +# Use Debug config if command ends with -debug, otherwise default to Release +# Set IS_RUNNING_PACKAGING when running packaging steps to avoid running iOS static tests with Xcode 8.3.2 +case "$COMMAND" in + *-debug) + COMMAND="${COMMAND%-debug}" + CONFIGURATION="Debug" + ;; + package-*) + IS_RUNNING_PACKAGING=1 + ;; +esac +export CONFIGURATION=${CONFIGURATION:-Release} +export IS_RUNNING_PACKAGING=${IS_RUNNING_PACKAGING:-0} + +# Pre-choose Xcode and Swift versions for those operations that do not set them +REALM_XCODE_VERSION=${xcode_version:-$REALM_XCODE_VERSION} +REALM_SWIFT_VERSION=${swift_version:-$REALM_SWIFT_VERSION} +source "${source_root}/scripts/swift-version.sh" +set_xcode_and_swift_versions + +###################################### +# Commands +###################################### + +case "$COMMAND" in + + ###################################### + # Clean + ###################################### + "clean") + find . -type d -name build -exec rm -r "{}" +\; + exit 0 + ;; + + ###################################### + # Object Server + ###################################### + "download-object-server") + download_object_server + exit 0 + ;; + + "start-object-server") + kill_object_server + ./sync/start-object-server.command + exit 0 + ;; + + "reset-object-server-between-tests") + # Leave the server files alone to avoid 'bad_server_ident' errors + rm -rf "~/Library/Application Support/xctest" + rm -rf "~/Library/Application Support/io.realm.TestHost" + rm -rf "~/Library/Application Support/xctest-child" + exit 0 + ;; + + "reset-object-server") + kill_object_server + # Add a short delay, so file system doesn't complain about files in use + sleep 1 + package="${source_root}/sync" + for file in "$package"/realm-object-server-*; do + if [ -d "$file" ]; then + package="$file" + break + fi + done + rm -rf "$package/object-server/root_dir/" + rm -rf "$package/object-server/temp_dir/" + sh build.sh reset-object-server-between-tests + exit 0 + ;; + + ###################################### + # Core + ###################################### + "download-core") + if [ "$REALM_CORE_VERSION" = "current" ]; then + echo "Using version of core already in core/ directory" + exit 0 + fi + if [ -d core -a -d ../realm-core -a ! -L core ]; then + # Allow newer versions than expected for local builds as testing + # with unreleased versions is one of the reasons to use a local build + if ! $(grep -i "${REALM_CORE_VERSION} Release notes" core/release_notes.txt >/dev/null); then + echo "Local build of core is out of date." + exit 1 + else + echo "The core library seems to be up to date." + fi + elif ! [ -L core ]; then + echo "core is not a symlink. Deleting..." + rm -rf core + download_core + # With a prebuilt version we only want to check the first non-empty + # line so that checking out an older commit will download the + # appropriate version of core if the already-present version is too new + elif ! $(grep -m 1 . core/release_notes.txt | grep -i "${REALM_CORE_VERSION} RELEASE NOTES" >/dev/null); then + download_core + else + echo "The core library seems to be up to date." + fi + exit 0 + ;; + + ###################################### + # Sync + ###################################### + "download-sync") + if [ "$REALM_SYNC_VERSION" = "current" ]; then + echo "Using version of core already in core/ directory" + exit 0 + fi + if [ -d core -a -d ../realm-core -a -d ../realm-sync -a ! -L core ]; then + echo "Using version of core already in core/ directory" + elif ! [ -L core ]; then + echo "core is not a symlink. Deleting..." + rm -rf core + download_sync + elif [[ "$(cat core/version.txt)" != "$REALM_SYNC_VERSION" ]]; then + download_sync + else + echo "The core library seems to be up to date." + fi + exit 0 + ;; + + ###################################### + # Swift versioning + ###################################### + "set-swift-version") + version=${2:-$REALM_SWIFT_VERSION} + + SWIFT_VERSION_FILE="RealmSwift/SwiftVersion.swift" + CONTENTS="let swiftLanguageVersion = \"$version\"" + if [ ! -f "$SWIFT_VERSION_FILE" ] || ! grep -q "$CONTENTS" "$SWIFT_VERSION_FILE"; then + echo "$CONTENTS" > "$SWIFT_VERSION_FILE" + fi + + exit 0 + ;; + + "prelaunch-simulator") + sh ${source_root}/scripts/reset-simulators.sh + ;; + + ###################################### + # Building + ###################################### + "build") + sh build.sh ios-static + sh build.sh ios-dynamic + sh build.sh ios-swift + sh build.sh watchos + sh build.sh watchos-swift + sh build.sh tvos + sh build.sh tvos-swift + sh build.sh osx + sh build.sh osx-swift + exit 0 + ;; + + "ios-static") + build_combined 'Realm iOS static' Realm iphoneos iphonesimulator "-static" + exit 0 + ;; + + "ios-dynamic") + build_combined Realm Realm iphoneos iphonesimulator + exit 0 + ;; + + "ios-swift") + sh build.sh ios-dynamic + build_combined RealmSwift RealmSwift iphoneos iphonesimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/ios/Realm.framework build/ios/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "watchos") + build_combined Realm Realm watchos watchsimulator + exit 0 + ;; + + "watchos-swift") + sh build.sh watchos + build_combined RealmSwift RealmSwift watchos watchsimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/watchos/Realm.framework build/watchos/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "tvos") + build_combined Realm Realm appletvos appletvsimulator + exit 0 + ;; + + "tvos-swift") + sh build.sh tvos + build_combined RealmSwift RealmSwift appletvos appletvsimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/tvos/Realm.framework build/tvos/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "osx") + xc "-scheme Realm -configuration $CONFIGURATION" + clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/Realm.framework" "build/osx" "Realm.framework" + exit 0 + ;; + + "osx-swift") + sh build.sh osx + xc "-scheme 'RealmSwift' -configuration $CONFIGURATION build" + destination="build/osx/swift-$REALM_SWIFT_VERSION" + clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/RealmSwift.framework" "$destination" "RealmSwift.framework" + cp -R build/osx/Realm.framework "$destination" + exit 0 + ;; + + ###################################### + # Analysis + ###################################### + + "analyze-osx") + xc "-scheme Realm -configuration $CONFIGURATION analyze" + exit 0 + ;; + + ###################################### + # Testing + ###################################### + "test") + set +e # Run both sets of tests even if the first fails + failed=0 + sh build.sh test-ios-static || failed=1 + sh build.sh test-ios-dynamic || failed=1 + sh build.sh test-ios-swift || failed=1 + sh build.sh test-ios-devices || failed=1 + sh build.sh test-tvos-devices || failed=1 + sh build.sh test-osx || failed=1 + sh build.sh test-osx-swift || failed=1 + exit $failed + ;; + + "test-all") + set +e + failed=0 + sh build.sh test || failed=1 + sh build.sh test-debug || failed=1 + exit $failed + ;; + + "test-ios-static") + test_ios_static "name=iPhone 6" + exit 0 + ;; + + "test-ios7-static") + test_ios_static "name=iPhone 5S,OS=7.1" + exit 0 + ;; + + "test-ios-dynamic") + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" + exit 0 + ;; + + "test-ios-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" + exit 0 + ;; + + "test-ios-devices") + failed=0 + trap "failed=1" ERR + sh build.sh test-ios-devices-objc + sh build.sh test-ios-devices-swift + exit $failed + ;; + + "test-ios-devices-objc") + test_devices iphoneos "Realm" "$CONFIGURATION" + exit $? + ;; + + "test-ios-devices-swift") + test_devices iphoneos "RealmSwift" "$CONFIGURATION" + exit $? + ;; + + "test-tvos") + xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + exit $? + ;; + + "test-tvos-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + exit $? + ;; + + "test-tvos-devices") + test_devices appletvos TestHost "$CONFIGURATION" + ;; + + "test-osx") + COVERAGE_PARAMS="" + if [[ "$CONFIGURATION" == "Debug" ]]; then + COVERAGE_PARAMS="GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES" + fi + xc "-scheme Realm -configuration $CONFIGURATION test $COVERAGE_PARAMS" + exit 0 + ;; + + "test-osx-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION test" + exit 0 + ;; + + "test-osx-object-server") + xc "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx test" + exit 0 + ;; + + ###################################### + # Full verification + ###################################### + "verify") + sh build.sh verify-cocoapods + sh build.sh verify-docs + sh build.sh verify-osx + sh build.sh verify-osx-debug + sh build.sh verify-osx-swift + sh build.sh verify-osx-swift-debug + sh build.sh verify-ios-static + sh build.sh verify-ios-static-debug + sh build.sh verify-ios7-static + sh build.sh verify-ios7-static-debug + sh build.sh verify-ios-dynamic + sh build.sh verify-ios-dynamic-debug + sh build.sh verify-ios-swift + sh build.sh verify-ios-swift-debug + sh build.sh verify-ios-device-objc + sh build.sh verify-ios-device-swift + sh build.sh verify-watchos + sh build.sh verify-tvos + sh build.sh verify-tvos-debug + sh build.sh verify-tvos-device + sh build.sh verify-swiftlint + sh build.sh verify-osx-object-server + ;; + + "verify-cocoapods") + if [[ -d .git ]]; then + # Verify the current branch, unless one was already specified in the sha environment variable. + if [[ -z $sha ]]; then + export sha=$(git rev-parse --abbrev-ref HEAD) + fi + + if [[ $(git log -1 @{push}..) != "" ]] || ! git diff-index --quiet HEAD; then + echo "WARNING: verify-cocoapods will test the latest revision of $sha found on GitHub." + echo " Any unpushed local changes will not be tested." + echo "" + sleep 1 + fi + fi + + cd examples/installation + sh build.sh test-ios-objc-cocoapods + sh build.sh test-ios-objc-cocoapods-dynamic + sh build.sh test-ios-swift-cocoapods + sh build.sh test-osx-objc-cocoapods + sh build.sh test-osx-swift-cocoapods + sh build.sh test-watchos-objc-cocoapods + sh build.sh test-watchos-swift-cocoapods + ;; + + "verify-osx-encryption") + REALM_ENCRYPT_ALL=YES sh build.sh test-osx + exit 0 + ;; + + "verify-osx") + sh build.sh test-osx + sh build.sh analyze-osx + sh build.sh examples-osx + + ( + cd examples/osx/objc/build/DerivedData/RealmExamples/Build/Products/$CONFIGURATION + DYLD_FRAMEWORK_PATH=. ./JSONImport >/dev/null + ) + exit 0 + ;; + + "verify-osx-swift") + sh build.sh test-osx-swift + exit 0 + ;; + + "verify-ios-static") + sh build.sh test-ios-static + sh build.sh examples-ios + ;; + + "verify-ios7-static") + sh build.sh test-ios7-static + ;; + + "verify-ios-dynamic") + sh build.sh test-ios-dynamic + ;; + + "verify-ios-swift") + sh build.sh test-ios-swift + sh build.sh examples-ios-swift + ;; + + "verify-ios-device-objc") + sh build.sh test-ios-devices-objc + exit 0 + ;; + + "verify-ios-device-swift") + sh build.sh test-ios-devices-swift + exit 0 + ;; + + "verify-docs") + sh build.sh docs + for lang in swift objc; do + undocumented="docs/${lang}_output/undocumented.json" + if ruby -rjson -e "j = JSON.parse(File.read('docs/${lang}_output/undocumented.json')); exit j['warnings'].length != 0"; then + echo "Undocumented Realm $lang declarations:" + cat "$undocumented" + exit 1 + fi + done + exit 0 + ;; + + "verify-watchos") + sh build.sh watchos-swift + exit 0 + ;; + + "verify-tvos") + sh build.sh test-tvos + sh build.sh test-tvos-swift + sh build.sh examples-tvos + sh build.sh examples-tvos-swift + exit 0 + ;; + + "verify-tvos-device") + sh build.sh test-tvos-devices + exit 0 + ;; + + "verify-swiftlint") + swiftlint lint --strict + exit 0 + ;; + + "verify-osx-object-server") + sh build.sh download-object-server + sh build.sh test-osx-object-server + sh build.sh reset-object-server + exit 0 + ;; + + ###################################### + # Docs + ###################################### + "docs") + build_docs objc + build_docs swift + exit 0 + ;; + + ###################################### + # Examples + ###################################### + "examples") + sh build.sh clean + sh build.sh examples-ios + sh build.sh examples-ios-swift + sh build.sh examples-osx + sh build.sh examples-tvos + sh build.sh examples-tvos-swift + exit 0 + ;; + + "examples-ios") + sh build.sh prelaunch-simulator + workspace="examples/ios/objc/RealmExamples.xcworkspace" + pod install --project-directory="$workspace/.." --no-repo-update + xc "-workspace $workspace -scheme Simple -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme TableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Migration -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Backlink -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme GroupedTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme RACTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Encryption -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Draw -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + + if [ ! -z "${JENKINS_HOME}" ]; then + xc "-workspace $workspace -scheme Extension -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + fi + + exit 0 + ;; + + "examples-ios-swift") + sh build.sh prelaunch-simulator + workspace="examples/ios/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme Simple -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme TableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Migration -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Encryption -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Backlink -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme GroupedTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + exit 0 + ;; + + "examples-osx") + xc "-workspace examples/osx/objc/RealmExamples.xcworkspace -scheme JSONImport -configuration ${CONFIGURATION} build ${CODESIGN_PARAMS}" + ;; + + "examples-tvos") + workspace="examples/tvos/objc/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + exit 0 + ;; + + "examples-tvos-swift") + workspace="examples/tvos/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + exit 0 + ;; + + ###################################### + # Versioning + ###################################### + "get-version") + version_file="Realm/Realm-Info.plist" + echo "$(PlistBuddy -c "Print :CFBundleVersion" "$version_file")" + exit 0 + ;; + + "set-version") + realm_version="$2" + version_files="Realm/Realm-Info.plist" + + if [ -z "$realm_version" ]; then + echo "You must specify a version." + exit 1 + fi + for version_file in $version_files; do + PlistBuddy -c "Set :CFBundleVersion $realm_version" "$version_file" + PlistBuddy -c "Set :CFBundleShortVersionString $realm_version" "$version_file" + done + sed -i '' "s/^VERSION=.*/VERSION=$realm_version/" dependencies.list + exit 0 + ;; + + ###################################### + # Bitcode Detection + ###################################### + + "binary-has-bitcode") + BINARY="$2" + # Although grep has a '-q' flag to prevent logging to stdout, grep + # behaves differently when used, so redirect stdout to /dev/null. + if otool -l "$BINARY" | grep "segname __LLVM" > /dev/null 2>&1; then + exit 0 + fi + # Work around rdar://21826157 by checking for bitcode in thin binaries + + # Get architectures for binary + archs="$(lipo -info "$BINARY" | rev | cut -d ':' -f1 | rev)" + + archs_array=( $archs ) + if [[ ${#archs_array[@]} < 2 ]]; then + exit 1 # Early exit if not a fat binary + fi + + TEMPDIR=$(mktemp -d $TMPDIR/realm-bitcode-check.XXXX) + + for arch in $archs; do + lipo -thin "$arch" "$BINARY" -output "$TEMPDIR/$arch" + if otool -l "$TEMPDIR/$arch" | grep -q "segname __LLVM"; then + exit 0 + fi + done + exit 1 + ;; + + ###################################### + # CocoaPods + ###################################### + "cocoapods-setup") + if [ ! -d core ]; then + sh build.sh download-sync + rm core + mv sync-* core + mv core/librealm-ios.a core/librealmcore-ios.a + mv core/librealm-macosx.a core/librealmcore-macosx.a + mv core/librealm-tvos.a core/librealmcore-tvos.a + mv core/librealm-watchos.a core/librealmcore-watchos.a + fi + + if [[ "$2" != "swift" ]]; then + if [ ! -d Realm/ObjectStore/src ]; then + cat >&2 <&1 | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + if [ "$failed" = "1" ] && cat build/build.log | grep -E 'DTXProxyChannel|DTXChannel|out of date and needs to be rebuilt|operation never finished bootstrapping'; then + echo "Known Xcode error detected. Running job again." + if cat build/build.log | grep -E 'out of date and needs to be rebuilt'; then + rm -rf build/DerivedData + fi + failed=0 + sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + elif [ "$failed" = "1" ] && tail ~/Library/Logs/CoreSimulator/CoreSimulator.log | grep -E "Operation not supported|Failed to lookup com.apple.coreservices.lsuseractivity.simulatorsupport"; then + echo "Known Xcode error detected. Running job again." + failed=0 + sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + fi + if [ "$failed" = "1" ]; then + echo "\n\n***\nbuild/build.log\n***\n\n" && cat build/build.log + echo "\n\n***\nCoreSimulator.log\n***\n\n" && tail -n2000 ~/Library/Logs/CoreSimulator/CoreSimulator.log + exit 1 + fi + fi + + if [ "$target" = "osx" ] && [ "$configuration" = "Debug" ]; then + gcovr -r . -f ".*Realm.*" -e ".*Tests.*" -e ".*core.*" --xml > build/reports/coverage-report.xml + WS=$(pwd | sed "s/\//\\\\\//g") + sed -i ".bak" "s/\./${WS}/" build/reports/coverage-report.xml + fi + ;; + + ###################################### + # Release packaging + ###################################### + + "package-examples") + cd tightdb_objc + ./scripts/package_examples.rb + zip --symlinks -r realm-examples.zip examples -x "examples/installation/*" + ;; + + "package-test-examples") + if ! VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*-[a-z]*'); then + VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*') + fi + OBJC="realm-objc-${VERSION}" + SWIFT="realm-swift-${VERSION}" + unzip ${OBJC}.zip + + cp $0 ${OBJC} + cp -r ${source_root}/scripts ${OBJC} + cd ${OBJC} + sh build.sh examples-ios + sh build.sh examples-tvos + sh build.sh examples-osx + cd .. + rm -rf ${OBJC} + + unzip ${SWIFT}.zip + + cp $0 ${SWIFT} + cp -r ${source_root}/scripts ${SWIFT} + cd ${SWIFT} + sh build.sh examples-ios-swift + sh build.sh examples-tvos-swift + cd .. + rm -rf ${SWIFT} + ;; + + "package-ios-static") + cd tightdb_objc + + sh build.sh prelaunch-simulator + sh build.sh test-ios-static + sh build.sh ios-static + + cd build/ios-static + zip --symlinks -r realm-framework-ios.zip Realm.framework + ;; + + "package-ios-dynamic") + cd tightdb_objc + + sh build.sh prelaunch-simulator + sh build.sh ios-dynamic + cd build/ios + zip --symlinks -r realm-dynamic-framework-ios.zip Realm.framework + ;; + + "package-osx") + cd tightdb_objc + sh build.sh test-osx + + cd build/DerivedData/Realm/Build/Products/Release + zip --symlinks -r realm-framework-osx.zip Realm.framework + ;; + + "package-ios-swift") + cd tightdb_objc + for version in 8.0 8.1 8.2 8.3.2; do + REALM_XCODE_VERSION=$version + REALM_SWIFT_VERSION= + set_xcode_and_swift_versions + sh build.sh prelaunch-simulator + sh build.sh ios-swift + done + + cd build/ios + zip --symlinks -r realm-swift-framework-ios.zip swift-3.0 swift-3.0.1 swift-3.0.2 swift-3.1 + ;; + + "package-osx-swift") + cd tightdb_objc + for version in 8.0 8.1 8.2 8.3.2; do + REALM_XCODE_VERSION=$version + REALM_SWIFT_VERSION= + set_xcode_and_swift_versions + sh build.sh prelaunch-simulator + sh build.sh osx-swift + done + + cd build/osx + zip --symlinks -r realm-swift-framework-osx.zip swift-3.0 swift-3.0.1 swift-3.0.2 swift-3.1 + ;; + + "package-watchos") + cd tightdb_objc + sh build.sh prelaunch-simulator + sh build.sh watchos + + cd build/watchos + zip --symlinks -r realm-framework-watchos.zip Realm.framework + ;; + + "package-watchos-swift") + cd tightdb_objc + for version in 8.0 8.1 8.2 8.3.2; do + REALM_XCODE_VERSION=$version + REALM_SWIFT_VERSION= + set_xcode_and_swift_versions + sh build.sh prelaunch-simulator + sh build.sh watchos-swift + done + + cd build/watchos + zip --symlinks -r realm-swift-framework-watchos.zip swift-3.0 swift-3.0.1 swift-3.0.2 swift-3.1 + ;; + + "package-tvos") + cd tightdb_objc + sh build.sh prelaunch-simulator + sh build.sh tvos + + cd build/tvos + zip --symlinks -r realm-framework-tvos.zip Realm.framework + ;; + + "package-tvos-swift") + cd tightdb_objc + for version in 8.0 8.1 8.2 8.3.2; do + REALM_XCODE_VERSION=$version + REALM_SWIFT_VERSION= + set_xcode_and_swift_versions + sh build.sh prelaunch-simulator + sh build.sh tvos-swift + done + + cd build/tvos + zip --symlinks -r realm-swift-framework-tvos.zip swift-3.0 swift-3.0.1 swift-3.0.2 swift-3.1 + ;; + + "package-release") + LANG="$2" + TEMPDIR=$(mktemp -d $TMPDIR/realm-release-package-${LANG}.XXXX) + + cd tightdb_objc + VERSION=$(sh build.sh get-version) + cd .. + + FOLDER=${TEMPDIR}/realm-${LANG}-${VERSION} + + mkdir -p ${FOLDER}/osx ${FOLDER}/ios ${FOLDER}/watchos ${FOLDER}/tvos + + if [[ "${LANG}" == "objc" ]]; then + mkdir -p ${FOLDER}/ios/static + mkdir -p ${FOLDER}/ios/dynamic + mkdir -p ${FOLDER}/Swift + + ( + cd ${FOLDER}/osx + unzip ${WORKSPACE}/realm-framework-osx.zip + ) + + ( + cd ${FOLDER}/ios/static + unzip ${WORKSPACE}/realm-framework-ios.zip + ) + + ( + cd ${FOLDER}/ios/dynamic + unzip ${WORKSPACE}/realm-dynamic-framework-ios.zip + ) + + ( + cd ${FOLDER}/watchos + unzip ${WORKSPACE}/realm-framework-watchos.zip + ) + + ( + cd ${FOLDER}/tvos + unzip ${WORKSPACE}/realm-framework-tvos.zip + ) + else + ( + cd ${FOLDER}/osx + unzip ${WORKSPACE}/realm-swift-framework-osx.zip + ) + + ( + cd ${FOLDER}/ios + unzip ${WORKSPACE}/realm-swift-framework-ios.zip + ) + + ( + cd ${FOLDER}/watchos + unzip ${WORKSPACE}/realm-swift-framework-watchos.zip + ) + + ( + cd ${FOLDER}/tvos + unzip ${WORKSPACE}/realm-swift-framework-tvos.zip + ) + fi + + ( + cd ${WORKSPACE}/tightdb_objc + cp -R plugin ${FOLDER} + cp LICENSE ${FOLDER}/LICENSE.txt + if [[ "${LANG}" == "objc" ]]; then + cp Realm/Swift/RLMSupport.swift ${FOLDER}/Swift/ + fi + ) + + ( + cd ${FOLDER} + unzip ${WORKSPACE}/realm-examples.zip + cd examples + if [[ "${LANG}" == "objc" ]]; then + rm -rf ios/swift-* tvos/swift-* + else + rm -rf ios/objc ios/rubymotion osx tvos/objc + fi + ) + + cat > ${FOLDER}/docs.webloc < + + + + URL + https://realm.io/docs/${LANG}/${VERSION} + + +EOF + + ( + cd ${TEMPDIR} + zip --symlinks -r realm-${LANG}-${VERSION}.zip realm-${LANG}-${VERSION} + mv realm-${LANG}-${VERSION}.zip ${WORKSPACE} + ) + ;; + + "test-package-release") + # Generate a release package locally for testing purposes + # Real releases should always be done via Jenkins + if [ -z "${WORKSPACE}" ]; then + echo 'WORKSPACE must be set to a directory to assemble the release in' + exit 1 + fi + if [ -d "${WORKSPACE}" ]; then + echo 'WORKSPACE directory should not already exist' + exit 1 + fi + + REALM_SOURCE="$(pwd)" + mkdir -p "$WORKSPACE" + WORKSPACE="$(cd "$WORKSPACE" && pwd)" + export WORKSPACE + cd $WORKSPACE + git clone --recursive $REALM_SOURCE tightdb_objc + + echo 'Packaging iOS' + sh tightdb_objc/build.sh package-ios-static + cp tightdb_objc/build/ios-static/realm-framework-ios.zip . + sh tightdb_objc/build.sh package-ios-dynamic + cp tightdb_objc/build/ios/realm-dynamic-framework-ios.zip . + sh tightdb_objc/build.sh package-ios-swift + cp tightdb_objc/build/ios/realm-swift-framework-ios.zip . + + echo 'Packaging OS X' + sh tightdb_objc/build.sh package-osx + cp tightdb_objc/build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip . + sh tightdb_objc/build.sh package-osx-swift + cp tightdb_objc/build/osx/realm-swift-framework-osx.zip . + + echo 'Packaging watchOS' + sh tightdb_objc/build.sh package-watchos + cp tightdb_objc/build/watchos/realm-framework-watchos.zip . + sh tightdb_objc/build.sh package-watchos-swift + cp tightdb_objc/build/watchos/realm-swift-framework-watchos.zip . + + echo 'Packaging tvOS' + sh tightdb_objc/build.sh package-tvos + cp tightdb_objc/build/tvos/realm-framework-tvos.zip . + sh tightdb_objc/build.sh package-tvos-swift + cp tightdb_objc/build/tvos/realm-swift-framework-tvos.zip . + + echo 'Packaging examples' + sh tightdb_objc/build.sh package-examples + cp tightdb_objc/realm-examples.zip . + + echo 'Building final release packages' + sh tightdb_objc/build.sh package-release objc + sh tightdb_objc/build.sh package-release swift + + echo 'Testing packaged examples' + sh tightdb_objc/build.sh package-test-examples + ;; + + "github-release") + if [ -z "${GITHUB_ACCESS_TOKEN}" ]; then + echo 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' + exit 1 + fi + ./scripts/github_release.rb + ;; + + "add-empty-changelog") + empty_section=$(cat < CHANGELOG.md + echo >> CHANGELOG.md + echo "$changelog" >> CHANGELOG.md + ;; + + *) + echo "Unknown command '$COMMAND'" + usage + exit 1 + ;; +esac diff --git a/Desafio Mobile iOS/Pods/Realm/core/librealmcore-ios.a b/Desafio Mobile iOS/Pods/Realm/core/librealmcore-ios.a new file mode 100644 index 0000000..8b21f54 Binary files /dev/null and b/Desafio Mobile iOS/Pods/Realm/core/librealmcore-ios.a differ diff --git a/Desafio Mobile iOS/Pods/Realm/include/NSError+RLMSync.h b/Desafio Mobile iOS/Pods/Realm/include/NSError+RLMSync.h new file mode 100644 index 0000000..ae08d8f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/NSError+RLMSync.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/// NSError category extension providing methods to get data out of Realm's +/// "client reset" error. +@interface NSError (RLMSync) + +/** + Given a Realm Object Server client reset error, return the block that can + be called to manually initiate the client reset process, or nil if the + error isn't a client reset error. + */ +- (nullable void(^)(void))rlmSync_clientResetBlock NS_REFINED_FOR_SWIFT; + +/** + Given a Realm Object Server client reset error, return the path where the + backup copy of the Realm will be placed once the client reset process is + complete. + */ +- (nullable NSString *)rlmSync_clientResetBackedUpRealmPath NS_SWIFT_UNAVAILABLE(""); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMAccessor.h b/Desafio Mobile iOS/Pods/Realm/include/RLMAccessor.h new file mode 100644 index 0000000..cc40523 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMAccessor.h @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + + +@class RLMObjectSchema, RLMProperty, RLMObjectBase, RLMProperty; + +#ifdef __cplusplus +typedef NSUInteger RLMCreationOptions; +#else +typedef NS_OPTIONS(NSUInteger, RLMCreationOptions); +#endif + +NS_ASSUME_NONNULL_BEGIN + +// +// Accessors Class Creation/Caching +// + +// get accessor classes for an object class - generates classes if not cached +Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name); +Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema); + +// +// Dynamic getters/setters +// +FOUNDATION_EXTERN void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id __nullable val); +FOUNDATION_EXTERN id __nullable RLMDynamicGet(RLMObjectBase *obj, RLMProperty *prop); +FOUNDATION_EXTERN id __nullable RLMDynamicGetByName(RLMObjectBase *obj, NSString *propName, bool asList); + +// by property/column +void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val, RLMCreationOptions options); + +// +// Class modification +// + +// Replace className method for the given class +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className); + +// Replace sharedSchema method for the given class +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema * __nullable schema); + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMAnalytics.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMAnalytics.hpp new file mode 100644 index 0000000..76bc429 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMAnalytics.hpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously submits build information to Realm if running in an iOS +// simulator or on OS X if a debugger is attached. Does nothing if running on an +// iOS / watchOS device or if a debugger is *not* attached. +// +// To be clear: this does *not* run when your app is in production or on +// your end-user’s devices; it will only run in the simulator or when a debugger +// is attached. +// +// Why are we doing this? In short, because it helps us build a better product +// for you. None of the data personally identifies you, your employer or your +// app, but it *will* help us understand what language you use, what iOS +// versions you target, etc. Having this info will help prioritizing our time, +// adding new features and deprecating old features. Collecting an anonymized +// bundle & anonymized MAC is the only way for us to count actual usage of the +// other metrics accurately. If we don’t have a way to deduplicate the info +// reported, it will be useless, as a single developer building their Swift app +// 10 times would report 10 times more than a single Objective-C developer that +// only builds once, making the data all but useless. +// No one likes sharing data unless it’s necessary, we get it, and we’ve +// debated adding this for a long long time. Since Realm is a free product +// without an email signup, we feel this is a necessary step so we can collect +// relevant data to build a better product for you. If you truly, absolutely +// feel compelled to not send this data back to Realm, then you can set an env +// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe +// letting these analytics run is a small price to pay for the product & support +// we give you. +// +// Currently the following information is reported: +// - What version of Realm is being used, and from which language (obj-c or Swift). +// - What version of OS X it's running on (in case Xcode aggressively drops +// support for older versions again, we need to know what we need to support). +// - The minimum iOS/OS X version that the application is targeting (again, to +// help us decide what versions we need to support). +// - An anonymous MAC address and bundle ID to aggregate the other information on. +// - What version of Swift is being used (if applicable). + +void RLMSendAnalytics(); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMArray.h b/Desafio Mobile iOS/Pods/Realm/include/RLMArray.h new file mode 100644 index 0000000..d5a6a65 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMArray.h @@ -0,0 +1,439 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObject, RLMRealm, RLMResults, RLMNotificationToken; + +/** + `RLMArray` is the container type in Realm used to define to-many relationships. + + Unlike an `NSArray`, `RLMArray`s hold a single type, specified by the `objectClassName` property. + This is referred to in these docs as the “type” of the array. + + When declaring an `RLMArray` property, the type must be marked as conforming to a + protocol by the same name as the objects it should contain (see the + `RLM_ARRAY_TYPE` macro). In addition, the property can be declared using Objective-C + generics for better compile-time type safety. + + RLM_ARRAY_TYPE(ObjectType) + ... + @property RLMArray *arrayOfObjectTypes; + + `RLMArray`s can be queried with the same predicates as `RLMObject` and `RLMResult`s. + + `RLMArray`s cannot be created directly. `RLMArray` properties on `RLMObject`s are + lazily created when accessed, or can be obtained by querying a Realm. + + ### Key-Value Observing + + `RLMArray` supports array key-value observing on `RLMArray` properties on `RLMObject` + subclasses, and the `invalidated` property on `RLMArray` instances themselves is + key-value observing compliant when the `RLMArray` is attached to a managed + `RLMObject` (`RLMArray`s on unmanaged `RLMObject`s will never become invalidated). + + Because `RLMArray`s are attached to the object which they are a property of, they + do not require using the mutable collection proxy objects from + `-mutableArrayValueForKey:` or KVC-compatible mutation methods on the containing + object. Instead, you can call the mutation methods on the `RLMArray` directly. + */ + +@interface RLMArray : NSObject + +#pragma mark - Properties + +/** + The number of objects in the array. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the array. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages the array. Returns `nil` for unmanaged arrays. + */ +@property (nonatomic, readonly, nullable) RLMRealm *realm; + +/** + Indicates if the array can no longer be accessed. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +#pragma mark - Accessing Objects from an Array + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the array. + */ +- (RLMObjectType)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the array. + + Returns `nil` if called on an empty array. + + @return An `RLMObject` of the type contained in the array. + */ +- (nullable RLMObjectType)firstObject; + +/** + Returns the last object in the array. + + Returns `nil` if called on an empty array. + + @return An `RLMObject` of the type contained in the array. + */ +- (nullable RLMObjectType)lastObject; + + + +#pragma mark - Adding, Removing, and Replacing Objects in an Array + +/** + Adds an object to the end of the array. + + @warning This method may only be called during a write transaction. + + @param object An `RLMObject` of the type contained in the array. + */ +- (void)addObject:(RLMObjectType)object; + +/** + Adds an array of objects to the end of the array. + + @warning This method may only be called during a write transaction. + + @param objects An enumerable object such as `NSArray` or `RLMResults` which contains objects of the + same class as the array. + */ +- (void)addObjects:(id)objects; + +/** + Inserts an object at the given index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param anObject An `RLMObject` of the type contained in the array. + @param index The index at which to insert the object. + */ +- (void)insertObject:(RLMObjectType)anObject atIndex:(NSUInteger)index; + +/** + Removes an object at the given index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index The array index identifying the object to be removed. + */ +- (void)removeObjectAtIndex:(NSUInteger)index; + +/** + Removes the last object in the array. + + @warning This method may only be called during a write transaction. +*/ +- (void)removeLastObject; + +/** + Removes all objects from the array. + + @warning This method may only be called during a write transaction. + */ +- (void)removeAllObjects; + +/** + Replaces an object at the given index with a new object. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index The index of the object to be replaced. + @param anObject An object (of the same type as returned from the `objectClassName` selector). + */ +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObjectType)anObject; + +/** + Moves the object at the given source index to the given destination index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param sourceIndex The index of the object to be moved. + @param destinationIndex The index to which the object at `sourceIndex` should be moved. + */ +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex; + +/** + Exchanges the objects in the array at given indices. + + Throws an exception if either index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index1 The index of the object which should replace the object at index `index2`. + @param index2 The index of the object which should replace the object at index `index1`. + */ +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2; + +#pragma mark - Querying an Array + +/** + Returns the index of an object in the array. + + Returns `NSNotFound` if the object is not found in the array. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObjectType)object; + +/** + Returns the index of the first object in the array matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the array. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the array matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the array. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all the objects matching the given predicate in the array. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all the objects matching the given predicate in the array. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` of objects that match the given predicate + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from the array. + + @param keyPath The key path to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified key path. + */ +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from the array. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending + __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); + +/** + Returns a sorted `RLMResults` from the array. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +/// :nodoc: +- (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index; + +/// :nodoc: +- (void)setObject:(RLMObjectType)newValue atIndexedSubscript:(NSUInteger)index; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the array changes. + + The block will be asynchronously called with the initial array, and then + called again after each write transaction which changes any of the objects in + the array, which objects are in the results, or the order of the objects in the + array. + + The `changes` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the array were added, removed or modified. If a write transaction + did not modify any objects in the array, the block is not called at all. + See the `RLMCollectionChange` documentation for information on how the changes + are reported and an example of updating a `UITableView`. + + If an error occurs the block will be called with `nil` for the results + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial results. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + Person *person = [[Person allObjectsInRealm:realm] firstObject]; + NSLog(@"person.dogs.count: %zu", person.dogs.count); // => 0 + self.token = [person.dogs addNotificationBlock(RLMArray *dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count) // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [person.dogs addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + @warning This method may only be called on a managed array. + + @param block The block to be called each time the array changes. + @return A token which must be held for as long as you want updates to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *__nullable array, + RLMCollectionChange *__nullable changes, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects in the array. + + NSNumber *min = [object.arrayProperty minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The minimum value of the property, or `nil` if the array is empty. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects in the array. + + NSNumber *max = [object.arrayProperty maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The maximum value of the property, or `nil` if the array is empty. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects in the array. + + NSNumber *sum = [object.arrayProperty sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of + types `int`, `float`, and `double` are supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects in the array. + + NSNumber *average = [object.arrayProperty averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only + properties of types `int`, `float`, and `double` are supported. + + @return The average value of the given property, or `nil` if the array is empty. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + + +#pragma mark - Unavailable Methods + +/** + `-[RLMArray init]` is not available because `RLMArray`s cannot be created directly. + `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + */ +- (instancetype)init __attribute__((unavailable("RLMArrays cannot be created directly"))); + +/** + `+[RLMArray new]` is not available because `RLMArray`s cannot be created directly. + `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + */ ++ (instancetype)new __attribute__((unavailable("RLMArrays cannot be created directly"))); + +@end + +/// :nodoc: +@interface RLMArray (Swift) +// for use only in Swift class definitions +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.h new file mode 100644 index 0000000..5e15e2d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMArray () +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.hpp new file mode 100644 index 0000000..5db9abe --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMArray_Private.hpp @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMArray_Private.h" + +#import "RLMCollection_Private.hpp" + +#import + +#import + +namespace realm { + class Results; +} + +@class RLMObjectBase, RLMObjectSchema, RLMProperty; +class RLMClassInfo; +class RLMObservationInfo; + +@interface RLMArray () { +@protected + NSString *_objectClassName; +@public + // The name of the property which this RLMArray represents + NSString *_key; + __weak RLMObjectBase *_parentObject; +} +@end + +// +// LinkView backed RLMArray subclass +// +@interface RLMArrayLinkView : RLMArray +- (instancetype)initWithParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property; + +// deletes all objects in the RLMArray from their containing realms +- (void)deleteObjectsFromRealm; +@end + +void RLMValidateArrayObservationKey(NSString *keyPath, RLMArray *array); + +// Initialize the observation info for an array if needed +void RLMEnsureArrayObservationInfo(std::unique_ptr& info, + NSString *keyPath, RLMArray *array, id observed); + + +// +// RLMResults private methods +// +@interface RLMResults () ++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info + results:(realm::Results)results; + +- (void)deleteObjectsFromRealm; +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMAuthResponseModel.h b/Desafio Mobile iOS/Pods/Realm/include/RLMAuthResponseModel.h new file mode 100644 index 0000000..0cbf68e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMAuthResponseModel.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +/** + An internal class representing a valid JSON response to an auth request. + + ``` + { + "access_token": { ... } // (optional), + "refresh_token": { ... } // (optional) + } + ``` + */ +@class RLMTokenModel; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMAuthResponseModel : NSObject + +@property (nonatomic, readonly, nullable) RLMTokenModel *accessToken; +@property (nonatomic, readonly, nullable) RLMTokenModel *refreshToken; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMTokenModel cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMTokenModel cannot be created directly"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMClassInfo.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMClassInfo.hpp new file mode 100644 index 0000000..69c822d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMClassInfo.hpp @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import +#import + +namespace realm { + class ObjectSchema; + class Schema; + class Table; + struct Property; +} + +class RLMObservationInfo; +@class RLMRealm, RLMSchema, RLMObjectSchema, RLMProperty; + +NS_ASSUME_NONNULL_BEGIN + +namespace std { +// Add specializations so that NSString can be used as the key for hash containers +template<> struct hash { + size_t operator()(__unsafe_unretained NSString *const str) const { + return [str hash]; + } +}; +template<> struct equal_to { + bool operator()(__unsafe_unretained NSString * lhs, __unsafe_unretained NSString *rhs) const { + return [lhs isEqualToString:rhs]; + } +}; +} + +// The per-RLMRealm object schema information which stores the cached table +// reference, handles table column lookups, and tracks observed objects +class RLMClassInfo { +public: + RLMClassInfo(RLMRealm *, RLMObjectSchema *, const realm::ObjectSchema *); + + __unsafe_unretained RLMRealm *const realm; + __unsafe_unretained RLMObjectSchema *const rlmObjectSchema; + const realm::ObjectSchema *const objectSchema; + + // Storage for the functionality in RLMObservation for handling indirect + // changes to KVO-observed things + std::vector observedObjects; + + // Get the table for this object type. Will return nullptr only if it's a + // read-only Realm that is missing the table entirely. + realm::Table *_Nullable table() const; + + // Get the RLMProperty for a given table column, or `nil` if it is a column + // not used by the current schema + RLMProperty *_Nullable propertyForTableColumn(NSUInteger) const noexcept; + + // Get the RLMProperty that's used as the primary key, or `nil` if there is + // no primary key for the current schema + RLMProperty *_Nullable propertyForPrimaryKey() const noexcept; + + // Get the table column for the given property. The property must be a valid + // persisted property. + NSUInteger tableColumn(NSString *propertyName) const; + NSUInteger tableColumn(RLMProperty *property) const; + + // Get the info for the target of the link at the given property index. + RLMClassInfo &linkTargetType(size_t propertyIndex); + + void releaseTable() { m_table = nullptr; } + +private: + mutable realm::Table *_Nullable m_table = nullptr; + std::vector m_linkTargets; +}; + +// A per-RLMRealm object schema map which stores RLMClassInfo keyed on the name +class RLMSchemaInfo { + using impl = std::unordered_map; +public: + RLMSchemaInfo() = default; + RLMSchemaInfo(RLMRealm *realm); + + RLMSchemaInfo clone(realm::Schema const& source_schema, RLMRealm *target_realm); + + // Look up by name, throwing if it's not present + RLMClassInfo& operator[](NSString *name); + + impl::iterator begin() noexcept; + impl::iterator end() noexcept; + impl::const_iterator begin() const noexcept; + impl::const_iterator end() const noexcept; +private: + std::unordered_map m_objects; +}; + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMCollection.h b/Desafio Mobile iOS/Pods/Realm/include/RLMCollection.h new file mode 100644 index 0000000..5d668ca --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMCollection.h @@ -0,0 +1,411 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import "RLMThreadSafeReference.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm, RLMResults, RLMObject, RLMSortDescriptor, RLMNotificationToken, RLMCollectionChange; + +/** + A homogenous collection of `RLMObject` instances. Examples of conforming types include `RLMArray`, + `RLMResults`, and `RLMLinkingObjects`. + */ +@protocol RLMCollection + +@required + +#pragma mark - Properties + +/** + The number of objects in the collection. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the collection. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages the collection, or `nil` for unmanaged collections. + */ +@property (nonatomic, readonly) RLMRealm *realm; + +#pragma mark - Accessing Objects from a Collection + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the collection. + */ +- (id)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the collection. + + Returns `nil` if called on an empty collection. + + @return An `RLMObject` of the type contained in the collection. + */ +- (nullable id)firstObject; + +/** + Returns the last object in the collection. + + Returns `nil` if called on an empty collection. + + @return An `RLMObject` of the type contained in the collection. + */ +- (nullable id)lastObject; + +#pragma mark - Querying a Collection + +/** + Returns the index of an object in the collection. + + Returns `NSNotFound` if the object is not found in the collection. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObject *)object; + +/** + Returns the index of the first object in the collection matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the collection. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the collection matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the collection. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all objects matching the given predicate in the collection. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all objects matching the given predicate in the collection. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing objects that match the given predicate. + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from the collection. + + @param keyPath The keyPath to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified key path. + */ +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from the collection. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending + __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); + +/** + Returns a sorted `RLMResults` from the collection. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +/// :nodoc: +- (id)objectAtIndexedSubscript:(NSUInteger)index; + +/** + Returns an `NSArray` containing the results of invoking `valueForKey:` using `key` on each of the collection's objects. + + @param key The name of the property. + + @return An `NSArray` containing results. + */ +- (nullable id)valueForKey:(NSString *)key; + +/** + Invokes `setValue:forKey:` on each of the collection's objects using the specified `value` and `key`. + + @warning This method may only be called during a write transaction. + + @param value The object value. + @param key The name of the property. + */ +- (void)setValue:(nullable id)value forKey:(NSString *)key; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the collection changes. + + The block will be asynchronously called with the initial collection, and then + called again after each write transaction which changes either any of the + objects in the collection, or which objects are in the collection. + + The `change` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the collection were added, removed or modified. If a write transaction + did not modify any objects in this collection, the block is not called at all. + See the `RLMCollectionChange` documentation for information on how the changes + are reported and an example of updating a `UITableView`. + + If an error occurs the block will be called with `nil` for the collection + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + At the time when the block is called, the collection object will be fully + evaluated and up-to-date, and as long as you do not perform a write transaction + on the same thread or explicitly call `-[RLMRealm refresh]`, accessing it will + never perform blocking work. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial collection. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + id collection = [Dog allObjects]; + NSLog(@"dogs.count: %zu", dogs.count); // => 0 + self.token = [collection addNotificationBlock:^(id dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count); // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [realm addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + + @param block The block to be called each time the collection changes. + @return A token which must be held for as long as you want collection notifications to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(id __nullable collection, + RLMCollectionChange *__nullable change, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects + in the collection. + + NSNumber *min = [results minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The minimum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects + in the collection. + + NSNumber *max = [results maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The maximum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects in the collection. + + NSNumber *sum = [results sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of + types `int`, `float`, and `double` are supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects in the collection. + + NSNumber *average = [results averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only + properties of types `int`, `float`, and `double` are supported. + + @return The average value of the given property, or `nil` if the Results are empty. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + +@end + +/** + An `RLMSortDescriptor` stores a property name and a sort order for use with + `sortedResultsUsingDescriptors:`. It is similar to `NSSortDescriptor`, but supports + only the subset of functionality which can be efficiently run by Realm's query + engine. + + `RLMSortDescriptor` instances are immutable. + */ +@interface RLMSortDescriptor : NSObject + +#pragma mark - Properties + +/** + The key path which the sort descriptor orders results by. + */ +@property (nonatomic, readonly) NSString *keyPath; + +/** + Whether the descriptor sorts in ascending or descending order. + */ +@property (nonatomic, readonly) BOOL ascending; + +#pragma mark - Methods + +/** + Returns a new sort descriptor for the given key path and sort direction. + */ ++ (instancetype)sortDescriptorWithKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; + +/** + Returns a copy of the receiver with the sort direction reversed. + */ +- (instancetype)reversedSortDescriptor; + +#pragma mark - Deprecated + +/** + The name of the property which the sort descriptor orders results by. + */ +@property (nonatomic, readonly) NSString *property __deprecated_msg("Use `-keyPath`"); + +/** + Returns a new sort descriptor for the given property name and sort direction. + */ ++ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending + __deprecated_msg("Use `+sortDescriptorWithKeyPath:ascending:`"); + +@end + +/** + A `RLMCollectionChange` object encapsulates information about changes to collections + that are reported by Realm notifications. + + `RLMCollectionChange` is passed to the notification blocks registered with + `-addNotificationBlock` on `RLMArray` and `RLMResults`, and reports what rows in the + collection changed since the last time the notification block was called. + + The change information is available in two formats: a simple array of row + indices in the collection for each type of change, and an array of index paths + in a requested section suitable for passing directly to `UITableView`'s batch + update methods. A complete example of updating a `UITableView` named `tv`: + + [tv beginUpdates]; + [tv deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv endUpdates]; + + All of the arrays in an `RLMCollectionChange` are always sorted in ascending order. + */ +@interface RLMCollectionChange : NSObject +/// The indices of objects in the previous version of the collection which have +/// been removed from this one. +@property (nonatomic, readonly) NSArray *deletions; + +/// The indices in the new version of the collection which were newly inserted. +@property (nonatomic, readonly) NSArray *insertions; + +/** + The indices in the new version of the collection which were modified. + + For `RLMResults`, this means that one or more of the properties of the object at + that index were modified (or an object linked to by that object was + modified). + + For `RLMArray`, the array itself being modified to contain a + different object at that index will also be reported as a modification. + */ +@property (nonatomic, readonly) NSArray *modifications; + +/// Returns the index paths of the deletion indices in the given section. +- (NSArray *)deletionsInSection:(NSUInteger)section; + +/// Returns the index paths of the insertion indices in the given section. +- (NSArray *)insertionsInSection:(NSUInteger)section; + +/// Returns the index paths of the modification indices in the given section. +- (NSArray *)modificationsInSection:(NSUInteger)section; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMCollection_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMCollection_Private.hpp new file mode 100644 index 0000000..9f58c97 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMCollection_Private.hpp @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import + +namespace realm { + class List; + class Results; + class TableView; + struct CollectionChangeSet; + struct NotificationToken; +} +class RLMClassInfo; + +@protocol RLMFastEnumerable +@property (nonatomic, readonly) RLMRealm *realm; +@property (nonatomic, readonly) RLMClassInfo *objectInfo; +@property (nonatomic, readonly) NSUInteger count; + +- (NSUInteger)indexInSource:(NSUInteger)index; +- (realm::TableView)tableView; +@end + +// An object which encapulates the shared logic for fast-enumerating RLMArray +// and RLMResults, and has a buffer to store strong references to the current +// set of enumerated items +@interface RLMFastEnumerator : NSObject +- (instancetype)initWithCollection:(id)collection + objectSchema:(RLMClassInfo&)objectSchema; + +// Detach this enumerator from the source collection. Must be called before the +// source collection is changed. +- (void)detach; + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + count:(NSUInteger)len; +@end + +@interface RLMNotificationToken () +- (void)suppressNextNotification; +- (RLMRealm *)realm; +@end + +@interface RLMCancellationToken : RLMNotificationToken +- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm; +@end + +@interface RLMCollectionChange () +- (instancetype)initWithChanges:(realm::CollectionChangeSet)indices; +@end + +template +RLMNotificationToken *RLMAddNotificationBlock(id objcCollection, + Collection& collection, + void (^block)(id, RLMCollectionChange *, NSError *), + bool suppressInitialChange=false); + +NSArray *RLMCollectionValueForKey(id collection, NSString *key); +void RLMCollectionSetValueForKey(id collection, NSString *key, id value); +NSString *RLMDescriptionWithMaxDepth(NSString *name, id collection, NSUInteger depth); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMConstants.h b/Desafio Mobile iOS/Pods/Realm/include/RLMConstants.h new file mode 100644 index 0000000..bf3b2e7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMConstants.h @@ -0,0 +1,204 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +// For compatibility with Xcode 7, before extensible string enums were introduced, +#ifdef NS_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM NS_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(_, extensible_string_enum) NS_SWIFT_NAME(extensible_string_enum) +#else +#define RLM_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(fully_qualified, _) NS_SWIFT_NAME(fully_qualified) +#endif + +#if __has_attribute(ns_error_domain) && (!defined(__cplusplus) || !__cplusplus || __cplusplus >= 201103L) +#define RLM_ERROR_ENUM(type, name, domain) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wignored-attributes\"") \ + NS_ENUM(type, __attribute__((ns_error_domain(domain))) name) \ + _Pragma("clang diagnostic pop") +#else +#define RLM_ERROR_ENUM(type, name, domain) NS_ENUM(type, name) +#endif + + +#pragma mark - Enums + +/** + `RLMPropertyType` is an enumeration describing all property types supported in Realm models. + + For more information, see [Realm Models](https://realm.io/docs/objc/latest/#models). + */ +// Make sure numbers match those in +typedef NS_ENUM(int32_t, RLMPropertyType) { + +#pragma mark - Primitive types + + /** Integers: `NSInteger`, `int`, `long`, `Int` (Swift) */ + RLMPropertyTypeInt = 0, + /** Booleans: `BOOL`, `bool`, `Bool` (Swift) */ + RLMPropertyTypeBool = 1, + /** Floating-point numbers: `float`, `Float` (Swift) */ + RLMPropertyTypeFloat = 9, + /** Double-precision floating-point numbers: `double`, `Double` (Swift) */ + RLMPropertyTypeDouble = 10, + +#pragma mark - Object types + + /** Strings: `NSString`, `String` (Swift) */ + RLMPropertyTypeString = 2, + /** Binary data: `NSData` */ + RLMPropertyTypeData = 4, + /** + Any object: `id`. + + This property type is no longer supported for new models. However, old models with any-typed properties are still + supported for migration purposes. + */ + RLMPropertyTypeAny = 6, + /** Dates: `NSDate` */ + RLMPropertyTypeDate = 8, + +#pragma mark - Array/Linked object types + + /** Realm model objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeObject = 12, + /** Realm arrays. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeArray = 13, + /** Realm linking objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeLinkingObjects = 14, +}; + +/** An error domain identifying Realm-specific errors. */ +extern NSString * const RLMErrorDomain; + +/** An error domain identifying non-specific system errors. */ +extern NSString * const RLMUnknownSystemErrorDomain; + +/** + `RLMError` is an enumeration representing all recoverable errors. It is associated with the + Realm error domain specified in `RLMErrorDomain`. + */ +typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) { + /** Denotes a general error that occurred when trying to open a Realm. */ + RLMErrorFail = 1, + + /** Denotes a file I/O error that occurred when trying to open a Realm. */ + RLMErrorFileAccess = 2, + + /** + Denotes a file permission error that ocurred when trying to open a Realm. + + This error can occur if the user does not have permission to open or create + the specified file in the specified access mode when opening a Realm. + */ + RLMErrorFilePermissionDenied = 3, + + /** Denotes an error where a file was to be written to disk, but another file with the same name already exists. */ + RLMErrorFileExists = 4, + + /** + Denotes an error that occurs if a file could not be found. + + This error may occur if a Realm file could not be found on disk when trying to open a + Realm as read-only, or if the directory part of the specified path was not found when + trying to write a copy. + */ + RLMErrorFileNotFound = 5, + + /** + Denotes an error that occurs if a file format upgrade is required to open the file, + but upgrades were explicitly disabled. + */ + RLMErrorFileFormatUpgradeRequired = 6, + + /** + Denotes an error that occurs if the database file is currently open in another + process which cannot share with the current process due to an + architecture mismatch. + + This error may occur if trying to share a Realm file between an i386 (32-bit) iOS + Simulator and the Realm Browser application. In this case, please use the 64-bit + version of the iOS Simulator. + */ + RLMErrorIncompatibleLockFile = 8, + + /** Denotes an error that occurs when there is insufficient available address space. */ + RLMErrorAddressSpaceExhausted = 9, + + /** Denotes an error that occurs if there is a schema version mismatch, so that a migration is required. */ + RLMErrorSchemaMismatch = 10, +}; + +#pragma mark - Constants + +#pragma mark - Notification Constants + +/** + A notification indicating that changes were made to a Realm. +*/ +typedef NSString * RLMNotification RLM_EXTENSIBLE_STRING_ENUM; + +/** + This notification is posted by a Realm when the data in that Realm has changed. + + More specifically, this notification is posted after a Realm has been refreshed to + reflect a write transaction. This can happen when an autorefresh occurs, when + `-[RLMRealm refresh]` is called, after an implicit refresh from `-[RLMRealm beginWriteTransaction]`, + or after a local write transaction is completed. + */ +extern RLMNotification const RLMRealmRefreshRequiredNotification +RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmRefreshRequiredNotification, RefreshRequired); + +/** + This notification is posted by a Realm when a write transaction has been + committed to a Realm on a different thread for the same file. + + It is not posted if `-[RLMRealm autorefresh]` is enabled, or if the Realm is + refreshed before the notification has a chance to run. + + Realms with autorefresh disabled should normally install a handler for this + notification which calls `-[RLMRealm refresh]` after doing some work. Refreshing + the Realm is optional, but not refreshing the Realm may lead to large Realm + files. This is because Realm must keep an extra copy of the data for the stale + Realm. + */ +extern RLMNotification const RLMRealmDidChangeNotification +RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmDidChangeNotification, DidChange); + +#pragma mark - Other Constants + +/** The schema version used for uninitialized Realms */ +extern const uint64_t RLMNotVersioned; + +/** The corresponding value is the name of an exception thrown by Realm. */ +extern NSString * const RLMExceptionName; + +/** The corresponding value is a Realm file version. */ +extern NSString * const RLMRealmVersionKey; + +/** The corresponding key is the version of the underlying database engine. */ +extern NSString * const RLMRealmCoreVersionKey; + +/** The corresponding key is the Realm invalidated property name. */ +extern NSString * const RLMInvalidatedKey; + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMListBase.h b/Desafio Mobile iOS/Pods/Realm/include/RLMListBase.h new file mode 100644 index 0000000..0151cfb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMListBase.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +@class RLMArray; + +NS_ASSUME_NONNULL_BEGIN + +// A base class for Swift generic Lists to make it possible to interact with +// them from obj-c +@interface RLMListBase : NSObject +@property (nonatomic, strong) RLMArray *_rlmArray; + +- (instancetype)initWithArray:(RLMArray *)array; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMMigration.h b/Desafio Mobile iOS/Pods/Realm/include/RLMMigration.h new file mode 100644 index 0000000..d3da9ff --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMMigration.h @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class RLMSchema; +@class RLMArray; +@class RLMObject; + +/** + A block type which provides both the old and new versions of an object in the Realm. Object + properties can only be accessed using keyed subscripting. + + @see `-[RLMMigration enumerateObjects:block:]` + + @param oldObject The object from the original Realm (read-only). + @param newObject The object from the migrated Realm (read-write). +*/ +typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObject * __nullable newObject); + +/** + `RLMMigration` instances encapsulate information intended to facilitate a schema migration. + + A `RLMMigration` instance is passed into a user-defined `RLMMigrationBlock` block when updating + the version of a Realm. This instance provides access to the old and new database schemas, the + objects in the Realm, and provides functionality for modifying the Realm during the migration. + */ +@interface RLMMigration : NSObject + +#pragma mark - Properties + +/** + Returns the old `RLMSchema`. This is the schema which describes the Realm before the + migration is applied. + */ +@property (nonatomic, readonly) RLMSchema *oldSchema; + +/** + Returns the new `RLMSchema`. This is the schema which describes the Realm after the + migration is applied. + */ +@property (nonatomic, readonly) RLMSchema *newSchema; + + +#pragma mark - Altering Objects during a Migration + +/** + Enumerates all the objects of a given type in the Realm, providing both the old and new versions + of each object. Within the block, object properties can only be accessed using keyed subscripting. + + @param className The name of the `RLMObject` class to enumerate. + + @warning All objects returned are of a type specific to the current migration and should not be cast + to `className`. Instead, treat them as `RLMObject`s and use keyed subscripting to access + properties. + */ +- (void)enumerateObjects:(NSString *)className block:(__attribute__((noescape)) RLMObjectMigrationBlock)block; + +/** + Creates and returns an `RLMObject` instance of type `className` in the Realm being migrated. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an `NSArray` as the `value` argument, all properties must be present, valid and in the same order as + the properties defined in the model. + + @param className The name of the `RLMObject` class to create. + @param value The value used to populate the object. + */ +- (RLMObject *)createObject:(NSString *)className withValue:(id)value; + +/** + Deletes an object from a Realm during a migration. + + It is permitted to call this method from within the block passed to `-[enumerateObjects:block:]`. + + @param object Object to be deleted from the Realm being migrated. + */ +- (void)deleteObject:(RLMObject *)object; + +/** + Deletes the data for the class with the given name. + + All objects of the given class will be deleted. If the `RLMObject` subclass no longer exists in your program, + any remaining metadata for the class will be removed from the Realm file. + + @param name The name of the `RLMObject` class to delete. + + @return A Boolean value indicating whether there was any data to delete. + */ +- (BOOL)deleteDataForClassName:(NSString *)name; + +/** + Renames a property of the given class from `oldName` to `newName`. + + @param className The name of the class whose property should be renamed. This class must be present + in both the old and new Realm schemas. + @param oldName The old name for the property to be renamed. There must not be a property with this name in the + class as defined by the new Realm schema. + @param newName The new name for the property to be renamed. There must not be a property with this name in the + class as defined by the old Realm schema. + */ +- (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMMigration_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMMigration_Private.h new file mode 100644 index 0000000..99699e5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMMigration_Private.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import +#import + +namespace realm { + class Schema; +} + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMMigration () + +@property (nonatomic, strong) RLMRealm *oldRealm; +@property (nonatomic, strong) RLMRealm *realm; + +- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema; + +- (void)execute:(RLMMigrationBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMNetworkClient.h b/Desafio Mobile iOS/Pods/Realm/include/RLMNetworkClient.h new file mode 100644 index 0000000..bde6ecf --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMNetworkClient.h @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil_Private.h" + +/** + An enum describing all possible endpoints on the Realm Object Server. + */ +typedef NS_ENUM(NSUInteger, RLMServerEndpoint) { + RLMServerEndpointAuth, + RLMServerEndpointLogout, + RLMServerEndpointAddCredentials, + RLMServerEndpointRemoveCredentials, +}; + +/** + A simple Realm Object Server network client that wraps `NSURLSession`. + */ +@interface RLMNetworkClient : NSObject + +NS_ASSUME_NONNULL_BEGIN + ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + completion:(RLMSyncCompletionBlock)completionBlock; + +/** + Post some JSON data to the authentication server, and asynchronously call a completion block with a JSON response + and/or error. + */ ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + timeout:(NSTimeInterval)timeout + completion:(RLMSyncCompletionBlock)completionBlock; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObject.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObject.h new file mode 100644 index 0000000..c803e35 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObject.h @@ -0,0 +1,535 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMNotificationToken; +@class RLMObjectSchema; +@class RLMPropertyChange; +@class RLMPropertyDescriptor; +@class RLMRealm; +@class RLMResults; + +/** + `RLMObject` is a base class for model objects representing data stored in Realms. + + Define your model classes by subclassing `RLMObject` and adding properties to be managed. + Then instantiate and use your custom subclasses instead of using the `RLMObject` class directly. + + // Dog.h + @interface Dog : RLMObject + @property NSString *name; + @property BOOL adopted; + @end + + // Dog.m + @implementation Dog + @end //none needed + + ### Supported property types + + - `NSString` + - `NSInteger`, `int`, `long`, `float`, and `double` + - `BOOL` or `bool` + - `NSDate` + - `NSData` + - `NSNumber`, where `X` is one of `RLMInt`, `RLMFloat`, `RLMDouble` or `RLMBool`, for optional number properties + - `RLMObject` subclasses, to model many-to-one relationships. + - `RLMArray`, where `X` is an `RLMObject` subclass, to model many-to-many relationships. + + ### Querying + + You can initiate queries directly via the class methods: `allObjects`, `objectsWhere:`, and `objectsWithPredicate:`. + These methods allow you to easily query a custom subclass for instances of that class in the default Realm. + + To search in a Realm other than the default Realm, use the `allObjectsInRealm:`, `objectsInRealm:where:`, + and `objectsInRealm:withPredicate:` class methods. + + @see `RLMRealm` + + ### Relationships + + See our [Cocoa guide](https://realm.io/docs/objc/latest#relationships) for more details. + + ### Key-Value Observing + + All `RLMObject` properties (including properties you create in subclasses) are + [Key-Value Observing compliant](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html), + except for `realm` and `objectSchema`. + + Keep the following tips in mind when observing Realm objects: + + 1. Unlike `NSMutableArray` properties, `RLMArray` properties do not require + using the proxy object returned from `-mutableArrayValueForKey:`, or defining + KVC mutation methods on the containing class. You can simply call methods on + the `RLMArray` directly; any changes will be automatically observed by the containing + object. + 2. Unmanaged `RLMObject` instances cannot be added to a Realm while they have any + observed properties. + 3. Modifying managed `RLMObject`s within `-observeValueForKeyPath:ofObject:change:context:` + is not recommended. Properties may change even when the Realm is not in a write + transaction (for example, when `-[RLMRealm refresh]` is called after changes + are made on a different thread), and notifications sent prior to the change + being applied (when `NSKeyValueObservingOptionPrior` is used) may be sent at + times when you *cannot* begin a write transaction. + */ + +@interface RLMObject : RLMObjectBase + +#pragma mark - Creating & Initializing Objects + +/** + Creates an unmanaged instance of a Realm object. + + Call `addObject:` on an `RLMRealm` instance to add an unmanaged object into that Realm. + + @see `[RLMRealm addObject:]` + */ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + + +/** + Creates an unmanaged instance of a Realm object. + + Pass in an `NSArray` or `NSDictionary` instance to set the values of the object's properties. + + Call `addObject:` on an `RLMRealm` instance to add an unmanaged object into that Realm. + + @see `[RLMRealm addObject:]` + */ +- (instancetype)initWithValue:(id)value NS_DESIGNATED_INITIALIZER; + + +/** + Returns the class name for a Realm object subclass. + + @warning Do not override. Realm relies on this method returning the exact class + name. + + @return The class name for the model class. + */ ++ (NSString *)className; + +/** + Creates an instance of a Realm object with a given value, and adds it to the default Realm. + + If nested objects are included in the argument, `createInDefaultRealmWithValue:` will be recursively called + on them. + + The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in + `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if + any required properties are not present and those properties were not defined with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param value The value used to populate the object. + + @see `defaultPropertyValues` + */ ++ (instancetype)createInDefaultRealmWithValue:(id)value; + +/** + Creates an instance of a Realm object with a given value, and adds it to the specified Realm. + + If nested objects are included in the argument, `createInRealm:withValue:` will be recursively called + on them. + + The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in + `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if any + required properties are not present and those properties were not defined with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param realm The Realm which should manage the newly-created object. + @param value The value used to populate the object. + + @see `defaultPropertyValues` + */ ++ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value; + +/** + Creates or updates a Realm object within the default Realm. + + This method may only be called on Realm object types with a primary key defined. If there is already + an object with the same primary key value in the default Realm, its values are updated and the object + is returned. Otherwise, this method creates and populates a new instance of the object in the default Realm. + + If nested objects are included in the argument, `createOrUpdateInDefaultRealmWithValue:` will be + recursively called on them if they have primary keys, `createInDefaultRealmWithValue:` if they do not. + + If the argument is a Realm object already managed by the default Realm, the argument's type is the same + as the receiver, and the objects have identical values for their managed properties, this method does nothing. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param value The value used to populate the object. + + @see `defaultPropertyValues`, `primaryKey` + */ ++ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value; + +/** + Creates or updates an Realm object within a specified Realm. + + This method may only be called on Realm object types with a primary key defined. If there is already + an object with the same primary key value in the given Realm, its values are updated and the object + is returned. Otherwise this method creates and populates a new instance of this object in the given Realm. + + If nested objects are included in the argument, `createOrUpdateInRealm:withValue:` will be + recursively called on them if they have primary keys, `createInRealm:withValue:` if they do not. + + If the argument is a Realm object already managed by the given Realm, the argument's type is the same + as the receiver, and the objects have identical values for their managed properties, this method does nothing. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param realm The Realm which should own the object. + @param value The value used to populate the object. + + @see `defaultPropertyValues`, `primaryKey` + */ ++ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value; + +#pragma mark - Properties + +/** + The Realm which manages the object, or `nil` if the object is unmanaged. + */ +@property (nonatomic, readonly, nullable) RLMRealm *realm; + +/** + The object schema which lists the managed properties for the object. + */ +@property (nonatomic, readonly) RLMObjectSchema *objectSchema; + +/** + Indicates if the object can no longer be accessed because it is now invalid. + + An object can no longer be accessed if the object has been deleted from the Realm that manages it, or + if `invalidate` is called on that Realm. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + + +#pragma mark - Customizing your Objects + +/** + Returns an array of property names for properties which should be indexed. + + Only string, integer, boolean, and `NSDate` properties are supported. + + @return An array of property names. + */ ++ (NSArray *)indexedProperties; + +/** + Override this method to specify the default values to be used for each property. + + @return A dictionary mapping property names to their default values. + */ ++ (nullable NSDictionary *)defaultPropertyValues; + +/** + Override this method to specify the name of a property to be used as the primary key. + + Only properties of types `RLMPropertyTypeString` and `RLMPropertyTypeInt` can be designated as the primary key. + Primary key properties enforce uniqueness for each value whenever the property is set, which incurs minor overhead. + Indexes are created automatically for primary key properties. + + @return The name of the property designated as the primary key. + */ ++ (nullable NSString *)primaryKey; + +/** + Override this method to specify the names of properties to ignore. These properties will not be managed by the Realm + that manages the object. + + @return An array of property names to ignore. + */ ++ (nullable NSArray *)ignoredProperties; + +/** + Override this method to specify the names of properties that are non-optional (i.e. cannot be assigned a `nil` value). + + By default, all properties of a type whose values can be set to `nil` are considered optional properties. + To require that an object in a Realm always store a non-`nil` value for a property, + add the name of the property to the array returned from this method. + + Properties of `RLMObject` type cannot be non-optional. Array and `NSNumber` properties + can be non-optional, but there is no reason to do so: arrays do not support storing nil, and + if you want a non-optional number you should instead use the primitive type. + + @return An array of property names that are required. + */ ++ (NSArray *)requiredProperties; + +/** + Override this method to provide information related to properties containing linking objects. + + Each property of type `RLMLinkingObjects` must have a key in the dictionary returned by this method consisting + of the property name. The corresponding value must be an instance of `RLMPropertyDescriptor` that describes the class + and property that the property is linked to. + + return @{ @"owners": [RLMPropertyDescriptor descriptorWithClass:Owner.class propertyName:@"dogs"] }; + + @return A dictionary mapping property names to `RLMPropertyDescriptor` instances. + */ ++ (NSDictionary *)linkingObjectsProperties; + + +#pragma mark - Getting & Querying Objects from the Default Realm + +/** + Returns all objects of this object type from the default Realm. + + @return An `RLMResults` containing all objects of this type in the default Realm. + */ ++ (RLMResults *)allObjects; + +/** + Returns all objects of this object type matching the given predicate from the default Realm. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing all objects of this type in the default Realm that match the given predicate. + */ ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + + +/** + Returns all objects of this object type matching the given predicate from the default Realm. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing all objects of this type in the default Realm that match the given predicate. + */ ++ (RLMResults *)objectsWithPredicate:(nullable NSPredicate *)predicate; + +/** + Retrieves the single instance of this object type with the given primary key from the default Realm. + + Returns the object from the default Realm which has the given primary key, or + `nil` if the object does not exist. This is slightly faster than the otherwise + equivalent `[[SubclassName objectsWhere:@"primaryKeyPropertyName = %@", key] firstObject]`. + + This method requires that `primaryKey` be overridden on the receiving subclass. + + @return An object of this object type, or `nil` if an object with the given primary key does not exist. + @see `-primaryKey` + */ ++ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey; + + +#pragma mark - Querying Specific Realms + +/** + Returns all objects of this object type from the specified Realm. + + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm. + */ ++ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm; + +/** + Returns all objects of this object type matching the given predicate from the specified Realm. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm that match the given predicate. + */ ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ...; + +/// :nodoc: ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all objects of this object type matching the given predicate from the specified Realm. + + @param predicate A predicate to use to filter the elements. + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm that match the given predicate. + */ ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(nullable NSPredicate *)predicate; + +/** + Retrieves the single instance of this object type with the given primary key from the specified Realm. + + Returns the object from the specified Realm which has the given primary key, or + `nil` if the object does not exist. This is slightly faster than the otherwise + equivalent `[[SubclassName objectsInRealm:realm where:@"primaryKeyPropertyName = %@", key] firstObject]`. + + This method requires that `primaryKey` be overridden on the receiving subclass. + + @return An object of this object type, or `nil` if an object with the given primary key does not exist. + @see `-primaryKey` + */ ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(nullable id)primaryKey; + +#pragma mark - Notifications + +/** + A callback block for `RLMObject` notifications. + + If the object is deleted from the managing Realm, the block is called with + `deleted` set to `YES` and the other two arguments are `nil`. The block will + never be called again after this. + + If the object is modified, the block will be called with `deleted` set to + `NO`, a `nil` error, and an array of `RLMPropertyChange` objects which + indicate which properties of the objects were modified. + + If an error occurs, `deleted` will be `NO`, `changes` will be `nil`, and + `error` will include information about the error. The block will never be + called again after an error occurs. + */ +typedef void (^RLMObjectChangeBlock)(BOOL deleted, + NSArray *_Nullable changes, + NSError *_Nullable error); + +/** + Registers a block to be called each time the object changes. + + The block will be asynchronously called after each write transaction which + deletes the object or modifies any of the managed properties of the object, + including self-assignments that set a property to its existing value. + + For write transactions performed on different threads or in differen + processes, the block will be called when the managing Realm is + (auto)refreshed to a version including the changes, while for local write + transactions it will be called at some point in the future after the write + transaction is committed. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When notifications + can't be delivered instantly, multiple notifications may be coalesced into a + single notification. + + Unlike with `RLMArray` and `RLMResults`, there is no "initial" callback made + after you add a new notification block. + + Only objects which are managed by a Realm can be observed in this way. You + must retain the returned token for as long as you want updates to be sent to + the block. To stop receiving updates, call `stop` on the token. + + It is safe to capture a strong reference to the observed object within the + callback block. There is no retain cycle due to that the callback is retained + by the returned token and not by the object itself. + + @warning This method cannot be called during a write transaction, when the + containing Realm is read-only, or on an unmanaged object. + + @param block The block to be called whenever a change occurs. + @return A token which must be held for as long as you want updates to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block; + +#pragma mark - Other Instance Methods + +/** + Returns YES if another Realm object instance points to the same object as the receiver in the Realm managing + the receiver. + + For object types with a primary, key, `isEqual:` is overridden to use this method (along with a corresponding + implementation for `hash`). + + @param object The object to compare the receiver to. + + @return Whether the object represents the same object as the receiver. + */ +- (BOOL)isEqualToObject:(RLMObject *)object; + +#pragma mark - Dynamic Accessors + +/// :nodoc: +- (nullable id)objectForKeyedSubscript:(NSString *)key; + +/// :nodoc: +- (void)setObject:(nullable id)obj forKeyedSubscript:(NSString *)key; + +@end + +/** + Information about a specific property which changed in an `RLMObject` change notification. + */ +@interface RLMPropertyChange : NSObject + +/** + The name of the property which changed. + */ +@property (nonatomic, readonly, strong) NSString *name; + +/** + The value of the property before the change occurred. This will always be `nil` + if the change happened on the same thread as the notification and for `RLMArray` + properties. + + For object properties this will give the object which was previously linked to, + but that object will have its new values and not the values it had before the + changes. This means that `previousValue` may be a deleted object, and you will + need to check `invalidated` before accessing any of its properties. + */ +@property (nonatomic, readonly, strong, nullable) id previousValue; + +/** + The value of the property after the change occurred. This will always be `nil` + for `RLMArray` properties. + */ +@property (nonatomic, readonly, strong, nullable) id value; +@end + +#pragma mark - RLMArray Property Declaration + +/** + Properties on `RLMObject`s of type `RLMArray` must have an associated type. A type is associated + with an `RLMArray` property by defining a protocol for the object type that the array should contain. + To define the protocol for an object, you can use the macro RLM_ARRAY_TYPE: + + RLM_ARRAY_TYPE(ObjectType) + ... + @property RLMArray *arrayOfObjectTypes; + */ +#define RLM_ARRAY_TYPE(RLM_OBJECT_SUBCLASS)\ +@protocol RLM_OBJECT_SUBCLASS \ +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase.h new file mode 100644 index 0000000..6b3271f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm; +@class RLMSchema; +@class RLMObjectSchema; + +/// :nodoc: +@interface RLMObjectBase : NSObject + +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + ++ (NSString *)className; + +// Returns whether the class is included in the default set of classes managed by a Realm. ++ (BOOL)shouldIncludeInDefaultSchema; + ++ (nullable NSString *)_realmObjectName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h new file mode 100644 index 0000000..08f25d5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +@class RLMObjectSchema, RLMRealm; + +NS_ASSUME_NONNULL_BEGIN + +/** + Returns the Realm that manages the object, if one exists. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve the Realm that manages the object via `RLMObject`. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + + @return The Realm which manages this object. Returns `nil `for unmanaged objects. + */ +FOUNDATION_EXTERN RLMRealm * _Nullable RLMObjectBaseRealm(RLMObjectBase * _Nullable object); + +/** + Returns an `RLMObjectSchema` which describes the managed properties of the object. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve `objectSchema` via `RLMObject`. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + + @return The object schema which lists the managed properties for the object. + */ +FOUNDATION_EXTERN RLMObjectSchema * _Nullable RLMObjectBaseObjectSchema(RLMObjectBase * _Nullable object); + +/** + Returns the object corresponding to a key value. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve key values via `RLMObject`. + + @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + @param key The name of the property. + + @return The object for the property requested. + */ +FOUNDATION_EXTERN id _Nullable RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase * _Nullable object, NSString *key); + +/** + Sets a value for a key on the object. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to set key values via `RLMObject`. + + @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + @param key The name of the property. + @param obj The object to set as the value of the key. + */ +FOUNDATION_EXTERN void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase * _Nullable object, NSString *key, id _Nullable obj); + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema.h new file mode 100644 index 0000000..808cdfa --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema.h @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class RLMProperty; + +/** + This class represents Realm model object schemas. + + When using Realm, `RLMObjectSchema` instances allow performing migrations and + introspecting the database's schema. + + Object schemas map to tables in the core database. + */ +@interface RLMObjectSchema : NSObject + +#pragma mark - Properties + +/** + An array of `RLMProperty` instances representing the managed properties of a class described by the schema. + + @see `RLMProperty` + */ +@property (nonatomic, readonly, copy) NSArray *properties; + +/** + The name of the class the schema describes. + */ +@property (nonatomic, readonly) NSString *className; + +/** + The property which serves as the primary key for the class the schema describes, if any. + */ +@property (nonatomic, readonly, nullable) RLMProperty *primaryKeyProperty; + +#pragma mark - Methods + +/** + Retrieves an `RLMProperty` object by the property name. + + @param propertyName The property's name. + + @return An `RLMProperty` object, or `nil` if there is no property with the given name. + */ +- (nullable RLMProperty *)objectForKeyedSubscript:(NSString *)propertyName; + +/** + Returns whether two `RLMObjectSchema` instances are equal. + */ +- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.h new file mode 100644 index 0000000..c2968cc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.h @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import + +NS_ASSUME_NONNULL_BEGIN + +// RLMObjectSchema private +@interface RLMObjectSchema () { +@public + bool _isSwiftClass; +} + +/// The object type name reported to the object store and core. +@property (nonatomic, readonly) NSString *objectName; + +// writable redeclaration +@property (nonatomic, readwrite, copy) NSArray *properties; +@property (nonatomic, readwrite, assign) bool isSwiftClass; + +// class used for this object schema +@property (nonatomic, readwrite, assign) Class objectClass; +@property (nonatomic, readwrite, assign) Class accessorClass; +@property (nonatomic, readwrite, assign) Class unmanagedClass; + +@property (nonatomic, readwrite, nullable) RLMProperty *primaryKeyProperty; + +@property (nonatomic, copy) NSArray *computedProperties; +@property (nonatomic, readonly) NSArray *swiftGenericProperties; + +// returns a cached or new schema for a given object class ++ (instancetype)schemaForObjectClass:(Class)objectClass; +@end + +@interface RLMObjectSchema (Dynamic) +/** + This method is useful only in specialized circumstances, for example, when accessing objects + in a Realm produced externally. If you are simply building an app on Realm, it is not recommended + to use this method as an [RLMObjectSchema](RLMObjectSchema) is generated automatically for every [RLMObject](RLMObject) subclass. + + Initialize an RLMObjectSchema with classname, objectClass, and an array of properties + + @warning This method is useful only in specialized circumstances. + + @param objectClassName The name of the class used to refer to objects of this type. + @param objectClass The Objective-C class used when creating instances of this type. + @param properties An array of RLMProperty instances describing the managed properties for this type. + + @return An initialized instance of RLMObjectSchema. + */ +- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp new file mode 100644 index 0000000..3d93be2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObjectSchema_Private.h" + +#import "object_schema.hpp" + +@interface RLMObjectSchema () +// create realm::ObjectSchema copy +- (realm::ObjectSchema)objectStoreCopy; + +// initialize with realm::ObjectSchema ++ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)objectSchema; +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObjectStore.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectStore.h new file mode 100644 index 0000000..3d7f194 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObjectStore.h @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +@class RLMRealm, RLMSchema, RLMObjectBase, RLMResults, RLMProperty; + +NS_ASSUME_NONNULL_BEGIN + +// +// Accessor Creation +// + +// create or get cached accessors for the given schema +void RLMRealmCreateAccessors(RLMSchema *schema); + + +// +// Options for object creation +// +typedef NS_OPTIONS(NSUInteger, RLMCreationOptions) { + // Normal object creation + RLMCreationOptionsNone = 0, + // If the property is a link or array property, upsert the linked objects + // if they have a primary key, and insert them otherwise. + RLMCreationOptionsCreateOrUpdate = 1 << 0, + // Allow unmanaged objects to be promoted to managed objects + // if false objects are copied during object creation + RLMCreationOptionsPromoteUnmanaged = 1 << 1, + // Use the SetDefault instruction. + RLMCreationOptionsSetDefault = 1 << 2, +}; + + +// +// Adding, Removing, Getting Objects +// + +// add an object to the given realm +void RLMAddObjectToRealm(RLMObjectBase *object, RLMRealm *realm, bool createOrUpdate); + +// delete an object from its realm +void RLMDeleteObjectFromRealm(RLMObjectBase *object, RLMRealm *realm); + +// deletes all objects from a realm +void RLMDeleteAllObjectsFromRealm(RLMRealm *realm); + +// get objects of a given class +RLMResults *RLMGetObjects(RLMRealm *realm, NSString *objectClassName, NSPredicate * _Nullable predicate) +NS_RETURNS_RETAINED; + +// get an object with the given primary key +id _Nullable RLMGetObject(RLMRealm *realm, NSString *objectClassName, id _Nullable key) NS_RETURNS_RETAINED; + +// create object from array or dictionary +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id _Nullable value, bool createOrUpdate) +NS_RETURNS_RETAINED; + + +// +// Accessor Creation +// + + +// switch List<> properties from being backed by unmanaged RLMArrays to RLMArrayLinkView +void RLMInitializeSwiftAccessorGenerics(RLMObjectBase *object); + +#ifdef __cplusplus +} + +namespace realm { + class Table; + template class BasicRowExpr; + using RowExpr = BasicRowExpr
; +} +class RLMClassInfo; + +// Create accessors +RLMObjectBase *RLMCreateObjectAccessor(RLMRealm *realm, RLMClassInfo& info, + NSUInteger index) NS_RETURNS_RETAINED; +RLMObjectBase *RLMCreateObjectAccessor(RLMRealm *realm, RLMClassInfo& info, + realm::RowExpr row) NS_RETURNS_RETAINED; +#endif + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.h new file mode 100644 index 0000000..cdb087f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.h @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +// RLMObject accessor and read/write realm +@interface RLMObjectBase () { +@public + RLMRealm *_realm; + __unsafe_unretained RLMObjectSchema *_objectSchema; +} + +// unmanaged initializer +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema NS_DESIGNATED_INITIALIZER; + +// live accessor initializer +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema NS_DESIGNATED_INITIALIZER; + +// shared schema for this class ++ (nullable RLMObjectSchema *)sharedSchema; + +// provide injection point for alternative Swift object util class ++ (Class)objectUtilClass:(BOOL)isSwift; + +@end + +@interface RLMObject () + +// unmanaged initializer +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema NS_DESIGNATED_INITIALIZER; + +// live accessor initializer +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema NS_DESIGNATED_INITIALIZER; + +@end + +@interface RLMDynamicObject : RLMObject + +@end + +// A reference to an object's row that doesn't keep the object accessor alive. +// Used by some Swift property types, such as LinkingObjects, to avoid retain cycles +// with their containing object. +@interface RLMWeakObjectHandle : NSObject + +- (instancetype)initWithObject:(RLMObjectBase *)object; + +// Consumes the row, so can only usefully be called once. +@property (nonatomic, readonly) RLMObjectBase *object; + +@end + +// Calls valueForKey: and re-raises NSUndefinedKeyExceptions +FOUNDATION_EXTERN id _Nullable RLMValidatedValueForProperty(id object, NSString *key, NSString *className); + +// Compare two RLObjectBases +FOUNDATION_EXTERN BOOL RLMObjectBaseAreEqual(RLMObjectBase * _Nullable o1, RLMObjectBase * _Nullable o2); + +typedef void (^RLMObjectNotificationCallback)(NSArray *_Nullable propertyNames, + NSArray *_Nullable oldValues, + NSArray *_Nullable newValues, + NSError *_Nullable error); +FOUNDATION_EXTERN RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block); + +// Get ObjectUil class for objc or swift +FOUNDATION_EXTERN Class RLMObjectUtilClass(BOOL isSwift); + +FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth; + +@class RLMProperty, RLMArray; +@interface RLMObjectUtil : NSObject + ++ (nullable NSArray *)ignoredPropertiesForClass:(Class)cls; ++ (nullable NSArray *)indexedPropertiesForClass:(Class)cls; ++ (nullable NSDictionary *> *)linkingObjectsPropertiesForClass:(Class)cls; + ++ (nullable NSArray *)getGenericListPropertyNames:(id)obj; ++ (nullable NSDictionary *)getLinkingObjectsProperties:(id)object; + ++ (nullable NSDictionary *)getOptionalProperties:(id)obj; ++ (nullable NSArray *)requiredPropertiesForClass:(Class)cls; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.hpp new file mode 100644 index 0000000..7f79dd6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObject_Private.hpp @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMObject_Private.h" + +#import "RLMRealm_Private.hpp" +#import "RLMUtil.hpp" + +#import // required by row.hpp +#import + +class RLMObservationInfo; + +// RLMObject accessor and read/write realm +@interface RLMObjectBase () { + @public + realm::Row _row; + RLMObservationInfo *_observationInfo; + RLMClassInfo *_info; +} +@end + +// FIXME-2.0: This should be folded into initWithRealm:schema:, but changing the +// signature of that is a breaking change for Swift +id RLMCreateManagedAccessor(Class cls, RLMRealm *realm, RLMClassInfo *info) NS_RETURNS_RETAINED; + +// throw an exception if the object is invalidated or on the wrong thread +static inline void RLMVerifyAttached(__unsafe_unretained RLMObjectBase *const obj) { + if (!obj->_row.is_attached()) { + @throw RLMException(@"Object has been deleted or invalidated."); + } + [obj->_realm verifyThread]; +} + +// throw an exception if the object can't be modified for any reason +static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMObjectBase *const obj) { + // first verify is attached + RLMVerifyAttached(obj); + + if (!obj->_realm.inWriteTransaction) { + @throw RLMException(@"Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first."); + } +} diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMObservation.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMObservation.hpp new file mode 100644 index 0000000..0f5b215 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMObservation.hpp @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 + +#import "binding_context.hpp" + +#import +#import + +#import + +@class RLMObjectBase, RLMRealm, RLMSchema, RLMProperty, RLMObjectSchema; +class RLMClassInfo; +class RLMSchemaInfo; + +namespace realm { + class History; + class SharedGroup; +} + +// RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and +// RLMArray. There is a one-to-one relationship between observed objects and +// RLMObservationInfo instances, so it could be folded into RLMObjectBase, and +// is a separate class mostly to avoid making all accessor objects far larger. +// +// RLMClassInfo stores a vector of pointers to the first observation info +// created for each row. If there are multiple observation infos for a single +// row (such as if there are multiple observed objects backed by a single row, +// or if both an object and an array property of that object are observed), +// they're stored in an intrusive doubly-linked-list in the `next` and `prev` +// members. This is done primarily to make it simpler and faster to loop over +// all of the observed objects for a single row, as that needs to be done for +// every change. +class RLMObservationInfo { +public: + RLMObservationInfo(id object); + RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object); + ~RLMObservationInfo(); + + realm::Row const& getRow() const { + return row; + } + + NSString *columnName(size_t col) const noexcept; + + // Send willChange/didChange notifications to all observers for this object/row + // Sends the array versions if indexes is non-nil, normal versions otherwise + void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const; + void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const; + + bool isForRow(size_t ndx) const { + return row && row.get_index() == ndx; + } + + void recordObserver(realm::Row& row, RLMClassInfo *objectInfo, RLMObjectSchema *objectSchema, NSString *keyPath); + void removeObserver(); + bool hasObservers() const { return observerCount > 0; } + + // valueForKey: on observed object and array properties needs to return the + // same object each time for KVO to work at all. Doing this all the time + // requires some odd semantics to avoid reference cycles, so instead we do + // it only to the extent specifically required by KVO. In addition, we + // need to continue to return the same object even if this row is deleted, + // or deleting an object with active observers will explode horribly. + // Once prepareForInvalidation() is called, valueForKey() will always return + // the cached value for object and array properties without checking the + // backing row to verify it's up-to-date. + // + // prepareForInvalidation() must be called on the head of the linked list + // (i.e. on the object pointed to directly by the object schema) + id valueForKey(NSString *key); + + void prepareForInvalidation(); + +private: + // Doubly-linked-list of observed objects for the same row as this + RLMObservationInfo *next = nullptr; + RLMObservationInfo *prev = nullptr; + + // Row being observed + realm::Row row; + RLMClassInfo *objectSchema = nullptr; + + // Object doing the observing + __unsafe_unretained id object = nil; + + // valueForKey: hack + bool invalidated = false; + size_t observerCount = 0; + NSString *lastKey = nil; + __unsafe_unretained RLMProperty *lastProp = nil; + + // objects returned from valueForKey() to keep them alive in case observers + // are added and so that they can still be accessed after row is detached + NSMutableDictionary *cachedObjects; + + void setRow(realm::Table &table, size_t newRow); + + template + void forEach(F&& f) const { + // The user's observation handler may release their last reference to + // the object being observed, which will result in the RLMObservationInfo + // being destroyed. As a result, we need to retain the object which owns + // both `this` and the current info we're looking at. + __attribute__((objc_precise_lifetime)) id self = object, current; + for (auto info = prev; info; info = info->prev) { + current = info->object; + f(info->object); + } + for (auto info = this; info; info = info->next) { + current = info->object; + f(info->object); + } + } + + // Default move/copy constructors don't work due to the intrusive linked + // list and we don't need them + RLMObservationInfo(RLMObservationInfo const&) = delete; + RLMObservationInfo(RLMObservationInfo&&) = delete; + RLMObservationInfo& operator=(RLMObservationInfo const&) = delete; + RLMObservationInfo& operator=(RLMObservationInfo&&) = delete; +}; + +// Get the the observation info chain for the given row +// Will simply return info if it's non-null, and will search ojectSchema's array +// for a matching one otherwise, and return null if there are none +RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, RLMClassInfo& objectSchema); + +// delete all objects from a single table with change notifications +void RLMClearTable(RLMClassInfo &realm); + +// invoke the block, sending notifications for cascading deletes/link nullifications +void RLMTrackDeletions(RLMRealm *realm, dispatch_block_t block); + +std::vector RLMGetObservedRows(RLMSchemaInfo const& schema); +void RLMWillChange(std::vector const& observed, std::vector const& invalidated); +void RLMDidChange(std::vector const& observed, std::vector const& invalidated); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMOptionalBase.h b/Desafio Mobile iOS/Pods/Realm/include/RLMOptionalBase.h new file mode 100644 index 0000000..5ee261e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMOptionalBase.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObjectBase, RLMProperty; + +@interface RLMOptionalBase : NSProxy + +- (instancetype)init; + +@property (nonatomic, weak) RLMObjectBase *object; + +@property (nonatomic, unsafe_unretained) RLMProperty *property; + +@property (nonatomic, strong, nullable) id underlyingValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMPlatform.h b/Desafio Mobile iOS/Pods/Realm/include/RLMPlatform.h new file mode 100644 index 0000000..e69de29 diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMPredicateUtil.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMPredicateUtil.hpp new file mode 100644 index 0000000..71426de --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMPredicateUtil.hpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 +#import + +using ExpressionVisitor = std::function; +NSPredicate *transformPredicate(NSPredicate *, ExpressionVisitor); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMPrefix.h b/Desafio Mobile iOS/Pods/Realm/include/RLMPrefix.h new file mode 100644 index 0000000..df08ce9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMPrefix.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifdef __OBJC__ +#import +#endif + +#ifdef __cplusplus +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMProperty.h b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty.h new file mode 100644 index 0000000..f19c592 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty.h @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/// :nodoc: +@protocol RLMInt +@end + +/// :nodoc: +@protocol RLMBool +@end + +/// :nodoc: +@protocol RLMDouble +@end + +/// :nodoc: +@protocol RLMFloat +@end + +/// :nodoc: +@interface NSNumber () +@end + +/** + `RLMProperty` instances represent properties managed by a Realm in the context of an object schema. Such properties may + be persisted to a Realm file or computed from other data from the Realm. + + When using Realm, `RLMProperty` instances allow performing migrations and introspecting the database's schema. + + These property instances map to columns in the core database. + */ +@interface RLMProperty : NSObject + +#pragma mark - Properties + +/** + The name of the property. + */ +@property (nonatomic, readonly) NSString *name; + +/** + The type of the property. + + @see `RLMPropertyType` + */ +@property (nonatomic, readonly) RLMPropertyType type; + +/** + Indicates whether this property is indexed. + + @see `RLMObject` + */ +@property (nonatomic, readonly) BOOL indexed; + +/** + For `RLMObject` and `RLMArray` properties, the name of the class of object stored in the property. + */ +@property (nonatomic, readonly, copy, nullable) NSString *objectClassName; + +/** + For linking objects properties, the property name of the property the linking objects property is linked to. + */ +@property (nonatomic, readonly, copy, nullable) NSString *linkOriginPropertyName; + +/** + Indicates whether this property is optional. + */ +@property (nonatomic, readonly) BOOL optional; + +#pragma mark - Methods + +/** + Returns whether a given property object is equal to the receiver. + */ +- (BOOL)isEqualToProperty:(RLMProperty *)property; + +@end + + +/** + An `RLMPropertyDescriptor` instance represents a specific property on a given class. + */ +@interface RLMPropertyDescriptor : NSObject + +/** + Creates and returns a property descriptor. + + @param objectClass The class of this property descriptor. + @param propertyName The name of this property descriptor. + */ ++ (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName; + +/// The class of the property. +@property (nonatomic, readonly) Class objectClass; + +/// The name of the property. +@property (nonatomic, readonly) NSString *propertyName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.h new file mode 100644 index 0000000..daaf5a3 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.h @@ -0,0 +1,139 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import + +@class RLMObjectBase; + +NS_ASSUME_NONNULL_BEGIN + +BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType); +BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType); +FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name); + +// Translate an rlmtype to a string representation +static inline NSString *RLMTypeToString(RLMPropertyType type) { + switch (type) { + case RLMPropertyTypeString: + return @"string"; + case RLMPropertyTypeInt: + return @"int"; + case RLMPropertyTypeBool: + return @"bool"; + case RLMPropertyTypeDate: + return @"date"; + case RLMPropertyTypeData: + return @"data"; + case RLMPropertyTypeDouble: + return @"double"; + case RLMPropertyTypeFloat: + return @"float"; + case RLMPropertyTypeAny: + return @"any"; + case RLMPropertyTypeObject: + return @"object"; + case RLMPropertyTypeArray: + return @"array"; + case RLMPropertyTypeLinkingObjects: + return @"linking objects"; + } + return @"Unknown"; +} + +// private property interface +@interface RLMProperty () { +@public + RLMPropertyType _type; + Ivar _swiftIvar; +} + +- (instancetype)initWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(nullable RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property; + +- (instancetype)initSwiftPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(nullable RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property + instance:(RLMObjectBase *)objectInstance; + +- (instancetype)initSwiftListPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(nullable NSString *)objectClassName; + +- (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + ivar:(Ivar)ivar + propertyType:(RLMPropertyType)propertyType; + +- (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(nullable NSString *)objectClassName + linkOriginPropertyName:(nullable NSString *)linkOriginPropertyName; + +// private setters +@property (nonatomic, readwrite) NSString *name; +@property (nonatomic, readwrite, assign) RLMPropertyType type; +@property (nonatomic, readwrite) BOOL indexed; +@property (nonatomic, readwrite) BOOL optional; +@property (nonatomic, copy, nullable) NSString *objectClassName; + +// private properties +@property (nonatomic, assign) NSUInteger index; +@property (nonatomic, assign) BOOL isPrimary; +@property (nonatomic, assign) Ivar swiftIvar; + +// getter and setter names +@property (nonatomic, copy) NSString *getterName; +@property (nonatomic, copy) NSString *setterName; +@property (nonatomic) SEL getterSel; +@property (nonatomic) SEL setterSel; + +- (RLMProperty *)copyWithNewName:(NSString *)name; + +@end + +@interface RLMProperty (Dynamic) +/** + This method is useful only in specialized circumstances, for example, in conjunction with + +[RLMObjectSchema initWithClassName:objectClass:properties:]. If you are simply building an + app on Realm, it is not recommened to use this method. + + Initialize an RLMProperty + + @warning This method is useful only in specialized circumstances. + + @param name The property name. + @param type The property type. + @param objectClassName The object type used for Object and Array types. + @param linkOriginPropertyName The property name of the origin of a link. Used for linking objects properties. + + @return An initialized instance of RLMProperty. + */ +- (instancetype)initWithName:(NSString *)name + type:(RLMPropertyType)type + objectClassName:(nullable NSString *)objectClassName + linkOriginPropertyName:(nullable NSString *)linkOriginPropertyName + indexed:(BOOL)indexed + optional:(BOOL)optional; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.hpp new file mode 100644 index 0000000..0e214d5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMProperty_Private.hpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "property.hpp" + +@interface RLMProperty () + ++ (instancetype)propertyForObjectStoreProperty:(const realm::Property&)property; + +- (realm::Property)objectStoreCopy; + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMQueryUtil.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMQueryUtil.hpp new file mode 100644 index 0000000..1be0c4e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMQueryUtil.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import + +namespace realm { + class Group; + class Query; + class SortDescriptor; +} + +@class RLMObjectSchema, RLMProperty, RLMSchema, RLMSortDescriptor; +class RLMClassInfo; + +extern NSString * const RLMPropertiesComparisonTypeMismatchException; +extern NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException; + +realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, + RLMSchema *schema, realm::Group &group); + +// return property - throw for invalid column name +RLMProperty *RLMValidatedProperty(RLMObjectSchema *objectSchema, NSString *columnName); + +// validate the array of RLMSortDescriptors and convert it to a realm::SortDescriptor +realm::SortDescriptor RLMSortDescriptorFromDescriptors(RLMClassInfo& classInfo, NSArray *descriptors); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealm.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm.h new file mode 100644 index 0000000..15fe342 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm.h @@ -0,0 +1,652 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import "RLMConstants.h" + +@class RLMRealmConfiguration, RLMRealm, RLMObject, RLMSchema, RLMMigration, RLMNotificationToken, RLMThreadSafeReference; + +/** + A callback block for opening Realms asynchronously. + + Returns the Realm if the open was successful, or an error otherwise. + */ +typedef void(^RLMAsyncOpenRealmCallback)(RLMRealm * _Nullable realm, NSError * _Nullable error); + +NS_ASSUME_NONNULL_BEGIN + +/** + An `RLMRealm` instance (also referred to as "a Realm") represents a Realm + database. + + Realms can either be stored on disk (see `+[RLMRealm realmWithURL:]`) or in + memory (see `RLMRealmConfiguration`). + + `RLMRealm` instances are cached internally, and constructing equivalent `RLMRealm` + objects (for example, by using the same path or identifier) multiple times on a single thread + within a single iteration of the run loop will normally return the same + `RLMRealm` object. + + If you specifically want to ensure an `RLMRealm` instance is + destroyed (for example, if you wish to open a Realm, check some property, and + then possibly delete the Realm file and re-open it), place the code which uses + the Realm within an `@autoreleasepool {}` and ensure you have no other + strong references to it. + + @warning `RLMRealm` instances are not thread safe and cannot be shared across + threads or dispatch queues. Trying to do so will cause an exception to be thrown. + You must call this method on each thread you want + to interact with the Realm on. For dispatch queues, this means that you must + call it in each block which is dispatched, as a queue is not guaranteed to run + all of its blocks on the same thread. + */ + +@interface RLMRealm : NSObject + +#pragma mark - Creating & Initializing a Realm + +/** + Obtains an instance of the default Realm. + + The default Realm is used by the `RLMObject` class methods + which do not take an `RLMRealm` parameter, but is otherwise not special. The + default Realm is persisted as *default.realm* under the *Documents* directory of + your Application on iOS, and in your application's *Application Support* + directory on OS X. + + The default Realm is created using the default `RLMRealmConfiguration`, which + can be changed via `+[RLMRealmConfiguration setDefaultConfiguration:]`. + + @return The default `RLMRealm` instance for the current thread. + */ ++ (instancetype)defaultRealm; + +/** + Obtains an `RLMRealm` instance with the given configuration. + + @param configuration A configuration object to use when creating the Realm. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return An `RLMRealm` instance. + */ ++ (nullable instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; + +/** + Obtains an `RLMRealm` instance persisted at a specified file URL. + + @param fileURL The local URL of the file the Realm should be saved at. + + @return An `RLMRealm` instance. + */ ++ (instancetype)realmWithURL:(NSURL *)fileURL; + +/** + Asynchronously open a Realm and deliver it to a block on the given queue. + + Opening a Realm asynchronously will perform all work needed to get the Realm to + a usable state (such as running potentially time-consuming migrations) on a + background thread before dispatching to the given queue. In addition, + synchronized Realms wait for all remote content available at the time the + operation began to be downloaded and available locally. + + @param configuration A configuration object to use when opening the Realm. + @param callbackQueue The dispatch queue on which the callback should be run. + @param callback A callback block. If the Realm was successfully opened, + it will be passed in as an argument. + Otherwise, an `NSError` describing what went wrong will be + passed to the block instead. + + @note The returned Realm is confined to the thread on which it was created. + Because GCD does not guarantee that queues will always use the same + thread, accessing the returned Realm outside the callback block (even if + accessed from `callbackQueue`) is unsafe. + */ ++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration + callbackQueue:(dispatch_queue_t)callbackQueue + callback:(RLMAsyncOpenRealmCallback)callback; + +/** + The `RLMSchema` used by the Realm. + */ +@property (nonatomic, readonly) RLMSchema *schema; + +/** + Indicates if the Realm is currently engaged in a write transaction. + + @warning Do not simply check this property and then start a write transaction whenever an object needs to be + created, updated, or removed. Doing so might cause a large number of write transactions to be created, + degrading performance. Instead, always prefer performing multiple updates during a single transaction. + */ +@property (nonatomic, readonly) BOOL inWriteTransaction; + +/** + The `RLMRealmConfiguration` object that was used to create this `RLMRealm` instance. + */ +@property (nonatomic, readonly) RLMRealmConfiguration *configuration; + +/** + Indicates if this Realm contains any objects. + */ +@property (nonatomic, readonly) BOOL isEmpty; + +#pragma mark - Notifications + +/** + The type of a block to run whenever the data within the Realm is modified. + + @see `-[RLMRealm addNotificationBlock:]` + */ +typedef void (^RLMNotificationBlock)(RLMNotification notification, RLMRealm *realm); + +#pragma mark - Receiving Notification when a Realm Changes + +/** + Adds a notification handler for changes in this Realm, and returns a notification token. + + Notification handlers are called after each write transaction is committed, + either on the current thread or other threads. + + Handler blocks are called on the same thread that they were added on, and may + only be added on threads which are currently within a run loop. Unless you are + specifically creating and running a run loop on a background thread, this will + normally only be the main thread. + + The block has the following definition: + + typedef void(^RLMNotificationBlock)(RLMNotification notification, RLMRealm *realm); + + It receives the following parameters: + + - `NSString` \***notification**: The name of the incoming notification. See + `RLMRealmNotification` for information on what + notifications are sent. + - `RLMRealm` \***realm**: The Realm for which this notification occurred. + + @param block A block which is called to process Realm notifications. + + @return A token object which must be retained as long as you wish to continue + receiving change notifications. + */ +- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block __attribute__((warn_unused_result)); + +#pragma mark - Transactions + + +#pragma mark - Writing to a Realm + +/** + Begins a write transaction on the Realm. + + Only one write transaction can be open at a time for each Realm file. Write + transactions cannot be nested, and trying to begin a write transaction on a + Realm which is already in a write transaction will throw an exception. Calls to + `beginWriteTransaction` from `RLMRealm` instances for the same Realm file in + other threads or other processes will block until the current write transaction + completes or is cancelled. + + Before beginning the write transaction, `beginWriteTransaction` updates the + `RLMRealm` instance to the latest Realm version, as if `refresh` had been + called, and generates notifications if applicable. This has no effect if the + Realm was already up to date. + + It is rarely a good idea to have write transactions span multiple cycles of + the run loop, but if you do wish to do so you will need to ensure that the + Realm participating in the write transaction is kept alive until the write + transaction is committed. + */ +- (void)beginWriteTransaction; + +/** + Commits all write operations in the current write transaction, and ends the + transaction. + + After saving the changes, all notification blocks registered on this specific + `RLMRealm` instance are invoked synchronously. Notification blocks registered + on other threads or on collections are invoked asynchronously. If you do not + want to receive a specific notification for this write tranaction, see + `commitWriteTransactionWithoutNotifying:error:`. + + This method can fail if there is insufficient disk space available to save the + writes made, or due to unexpected i/o errors. This version of the method throws + an exception when errors occur. Use the version with a `NSError` out parameter + instead if you wish to handle errors. + + @warning This method may only be called during a write transaction. + */ +- (void)commitWriteTransaction NS_SWIFT_UNAVAILABLE(""); + +/** + Commits all write operations in the current write transaction, and ends the + transaction. + + After saving the changes, all notification blocks registered on this specific + `RLMRealm` instance are invoked synchronously. Notification blocks registered + on other threads or on collections are invoked asynchronously. If you do not + want to receive a specific notification for this write tranaction, see + `commitWriteTransactionWithoutNotifying:error:`. + + This method can fail if there is insufficient disk space available to save the + writes made, or due to unexpected i/o errors. + + @warning This method may only be called during a write transaction. + + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return Whether the transaction succeeded. + */ +- (BOOL)commitWriteTransaction:(NSError **)error; + +/** + Commits all write operations in the current write transaction, without + notifying specific notification blocks of the changes. + + After saving the changes, all notification blocks registered on this specific + `RLMRealm` instance are invoked synchronously. Notification blocks registered + on other threads or on collections are scheduled to be invoked asynchronously. + + You can skip notifiying specific notification blocks about the changes made + in this write transaction by passing in their associated notification tokens. + This is primarily useful when the write transaction is saving changes already + made in the UI and you do not want to have the notification block attempt to + re-apply the same changes. + + The tokens passed to this method must be for notifications for this specific + `RLMRealm` instance. Notifications for different threads cannot be skipped + using this method. + + This method can fail if there is insufficient disk space available to save the + writes made, or due to unexpected i/o errors. + + @warning This method may only be called during a write transaction. + + @param tokens An array of notification tokens which were returned from adding + callbacks which you do not want to be notified for the changes + made in this write transaction. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return Whether the transaction succeeded. + */ +- (BOOL)commitWriteTransactionWithoutNotifying:(NSArray *)tokens error:(NSError **)error; + +/** + Reverts all writes made during the current write transaction and ends the transaction. + + This rolls back all objects in the Realm to the state they were in at the + beginning of the write transaction, and then ends the transaction. + + This restores the data for deleted objects, but does not revive invalidated + object instances. Any `RLMObject`s which were added to the Realm will be + invalidated rather than becoming unmanaged. + Given the following code: + + ObjectType *oldObject = [[ObjectType objectsWhere:@"..."] firstObject]; + ObjectType *newObject = [[ObjectType alloc] init]; + + [realm beginWriteTransaction]; + [realm addObject:newObject]; + [realm deleteObject:oldObject]; + [realm cancelWriteTransaction]; + + Both `oldObject` and `newObject` will return `YES` for `isInvalidated`, + but re-running the query which provided `oldObject` will once again return + the valid object. + + KVO observers on any objects which were modified during the transaction will + be notified about the change back to their initial values, but no other + notifcations are produced by a cancelled write transaction. + + @warning This method may only be called during a write transaction. + */ +- (void)cancelWriteTransaction; + +/** + Performs actions contained within the given block inside a write transaction. + + @see `[RLMRealm transactionWithBlock:error:]` + */ +- (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block NS_SWIFT_UNAVAILABLE(""); + +/** + Performs actions contained within the given block inside a write transaction. + + Write transactions cannot be nested, and trying to execute a write transaction + on a Realm which is already participating in a write transaction will throw an + exception. Calls to `transactionWithBlock:` from `RLMRealm` instances in other + threads will block until the current write transaction completes. + + Before beginning the write transaction, `transactionWithBlock:` updates the + `RLMRealm` instance to the latest Realm version, as if `refresh` had been called, and + generates notifications if applicable. This has no effect if the Realm + was already up to date. + + @param block The block containing actions to perform. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return Whether the transaction succeeded. + */ +- (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)error; + +/** + Updates the Realm and outstanding objects managed by the Realm to point to the + most recent data. + + If the version of the Realm is actually changed, Realm and collection + notifications will be sent to reflect the changes. This may take some time, as + collection notifications are prepared on a background thread. As a result, + calling this method on the main thread is not advisable. + + @return Whether there were any updates for the Realm. Note that `YES` may be + returned even if no data actually changed. + */ +- (BOOL)refresh; + +/** + Set this property to `YES` to automatically update this Realm when changes + happen in other threads. + + If set to `YES` (the default), changes made on other threads will be reflected + in this Realm on the next cycle of the run loop after the changes are + committed. If set to `NO`, you must manually call `-refresh` on the Realm to + update it to get the latest data. + + Note that by default, background threads do not have an active run loop and you + will need to manually call `-refresh` in order to update to the latest version, + even if `autorefresh` is set to `YES`. + + Even with this property enabled, you can still call `-refresh` at any time to + update the Realm before the automatic refresh would occur. + + Write transactions will still always advance a Realm to the latest version and + produce local notifications on commit even if autorefresh is disabled. + + Disabling `autorefresh` on a Realm without any strong references to it will not + have any effect, and `autorefresh` will revert back to `YES` the next time the + Realm is created. This is normally irrelevant as it means that there is nothing + to refresh (as managed `RLMObject`s, `RLMArray`s, and `RLMResults` have strong + references to the Realm that manages them), but it means that setting + `RLMRealm.defaultRealm.autorefresh = NO` in + `application:didFinishLaunchingWithOptions:` and only later storing Realm + objects will not work. + + Defaults to `YES`. + */ +@property (nonatomic) BOOL autorefresh; + +/** + Writes a compacted and optionally encrypted copy of the Realm to the given local URL. + + The destination file cannot already exist. + + Note that if this method is called from within a write transaction, the + *current* data is written, not the data from the point when the previous write + transaction was committed. + + @param fileURL Local URL to save the Realm to. + @param key Optional 64-byte encryption key to encrypt the new file with. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return `YES` if the Realm was successfully written to disk, `NO` if an error occurred. +*/ +- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error; + +/** + Invalidates all `RLMObject`s, `RLMResults`, `RLMLinkingObjects`, and `RLMArray`s managed by the Realm. + + A Realm holds a read lock on the version of the data accessed by it, so + that changes made to the Realm on different threads do not modify or delete the + data seen by this Realm. Calling this method releases the read lock, + allowing the space used on disk to be reused by later write transactions rather + than growing the file. This method should be called before performing long + blocking operations on a background thread on which you previously read data + from the Realm which you no longer need. + + All `RLMObject`, `RLMResults` and `RLMArray` instances obtained from this + `RLMRealm` instance on the current thread are invalidated. `RLMObject`s and `RLMArray`s + cannot be used. `RLMResults` will become empty. The Realm itself remains valid, + and a new read transaction is implicitly begun the next time data is read from the Realm. + + Calling this method multiple times in a row without reading any data from the + Realm, or before ever reading any data from the Realm, is a no-op. This method + may not be called on a read-only Realm. + */ +- (void)invalidate; + +#pragma mark - Accessing Objects + +/** + Returns the same object as the one referenced when the `RLMThreadSafeReference` was first created, + but resolved for the current Realm for this thread. Returns `nil` if this object was deleted after + the reference was created. + + @param reference The thread-safe reference to the thread-confined object to resolve in this Realm. + + @warning A `RLMThreadSafeReference` object must be resolved at most once. + Failing to resolve a `RLMThreadSafeReference` will result in the source version of the + Realm being pinned until the reference is deallocated. + An exception will be thrown if a reference is resolved more than once. + + @warning Cannot call within a write transaction. + + @note Will refresh this Realm if the source Realm was at a later version than this one. + + @see `+[RLMThreadSafeReference referenceWithThreadConfined:]` + */ +- (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference +NS_REFINED_FOR_SWIFT; + +#pragma mark - Adding and Removing Objects from a Realm + +/** + Adds an object to the Realm. + + Once added, this object is considered to be managed by the Realm. It can be retrieved + using the `objectsWhere:` selectors on `RLMRealm` and on subclasses of `RLMObject`. + + When added, all child relationships referenced by this object will also be added to + the Realm if they are not already in it. + + If the object or any related objects are already being managed by a different Realm + an exception will be thrown. Use `-[RLMObject createInRealm:withObject:]` to insert a copy of a managed object + into a different Realm. + + The object to be added must be valid and cannot have been previously deleted + from a Realm (i.e. `isInvalidated` must be `NO`). + + @warning This method may only be called during a write transaction. + + @param object The object to be added to this Realm. + */ +- (void)addObject:(RLMObject *)object; + +/** + Adds all the objects in a collection to the Realm. + + This is the equivalent of calling `addObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An enumerable object such as `NSArray` or `RLMResults` which contains objects to be added to + the Realm. + + @see `addObject:` + */ +- (void)addObjects:(id)array; + +/** + Adds or updates an existing object into the Realm. + + The object provided must have a designated primary key. If no objects exist in the Realm + with the same primary key value, the object is inserted. Otherwise, the existing object is + updated with any changed values. + + As with `addObject:`, the object cannot already be managed by a different + Realm. Use `-[RLMObject createOrUpdateInRealm:withValue:]` to copy values to + a different Realm. + + @warning This method may only be called during a write transaction. + + @param object The object to be added or updated. + */ +- (void)addOrUpdateObject:(RLMObject *)object; + +/** + Adds or updates all the objects in a collection into the Realm. + + This is the equivalent of calling `addOrUpdateObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An `NSArray`, `RLMArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be added to the Realm. + + @see `addOrUpdateObject:` + */ +- (void)addOrUpdateObjectsFromArray:(id)array; + +/** + Deletes an object from the Realm. Once the object is deleted it is considered invalidated. + + @warning This method may only be called during a write transaction. + + @param object The object to be deleted. + */ +- (void)deleteObject:(RLMObject *)object; + +/** + Deletes one or more objects from the Realm. + + This is the equivalent of calling `deleteObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An `RLMArray`, `NSArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be deleted. + + @see `deleteObject:` + */ +- (void)deleteObjects:(id)array; + +/** + Deletes all objects from the Realm. + + @warning This method may only be called during a write transaction. + + @see `deleteObject:` + */ +- (void)deleteAllObjects; + + +#pragma mark - Migrations + +/** + The type of a migration block used to migrate a Realm. + + @param migration A `RLMMigration` object used to perform the migration. The + migration object allows you to enumerate and alter any + existing objects which require migration. + + @param oldSchemaVersion The schema version of the Realm being migrated. + */ +typedef void (^RLMMigrationBlock)(RLMMigration *migration, uint64_t oldSchemaVersion); + +/** + Returns the schema version for a Realm at a given local URL. + + @param fileURL Local URL to a Realm file. + @param key 64-byte key used to encrypt the file, or `nil` if it is unencrypted. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return The version of the Realm at `fileURL`, or `RLMNotVersioned` if the version cannot be read. + */ ++ (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error +NS_REFINED_FOR_SWIFT; + +/** + Performs the given Realm configuration's migration block on a Realm at the given path. + + This method is called automatically when opening a Realm for the first time and does + not need to be called explicitly. You can choose to call this method to control + exactly when and how migrations are performed. + + @param configuration The Realm configuration used to open and migrate the Realm. + @return The error that occurred while applying the migration, if any. + + @see RLMMigration + */ ++ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration +__deprecated_msg("Use `performMigrationForConfiguration:error:`") NS_REFINED_FOR_SWIFT; + +/** + Performs the given Realm configuration's migration block on a Realm at the given path. + + This method is called automatically when opening a Realm for the first time and does + not need to be called explicitly. You can choose to call this method to control + exactly when and how migrations are performed. + + @param configuration The Realm configuration used to open and migrate the Realm. + @return The error that occurred while applying the migration, if any. + + @see RLMMigration + */ ++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; + +#pragma mark - Unavailable Methods + +/** + RLMRealm instances are cached internally by Realm and cannot be created directly. + + Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or + `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm. + */ +- (instancetype)init __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:."))); + +/** + RLMRealm instances are cached internally by Realm and cannot be created directly. + + Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or + `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm. + */ ++ (instancetype)new __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:."))); + +@end + +/** + A token which is returned from methods which subscribe to changes to a Realm. + + Change subscriptions in Realm return an `RLMNotificationToken` instance, + which can be used to unsubscribe from the changes. You must store a strong + reference to the token for as long as you want to continue to receive notifications. + When you wish to stop, call the `-stop` method. Notifications are also stopped if + the token is deallocated. + */ +@interface RLMNotificationToken : NSObject +/// Stops notifications for the change subscription that returned this token. +- (void)stop; +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h new file mode 100644 index 0000000..b7e04b6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil.h" + +@class RLMSyncConfiguration; + +/// :nodoc: +@interface RLMRealmConfiguration (Sync) + +NS_ASSUME_NONNULL_BEGIN + +/** + A configuration object representing configuration state for Realms intended + to sync with a Realm Object Server. + + This property is mutually exclusive with both `inMemoryIdentifier` and `fileURL`; + setting any one of the three properties will automatically nil out the other two. + + @see `RLMSyncConfiguration` + */ +@property (nullable, nonatomic) RLMSyncConfiguration *syncConfiguration; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration.h new file mode 100644 index 0000000..377495d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration.h @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + A block called when opening a Realm for the first time during the life + of a process to determine if it should be compacted before being returned + to the user. It is passed the total file size (data + free space) and the total + bytes used by data in the file. + + Return `YES` to indicate that an attempt to compact the file should be made. + The compaction will be skipped if another process is accessing it. + */ +typedef BOOL (^RLMShouldCompactOnLaunchBlock)(NSUInteger totalBytes, NSUInteger bytesUsed); + +/** + An `RLMRealmConfiguration` instance describes the different options used to + create an instance of a Realm. + + `RLMRealmConfiguration` instances are just plain `NSObject`s. Unlike `RLMRealm`s + and `RLMObject`s, they can be freely shared between threads as long as you do not + mutate them. + + Creating configuration objects for class subsets (by setting the + `objectClasses` property) can be expensive. Because of this, you will normally want to + cache and reuse a single configuration object for each distinct configuration rather than + creating a new object each time you open a Realm. + */ +@interface RLMRealmConfiguration : NSObject + +#pragma mark - Default Configuration + +/** + Returns the default configuration used to create Realms when no other + configuration is explicitly specified (i.e. `+[RLMRealm defaultRealm]`). + + @return The default Realm configuration. + */ ++ (instancetype)defaultConfiguration; + +/** + Sets the default configuration to the given `RLMRealmConfiguration`. + + @param configuration The new default Realm configuration. + */ ++ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration; + +#pragma mark - Properties + +/// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier`. +@property (nonatomic, copy, nullable) NSURL *fileURL; + +/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL`. +@property (nonatomic, copy, nullable) NSString *inMemoryIdentifier; + +/// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled. +@property (nonatomic, copy, nullable) NSData *encryptionKey; + +/// Whether to open the Realm in read-only mode. +/// +/// This is required to be able to open Realm files which are not writeable or +/// are in a directory which is not writeable. This should only be used on files +/// which will not be modified by anyone while they are open, and not just to +/// get a read-only view of a file which may be written to by another thread or +/// process. Opening in read-only mode requires disabling Realm's reader/writer +/// coordination, so committing a write transaction from another process will +/// result in crashes. +@property (nonatomic) BOOL readOnly; + +/// The current schema version. +@property (nonatomic) uint64_t schemaVersion; + +/// The block which migrates the Realm to the current version. +@property (nonatomic, copy, nullable) RLMMigrationBlock migrationBlock; + +/** + Whether to recreate the Realm file with the provided schema if a migration is required. + This is the case when the stored schema differs from the provided schema or + the stored schema version differs from the version on this configuration. + Setting this property to `YES` deletes the file if a migration would otherwise be required or executed. + + @note Setting this property to `YES` doesn't disable file format migrations. + */ +@property (nonatomic) BOOL deleteRealmIfMigrationNeeded; + +/** + A block called when opening a Realm for the first time during the life + of a process to determine if it should be compacted before being returned + to the user. It is passed the total file size (data + free space) and the total + bytes used by data in the file. + + Return `YES` to indicate that an attempt to compact the file should be made. + The compaction will be skipped if another process is accessing it. + */ +@property (nonatomic, copy, nullable) RLMShouldCompactOnLaunchBlock shouldCompactOnLaunch; + +/// The classes managed by the Realm. +@property (nonatomic, copy, nullable) NSArray *objectClasses; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h new file mode 100644 index 0000000..e930603 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 + +@class RLMSchema; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMRealmConfiguration () + +@property (nonatomic, readwrite) bool cache; +@property (nonatomic, readwrite) bool dynamic; +@property (nonatomic, readwrite) bool disableFormatUpgrade; +@property (nonatomic, copy, nullable) RLMSchema *customSchema; +@property (nonatomic, copy) NSString *pathOnDisk; + +// Get the default confiugration without copying it ++ (RLMRealmConfiguration *)rawDefaultConfiguration; + ++ (void)resetRealmConfigurationState; +@end + +// Get a path in the platform-appropriate documents directory with the given filename +FOUNDATION_EXTERN NSString *RLMRealmPathForFile(NSString *fileName); +FOUNDATION_EXTERN NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *mainBundleIdentifier); + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp new file mode 100644 index 0000000..a89fb0f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMRealmConfiguration_Private.h" +#import "shared_realm.hpp" + +@interface RLMRealmConfiguration () +- (realm::Realm::Config&)config; + +@property (nonatomic) realm::SchemaMode schemaMode; +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealmUtil.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmUtil.hpp new file mode 100644 index 0000000..36dbc84 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealmUtil.hpp @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import +#import + +@class RLMRealm; + +namespace realm { + class BindingContext; +} + +// Add a Realm to the weak cache +void RLMCacheRealm(std::string const& path, RLMRealm *realm); +// Get a Realm for the given path which can be used on the current thread +RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path); +// Get a Realm for the given path +RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path); +// Clear the weak cache of Realms +void RLMClearRealmCache(); + +std::unique_ptr RLMCreateBindingContext(RLMRealm *realm); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Dynamic.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Dynamic.h new file mode 100644 index 0000000..3a26f49 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Dynamic.h @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import +#import + +@class RLMResults; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMRealm (Dynamic) + +#pragma mark - Getting Objects from a Realm + +/** + Returns all objects of a given type from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The name of the `RLMObject` subclass to retrieve on (e.g. `MyClass.className`). + + @return An `RLMResults` containing all objects in the Realm of the given type. + + @see `+[RLMObject allObjects]` + */ +- (RLMResults *)allObjects:(NSString *)className; + +/** + Returns all objects matching the given predicate from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The type of objects you are looking for (name of the class). + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing results matching the given predicate. + + @see `+[RLMObject objectsWhere:]` + */ +- (RLMResults *)objects:(NSString *)className where:(NSString *)predicateFormat, ...; + +/** + Returns all objects matching the given predicate from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The type of objects you are looking for (name of the class). + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing results matching the given predicate. + + @see `+[RLMObject objectsWhere:]` + */ +- (RLMResults *)objects:(NSString *)className withPredicate:(NSPredicate *)predicate; + +/** + Returns the object of the given type with the given primary key from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get an object of a single class is to use the class + methods on `RLMObject`. + + @param className The class name for the object you are looking for. + @param primaryKey The primary key value for the object you are looking for. + + @return An object, or `nil` if an object with the given primary key does not exist. + + @see `+[RLMObject objectForPrimaryKey:]` + */ +- (nullable RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey; + +/** + Creates an `RLMObject` instance of type `className` in the Realm, and populates it using a given object. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is recommended to + use `[RLMObject createInDefaultRealmWithValue:]`. + + @param value The value used to populate the object. + + @return An `RLMObject` instance of type `className`. + */ +-(RLMObject *)createObject:(NSString *)className withValue:(id)value; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.h new file mode 100644 index 0000000..c8ca2d2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +@class RLMFastEnumerator; + +NS_ASSUME_NONNULL_BEGIN + +// Disable syncing files to disk. Cannot be re-enabled. Use only for tests. +FOUNDATION_EXTERN void RLMDisableSyncToDisk(); + +FOUNDATION_EXTERN NSData * _Nullable RLMRealmValidatedEncryptionKey(NSData *key); + +// Translate an in-flight exception resulting from opening a SharedGroup to +// an NSError or NSException (if error is nil) +void RLMRealmTranslateException(NSError **error); + +// RLMRealm private members +@interface RLMRealm () + +@property (nonatomic, readonly) BOOL dynamic; +@property (nonatomic, readwrite) RLMSchema *schema; + ++ (void)resetRealmState; + +- (void)registerEnumerator:(RLMFastEnumerator *)enumerator; +- (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator; +- (void)detachAllEnumerators; + +- (void)sendNotifications:(RLMNotification)notification; +- (void)verifyThread; +- (void)verifyNotificationsAreSupported; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.hpp new file mode 100644 index 0000000..a1bb294 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMRealm_Private.hpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 "RLMRealm_Private.h" + +#import "RLMClassInfo.hpp" + +namespace realm { + class Group; + class Realm; +} + +@interface RLMRealm () { + @public + std::shared_ptr _realm; + RLMSchemaInfo _info; +} + +// FIXME - group should not be exposed +@property (nonatomic, readonly) realm::Group &group; +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMResults.h b/Desafio Mobile iOS/Pods/Realm/include/RLMResults.h new file mode 100644 index 0000000..4250757 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMResults.h @@ -0,0 +1,342 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObject, RLMRealm, RLMNotificationToken; + +/** + `RLMResults` is an auto-updating container type in Realm returned from object + queries. It represents the results of the query in the form of a collection of objects. + + `RLMResults` can be queried using the same predicates as `RLMObject` and `RLMArray`, + and you can chain queries to further filter results. + + `RLMResults` always reflect the current state of the Realm on the current thread, + including during write transactions on the current thread. The one exception to + this is when using `for...in` fast enumeration, which will always enumerate + over the objects which matched the query when the enumeration is begun, even if + some of them are deleted or modified to be excluded by the filter during the + enumeration. + + `RLMResults` are lazily evaluated the first time they are accessed; they only + run queries when the result of the query is requested. This means that + chaining several temporary `RLMResults` to sort and filter your data does not + perform any extra work processing the intermediate state. + + Once the results have been evaluated or a notification block has been added, + the results are eagerly kept up-to-date, with the work done to keep them + up-to-date done on a background thread whenever possible. + + `RLMResults` cannot be directly instantiated. + */ +@interface RLMResults : NSObject + +#pragma mark - Properties + +/** + The number of objects in the results collection. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the results collection. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages this results collection. + */ +@property (nonatomic, readonly) RLMRealm *realm; + +/** + Indicates if the results collection is no longer valid. + + The results collection becomes invalid if `invalidate` is called on the containing `realm`. + An invalidated results collection can be accessed, but will always be empty. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +#pragma mark - Accessing Objects from an RLMResults + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (RLMObjectType)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the results collection. + + Returns `nil` if called on an empty results collection. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (nullable RLMObjectType)firstObject; + +/** + Returns the last object in the results collection. + + Returns `nil` if called on an empty results collection. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (nullable RLMObjectType)lastObject; + +#pragma mark - Querying Results + +/** + Returns the index of an object in the results collection. + + Returns `NSNotFound` if the object is not found in the results collection. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObjectType)object; + +/** + Returns the index of the first object in the results collection matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the results collection. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the results collection matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the results collection. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all the objects matching the given predicate in the results collection. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all the objects matching the given predicate in the results collection. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from an existing results collection. + + @param keyPath The key path to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified key path. + */ +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from an existing results collection. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending + __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); + +/** + Returns a sorted `RLMResults` from an existing results collection. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the results collection changes. + + The block will be asynchronously called with the initial results collection, + and then called again after each write transaction which changes either any + of the objects in the results, or which objects are in the results. + + The `change` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the results collection were added, removed or modified. If a + write transaction did not modify any objects in the results collection, + the block is not called at all. See the `RLMCollectionChange` documentation for + information on how the changes are reported and an example of updating a + `UITableView`. + + If an error occurs the block will be called with `nil` for the results + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + At the time when the block is called, the `RLMResults` object will be fully + evaluated and up-to-date, and as long as you do not perform a write transaction + on the same thread or explicitly call `-[RLMRealm refresh]`, accessing it will + never perform blocking work. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial results. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + RLMResults *results = [Dog allObjects]; + NSLog(@"dogs.count: %zu", dogs.count); // => 0 + self.token = [results addNotificationBlock:^(RLMResults *dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count); // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [realm addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + + @param block The block to be called whenever a change occurs. + @return A token which must be held for as long as you want updates to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *__nullable results, + RLMCollectionChange *__nullable change, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects + represented by the results collection. + + NSNumber *min = [results minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of types `int`, `float`, `double`, and + `NSDate` are supported. + + @return The minimum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects represented by the results collection. + + NSNumber *max = [results maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of types `int`, `float`, `double`, and + `NSDate` are supported. + + @return The maximum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects represented by the results collection. + + NSNumber *sum = [results sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of types `int`, `float`, and `double` are + supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects represented by the results collection. + + NSNumber *average = [results averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only properties of types `int`, `float`, and + `double` are supported. + + @return The average value of the given property, or `nil` if the Results are empty. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + +/// :nodoc: +- (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index; + +#pragma mark - Unavailable Methods + +/** + `-[RLMResults init]` is not available because `RLMResults` cannot be created directly. + `RLMResults` can be obtained by querying a Realm. + */ +- (instancetype)init __attribute__((unavailable("RLMResults cannot be created directly"))); + +/** + `+[RLMResults new]` is not available because `RLMResults` cannot be created directly. + `RLMResults` can be obtained by querying a Realm. + */ ++ (instancetype)new __attribute__((unavailable("RLMResults cannot be created directly"))); + +@end + +/** + `RLMLinkingObjects` is an auto-updating container type. It represents a collection of objects that link to its + parent object. + + For more information, please see the "Inverse Relationships" section in the + [documentation](https://realm.io/docs/objc/latest/#relationships). + */ +@interface RLMLinkingObjects : RLMResults +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMResults_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMResults_Private.h new file mode 100644 index 0000000..f74b4fd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMResults_Private.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +@class RLMObjectSchema; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMResults () +@property (nonatomic, readonly, getter=isAttached) BOOL attached; + ++ (instancetype)emptyDetachedResults; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSchema.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema.h new file mode 100644 index 0000000..2b11792 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema.h @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObjectSchema; + +/** + `RLMSchema` instances represent collections of model object schemas managed by a Realm. + + When using Realm, `RLMSchema` instances allow performing migrations and + introspecting the database's schema. + + Schemas map to collections of tables in the core database. + */ +@interface RLMSchema : NSObject + +#pragma mark - Properties + +/** + An `NSArray` containing `RLMObjectSchema`s for all object types in the Realm. + + This property is intended to be used during migrations for dynamic introspection. + + @see `RLMObjectSchema` + */ +@property (nonatomic, readonly, copy) NSArray *objectSchema; + +#pragma mark - Methods + +/** + Returns an `RLMObjectSchema` for the given class name in the schema. + + @param className The object class name. + @return An `RLMObjectSchema` for the given class in the schema. + + @see `RLMObjectSchema` + */ +- (nullable RLMObjectSchema *)schemaForClassName:(NSString *)className; + +/** + Looks up and returns an `RLMObjectSchema` for the given class name in the Realm. + + If there is no object of type `className` in the schema, an exception will be thrown. + + @param className The object class name. + @return An `RLMObjectSchema` for the given class in this Realm. + + @see `RLMObjectSchema` + */ +- (RLMObjectSchema *)objectForKeyedSubscript:(NSString *)className; + +/** + Returns whether two `RLMSchema` instances are equivalent. + */ +- (BOOL)isEqualToSchema:(RLMSchema *)schema; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.h new file mode 100644 index 0000000..f5823d1 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm; + +// +// RLMSchema private interface +// +@interface RLMSchema () + +/** + Returns an `RLMSchema` containing only the given `RLMObject` subclasses. + + @param classes The classes to be included in the schema. + + @return An `RLMSchema` containing only the given classes. + */ ++ (instancetype)schemaWithObjectClasses:(NSArray *)classes; + +@property (nonatomic, readwrite, copy) NSArray *objectSchema; + +// schema based on runtime objects ++ (instancetype)sharedSchema; + +// schema based upon all currently registered object classes ++ (instancetype)partialSharedSchema; + +// class for string ++ (nullable Class)classForString:(NSString *)className; + ++ (nullable RLMObjectSchema *)sharedSchemaForClass:(Class)cls; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.hpp new file mode 100644 index 0000000..197ddee --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSchema_Private.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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 "RLMSchema_Private.h" + +#import + +namespace realm { + class Schema; + class ObjectSchema; +} + +@interface RLMSchema () ++ (instancetype)dynamicSchemaFromObjectStoreSchema:(realm::Schema const&)objectStoreSchema; +- (realm::Schema)objectStoreCopy; +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h new file mode 100644 index 0000000..4758043 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import + +@interface RLMRealm (Swift) ++ (void)resetRealmState; +@end + +@interface RLMArray (Swift) + +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +@end + +@interface RLMResults (Swift) + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +@end + +@interface RLMObjectBase (Swift) + +- (instancetype)initWithRealm:(RLMRealm *)realm schema:(RLMObjectSchema *)schema defaultValues:(BOOL)useDefaults; + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftSupport.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftSupport.h new file mode 100644 index 0000000..6e45b65 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSwiftSupport.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSwiftSupport : NSObject + ++ (BOOL)isSwiftClassName:(NSString *)className; ++ (NSString *)demangleClassName:(NSString *)className; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration.h new file mode 100644 index 0000000..6181d91 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration.h @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +@class RLMSyncUser; + +NS_ASSUME_NONNULL_BEGIN + +/** + A configuration object representing configuration state for a Realm which is intended to sync with a Realm Object + Server. + */ +@interface RLMSyncConfiguration : NSObject + +/// The user to which the remote Realm belongs. +@property (nonatomic, readonly) RLMSyncUser *user; + +/** + The URL of the remote Realm upon the Realm Object Server. + + @warning The URL cannot end with `.realm`, `.realm.lock` or `.realm.management`. + */ +@property (nonatomic, readonly) NSURL *realmURL; + +/** + Create a sync configuration instance. + + @param user A `RLMSyncUser` that owns the Realm at the given URL. + @param url The unresolved absolute URL to the Realm on the Realm Object Server, e.g. + `realm://example.org/~/path/to/realm`. "Unresolved" means the path should + contain the wildcard marker `~`, which will automatically be filled in with + the user identity by the Realm Object Server. + */ +- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("This type cannot be created directly"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h new file mode 100644 index 0000000..1001ae8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, RLMSyncStopPolicy) { + RLMSyncStopPolicyImmediately, + RLMSyncStopPolicyLiveIndefinitely, + RLMSyncStopPolicyAfterChangesUploaded, +}; + +@interface RLMSyncConfiguration () + +@property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy; + +// Internal-only APIs +@property (nullable, nonatomic) NSURL *customFileURL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp new file mode 100644 index 0000000..1a80e7f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncConfiguration_Private.h" + +namespace realm { +struct SyncConfig; +} + +@interface RLMSyncConfiguration () + +- (instancetype)initWithRawConfig:(realm::SyncConfig)config; + +- (realm::SyncConfig)rawConfiguration; + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncCredentials.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncCredentials.h new file mode 100644 index 0000000..bb19fbe --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncCredentials.h @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil.h" + +NS_ASSUME_NONNULL_BEGIN + +/// A token representing an identity provider's credentials. +typedef NSString *RLMSyncCredentialsToken; + +/// A type representing the unique identifier of a Realm Object Server identity provider. +typedef NSString *RLMIdentityProvider RLM_EXTENSIBLE_STRING_ENUM; + +/// The debug identity provider, which accepts any token string and creates a user associated with that token if one +/// does not yet exist. Not enabled for Realm Object Server configured for production. +extern RLMIdentityProvider const RLMIdentityProviderDebug; + +/// The username/password identity provider. User accounts are handled by the Realm Object Server directly without the +/// involvement of a third-party identity provider. +extern RLMIdentityProvider const RLMIdentityProviderUsernamePassword; + +/// A Facebook account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderFacebook; + +/// A Google account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderGoogle; + +/// A CloudKit account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderCloudKit; + +/** + Opaque credentials representing a specific Realm Object Server user. + */ +@interface RLMSyncCredentials : NSObject + +/// An opaque credentials token containing information that uniquely identifies a Realm Object Server user. +@property (nonatomic, readonly) RLMSyncCredentialsToken token; + +/// The name of the identity provider which generated the credentials token. +@property (nonatomic, readonly) RLMIdentityProvider provider; + +/// A dictionary containing additional pertinent information. In most cases this is automatically configured. +@property (nonatomic, readonly) NSDictionary *userInfo; + +/** + Construct and return credentials from a Facebook account token. + */ ++ (instancetype)credentialsWithFacebookToken:(RLMSyncCredentialsToken)token; + +/** + Construct and return credentials from a Google account token. + */ ++ (instancetype)credentialsWithGoogleToken:(RLMSyncCredentialsToken)token; + +/** + Construct and return credentials from an CloudKit account token. + */ ++ (instancetype)credentialsWithCloudKitToken:(RLMSyncCredentialsToken)token; + +/** + Construct and return credentials from a Realm Object Server username and password. + */ ++ (instancetype)credentialsWithUsername:(NSString *)username + password:(NSString *)password + register:(BOOL)shouldRegister; + +/** + Construct and return special credentials representing a token that can be directly used to open a Realm. The identity + is used to uniquely identify the user across application launches. + */ ++ (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity; + +/** + Construct and return credentials with a custom token string, identity provider string, and optional user info. In most + cases, the convenience initializers should be used instead. + */ +- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)token + provider:(RLMIdentityProvider)provider + userInfo:(nullable NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncCredentials cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncCredentials cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncErrorResponseModel.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncErrorResponseModel.h new file mode 100644 index 0000000..930a7ea --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncErrorResponseModel.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import "RLMSyncUtil_Private.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncErrorResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly, assign) NSInteger status; +@property (nonatomic, readonly, assign) NSInteger code; +@property (nonatomic, readonly, copy) NSString *title; +@property (nonatomic, readonly, copy) NSString *hint; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager.h new file mode 100644 index 0000000..1c7695d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager.h @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil.h" + +@class RLMSyncSession; + +/// An enum representing different levels of sync-related logging that can be configured. +typedef NS_ENUM(NSUInteger, RLMSyncLogLevel) { + /// Nothing will ever be logged. + RLMSyncLogLevelOff, + /// Only fatal errors will be logged. + RLMSyncLogLevelFatal, + /// Only errors will be logged. + RLMSyncLogLevelError, + /// Warnings and errors will be logged. + RLMSyncLogLevelWarn, + /// Information about sync events will be logged. Fewer events will be logged in order to avoid overhead. + RLMSyncLogLevelInfo, + /// Information about sync events will be logged. More events will be logged than with `RLMSyncLogLevelInfo`. + RLMSyncLogLevelDetail, + /// Log information that can aid in debugging. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelDebug, + /// Log information that can aid in debugging. More events will be logged than with `RLMSyncLogLevelDebug`. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelTrace, + /// Log information that can aid in debugging. More events will be logged than with `RLMSyncLogLevelTrace`. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelAll +}; + +NS_ASSUME_NONNULL_BEGIN + +/// A block type representing a block which can be used to report a sync-related error to the application. If the error +/// pertains to a specific session, that session will also be passed into the block. +typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable); + +/** + A singleton manager which serves as a central point for sync-related configuration. + */ +@interface RLMSyncManager : NSObject + +/** + A block which can optionally be set to report sync-related errors to your application. + + Errors reported through this mechanism are fatal, with several exceptions. Please consult + `RLMSyncError` for information about the types of errors that can be reported through + the block, and for for suggestions on handling recoverable error codes. + + @see `RLMSyncError` + */ +@property (nullable, nonatomic, copy) RLMSyncErrorReportingBlock errorHandler; + +/** + A reverse-DNS string uniquely identifying this application. In most cases this is automatically set by the SDK, and + does not have to be explicitly configured. + */ +@property (nonatomic, copy) NSString *appID; + +/** + Whether SSL certificate validation should be disabled. SSL certificate validation is ON by default. Setting this + property after at least one synced Realm or standalone Session has been opened is a no-op. + + @warning NEVER disable certificate validation for clients and servers in production. + */ +@property (nonatomic) BOOL disableSSLValidation; + +/** + The logging threshold which newly opened synced Realms will use. Defaults to + `RLMSyncLogLevelInfo`. + + Logging strings are output to Apple System Logger. + + @warning This property must be set before any synced Realms are opened. Setting it after + opening any synced Realm will do nothing. + */ +@property (nonatomic) RLMSyncLogLevel logLevel; + +/// The sole instance of the singleton. ++ (instancetype)sharedManager NS_REFINED_FOR_SWIFT; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncManager cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncManager cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager_Private.h new file mode 100644 index 0000000..89fb2af --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncManager_Private.h @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil_Private.h" + +typedef NS_ENUM(NSUInteger, RLMSyncSystemErrorKind) { + // Specific + RLMSyncSystemErrorKindClientReset, + // General + RLMSyncSystemErrorKindClient, + RLMSyncSystemErrorKindConnection, + RLMSyncSystemErrorKindSession, + RLMSyncSystemErrorKindUser, + RLMSyncSystemErrorKindUnknown, +}; + +@class RLMSyncUser, RLMSyncConfiguration; + +// All private API methods are threadsafe and synchronized, unless denoted otherwise. Since they are expected to be +// called very infrequently, this should pose no issues. + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncManager () + +@property (nullable, nonatomic, copy) RLMSyncBasicErrorReportingBlock sessionCompletionNotifier; + +- (void)_fireError:(NSError *)error; + +- (void)_fireErrorWithCode:(int)errorCode + message:(NSString *)message + isFatal:(BOOL)fatal + session:(RLMSyncSession *)session + userInfo:(NSDictionary *)userInfo + errorClass:(RLMSyncSystemErrorKind)errorClass; + +- (NSArray *)_allUsers; + ++ (void)resetForTesting; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission.h new file mode 100644 index 0000000..5e3e829 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This model is used to reflect permissions. + + It should be used in conjunction with a `RLMSyncUser`'s Permission Realm. + You can only read this Realm. Use the objects in Management Realm to + make request for modifications of permissions. + + See https://realm.io/docs/realm-object-server/#permissions for general + documentation. + */ +@interface RLMSyncPermission : RLMObject + +/// The date this object was last modified. +@property (readonly) NSDate *updatedAt; + +/// The identity of a user affected by this permission. +@property (readonly) NSString *userId; + +/// The path to the realm. +@property (readonly) NSString *path; + +/// Whether the affected user is allowed to read from the Realm. +@property (readonly) BOOL mayRead; +/// Whether the affected user is allowed to write to the Realm. +@property (readonly) BOOL mayWrite; +/// Whether the affected user is allowed to manage the access rights for others. +@property (readonly) BOOL mayManage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange.h new file mode 100644 index 0000000..8632cf9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange.h @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This model is used for requesting changes to a Realm's permissions. + + It should be used in conjunction with an `RLMSyncUser`'s Management Realm. + + See https://realm.io/docs/realm-object-server/#permissions for general + documentation. + */ +@interface RLMSyncPermissionChange : RLMObject + +/// The globally unique ID string of this permission change object. +@property (readonly) NSString *id; + +/// The date this object was initially created. +@property (readonly) NSDate *createdAt; + +/// The date this object was last modified. +@property (readonly) NSDate *updatedAt; + +/// The status code of the object that was processed by Realm Object Server. +@property (nullable, readonly) NSNumber *statusCode; + +/// An error or informational message, typically written to by the Realm Object Server. +@property (nullable, readonly) NSString *statusMessage; + +/// Sync management object status. +@property (readonly) RLMSyncManagementObjectStatus status; + +/// The remote URL to the realm. +@property (readonly) NSString *realmUrl; + +/// The identity of a user affected by this permission change. +@property (readonly) NSString *userId; + +/// Define read access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. +@property (nullable, readonly) NSNumber *mayRead; +/// Define write access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. +@property (nullable, readonly) NSNumber *mayWrite; +/// Define management access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. +@property (nullable, readonly) NSNumber *mayManage; + +/** + Construct a permission change object used to change the access permissions for a user on a Realm. + + @param realmURL The Realm URL whose permissions settings should be changed. + Use `*` to change the permissions of all Realms managed by the Management Realm's `RLMSyncUser`. + @param userID The user or users who should be granted these permission changes. + Use `*` to change the permissions for all users. + @param mayRead Define read access. Set to `YES` or `NO` to update this value. + Leave unset to preserve the existing setting. + @param mayWrite Define write access. Set to `YES` or `NO` to update this value. + Leave unset to preserve the existing setting. + @param mayManage Define management access. Set to `YES` or `NO` to update this value. + Leave unset to preserve the existing setting. + */ ++ (instancetype)permissionChangeWithRealmURL:(NSString *)realmURL + userID:(NSString *)userID + read:(nullable NSNumber *)mayRead + write:(nullable NSNumber *)mayWrite + manage:(nullable NSNumber *)mayManage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange_Private.h new file mode 100644 index 0000000..df632bc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionChange_Private.h @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionChange.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncPermissionChange() + +@property (readwrite) NSString *id; +@property (readwrite) NSDate *createdAt; +@property (readwrite) NSDate *updatedAt; +@property (nullable, readwrite) NSNumber *statusCode; +@property (nullable, readwrite) NSString *statusMessage; +@property (readwrite) NSString *realmUrl; +@property (readwrite) NSString *userId; + +@property (nullable, readwrite) NSNumber *mayRead; +@property (nullable, readwrite) NSNumber *mayWrite; +@property (nullable, readwrite) NSNumber *mayManage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer.h new file mode 100644 index 0000000..2c1cd41 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This model is used for offering permission changes to other users. + + It should be used in conjunction with an `RLMSyncUser`'s Management Realm. + + See https://realm.io/docs/realm-object-server/#permissions for general + documentation. + */ +@interface RLMSyncPermissionOffer : RLMObject + +/// The globally unique ID string of this permission offer object. +@property (readonly) NSString *id; + +/// The date this object was initially created. +@property (readonly) NSDate *createdAt; + +/// The date this object was last modified. +@property (readonly) NSDate *updatedAt; + +/// The status code of the object that was processed by Realm Object Server. +@property (nullable, readonly) NSNumber *statusCode; + +/// An error or informational message, typically written to by the Realm Object Server. +@property (nullable, readonly) NSString *statusMessage; + +/// Sync management object status. +@property (readonly) RLMSyncManagementObjectStatus status; + +/// A token which uniquely identifies this offer. Generated by the server. +@property (nullable, readonly) NSString *token; + +/// The remote URL to the realm. +@property (readonly) NSString *realmUrl; + +/// Whether this offer allows the receiver to read from the Realm. +@property (readonly) BOOL mayRead; + +/// Whether this offer allows the receiver to write to the Realm. +@property (readonly) BOOL mayWrite; + +/// Whether this offer allows the receiver to manage the access rights for others. +@property (readonly) BOOL mayManage; + +/// When this token will expire and become invalid. +@property (nullable, readonly) NSDate *expiresAt; + +/** + Construct a permission offer object used to offer permission changes to other users. + + @param realmURL The URL to the Realm on which to apply these permission changes + to, once the offer is accepted. + @param expiresAt When this token will expire and become invalid. + Pass `nil` if this offer should not expire. + @param mayRead Grant or revoke read access. + @param mayWrite Grant or revoked read-write access. + @param mayManage Grant or revoke administrative access. + */ ++ (instancetype)permissionOfferWithRealmURL:(NSString *)realmURL + expiresAt:(nullable NSDate *)expiresAt + read:(BOOL)mayRead + write:(BOOL)mayWrite + manage:(BOOL)mayManage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse.h new file mode 100644 index 0000000..46fa199 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse.h @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This model is used to apply permission changes defined in the permission offer + object represented by the specified token, which was created by another user's + `RLMSyncPermissionOffer` object. + + It should be used in conjunction with an `RLMSyncUser`'s Management Realm. + + See https://realm.io/docs/realm-object-server/#permissions for general + documentation. + */ +@interface RLMSyncPermissionOfferResponse : RLMObject + +/// The globally unique ID string of this permission offer response object. +@property (readonly) NSString *id; + +/// The date this object was initially created. +@property (readonly) NSDate *createdAt; + +/// The date this object was last modified. +@property (readonly) NSDate *updatedAt; + +/// The status code of the object that was processed by Realm Object Server. +@property (nullable, readonly) NSNumber *statusCode; + +/// An error or informational message, typically written to by the Realm Object Server. +@property (nullable, readonly) NSString *statusMessage; + +/// Sync management object status. +@property (readonly) RLMSyncManagementObjectStatus status; + +/// The received token which uniquely identifies another user's `RLMSyncPermissionOffer`. +@property (readonly) NSString *token; + +/// The remote URL to the realm on which these permission changes were applied. +/// Generated by the server. +@property (nullable, readonly) NSString *realmUrl; + +/** + Construct a permission offer response object used to apply permission changes + defined in the permission offer object represented by the specified token, + which was created by another user's `RLMSyncPermissionOffer` object. + + @param token The received token which uniquely identifies another user's + `RLMSyncPermissionOffer`. + */ ++ (instancetype)permissionOfferResponseWithToken:(NSString *)token; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h new file mode 100644 index 0000000..88e7809 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionOfferResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncPermissionOfferResponse() + +@property (readwrite) NSString *id; +@property (readwrite) NSDate *createdAt; +@property (readwrite) NSDate *updatedAt; +@property (nullable, readwrite) NSNumber *statusCode; +@property (nullable, readwrite) NSString *statusMessage; + +@property (readwrite) NSString *token; +@property (nullable, readwrite) NSString *realmUrl; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer_Private.h new file mode 100644 index 0000000..3d7b72e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermissionOffer_Private.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermissionOffer.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncPermissionOffer() + +@property (readwrite) NSString *id; +@property (readwrite) NSDate *createdAt; +@property (readwrite) NSDate *updatedAt; +@property (nullable, readwrite) NSNumber *statusCode; +@property (nullable, readwrite) NSString *statusMessage; + +@property (nullable, readwrite) NSString *token; +@property (readwrite) NSString *realmUrl; + +@property (readwrite) BOOL mayRead; +@property (readwrite) BOOL mayWrite; +@property (readwrite) BOOL mayManage; + +@property (nullable, readwrite) NSDate *expiresAt; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission_Private.h new file mode 100644 index 0000000..b4a93df --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncPermission_Private.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncPermission.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncPermission() + +@property (readwrite) NSDate *updatedAt; +@property (readwrite) NSString *userId; +@property (readwrite) NSString *path; + +@property (readwrite) BOOL mayRead; +@property (readwrite) BOOL mayWrite; +@property (readwrite) BOOL mayManage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession.h new file mode 100644 index 0000000..f8c9e9b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession.h @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMRealm.h" + +/** + The current state of the session represented by a session object. + */ +typedef NS_ENUM(NSUInteger, RLMSyncSessionState) { + /// The sync session is bound to the Realm Object Server and communicating with it. + RLMSyncSessionStateActive, + /// The sync session is not currently communicating with the Realm Object Server. + RLMSyncSessionStateInactive, + /// The sync session encountered a fatal error and is permanently invalid; it should be discarded. + RLMSyncSessionStateInvalid +}; + +/** + The transfer direction (upload or download) tracked by a given progress notification block. + + Progress notification blocks can be registered on sessions if your app wishes to be informed + how many bytes have been uploaded or downloaded, for example to show progress indicator UIs. + */ +typedef NS_ENUM(NSUInteger, RLMSyncProgressDirection) { + /// For monitoring upload progress. + RLMSyncProgressDirectionUpload, + /// For monitoring download progress. + RLMSyncProgressDirectionDownload, +}; + +/** + The desired behavior of a progress notification block. + + Progress notification blocks can be registered on sessions if your app wishes to be informed + how many bytes have been uploaded or downloaded, for example to show progress indicator UIs. + */ +typedef NS_ENUM(NSUInteger, RLMSyncProgress) { + /** + The block will be called indefinitely, or until it is unregistered by calling + `-[RLMProgressNotificationToken stop]`. + + Notifications will always report the latest number of transferred bytes, and the + most up-to-date number of total transferrable bytes. + */ + RLMSyncProgressReportIndefinitely, + /** + The block will, upon registration, store the total number of bytes + to be transferred. When invoked, it will always report the most up-to-date number + of transferrable bytes out of that original number of transferrable bytes. + + When the number of transferred bytes reaches or exceeds the + number of transferrable bytes, the block will be unregistered. + */ + RLMSyncProgressForCurrentlyOutstandingWork, +}; + +@class RLMSyncUser, RLMSyncConfiguration; + +/** + The type of a progress notification block intended for reporting a session's network + activity to the user. + + `transferredBytes` refers to the number of bytes that have been uploaded or downloaded. + `transferrableBytes` refers to the total number of bytes transferred, and pending transfer. + */ +typedef void(^RLMProgressNotificationBlock)(NSUInteger transferredBytes, NSUInteger transferrableBytes); + +NS_ASSUME_NONNULL_BEGIN + +/** + A token object corresponding to a progress notification block on a session object. + + To stop notifications manually, call `-stop` on it. Notifications should be stopped before + the token goes out of scope or is destroyed. + */ +@interface RLMProgressNotificationToken : RLMNotificationToken +@end + +/** + An object encapsulating a Realm Object Server "session". Sessions represent the + communication between the client (and a local Realm file on disk), and the server + (and a remote Realm at a given URL stored on a Realm Object Server). + + Sessions are always created by the SDK and vended out through various APIs. The + lifespans of sessions associated with Realms are managed automatically. Session + objects can be accessed from any thread. + */ +@interface RLMSyncSession : NSObject + +/// The session's current state. +@property (nonatomic, readonly) RLMSyncSessionState state; + +/// The Realm Object Server URL of the remote Realm this session corresponds to. +@property (nullable, nonatomic, readonly) NSURL *realmURL; + +/// The user that owns this session. +- (nullable RLMSyncUser *)parentUser; + +/** + If the session is valid, return a sync configuration that can be used to open the Realm + associated with this session. + */ +- (nullable RLMSyncConfiguration *)configuration; + +/** + Register a progress notification block. + + Multiple blocks can be registered with the same session at once. Each block + will be invoked on a side queue devoted to progress notifications. + + If the session has already received progress information from the + synchronization subsystem, the block will be called immediately. Otherwise, it + will be called as soon as progress information becomes available. + + The token returned by this method must be retained as long as progress + notifications are desired, and the `-stop` method should be called on it + when notifications are no longer needed and before the token is destroyed. + + If no token is returned, the notification block will never be called again. + There are a number of reasons this might be true. If the session has previously + experienced a fatal error it will not accept progress notification blocks. If + the block was configured in the `RLMSyncProgressForCurrentlyOutstandingWork` + mode but there is no additional progress to report (for example, the number + of transferrable bytes and transferred bytes are equal), the block will not be + called again. + + @param direction The transfer direction (upload or download) to track in this progress notification block. + @param mode The desired behavior of this progress notification block. + @param block The block to invoke when notifications are available. + + @return A token which must be held for as long as you want notifications to be delivered. + + @see `RLMSyncProgressDirection`, `RLMSyncProgress`, `RLMProgressNotificationBlock`, `RLMProgressNotificationToken` + */ +- (nullable RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction + mode:(RLMSyncProgress)mode + block:(RLMProgressNotificationBlock)block +NS_REFINED_FOR_SWIFT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h new file mode 100644 index 0000000..78a278b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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 + +@class RLMSyncUser; + +/// An object that handles refreshing a session's token periodically, as long +/// as the session remains live and valid. +@interface RLMSyncSessionRefreshHandle : NSObject + +- (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires; +- (void)invalidate; + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp new file mode 100644 index 0000000..fd251b7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncSessionRefreshHandle.h" + +#import "RLMSyncUtil_Private.h" + +#import + +namespace realm { +class SyncSession; +} + +@class RLMSyncUser; + +@interface RLMSyncSessionRefreshHandle () + +NS_ASSUME_NONNULL_BEGIN + +- (instancetype)initWithRealmURL:(NSURL *)realmURL + user:(RLMSyncUser *)user + session:(std::shared_ptr)session + completionBlock:(nullable RLMSyncBasicErrorReportingBlock)completionBlock; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession_Private.hpp new file mode 100644 index 0000000..10a39ea --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncSession_Private.hpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncSession.h" + +#import "RLMSyncUtil_Private.h" +#import + +namespace realm { +class SyncSession; +} + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncSession () { +@public // So it's visible to tests + std::weak_ptr _session; +} RLM_SYNC_UNINITIALIZABLE + +- (instancetype)initWithSyncSession:(std::shared_ptr)session; + +/// Wait for pending uploads to complete or the session to expire, and dispatch the callback onto the specified queue. +- (BOOL)waitForUploadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; + +/// Wait for pending downloads to complete or the session to expire, and dispatch the callback onto the specified queue. +- (BOOL)waitForDownloadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser.h new file mode 100644 index 0000000..185a728 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser.h @@ -0,0 +1,148 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +@class RLMSyncUser, RLMSyncCredentials, RLMSyncSession, RLMRealm; + +/** + The state of the user object. + */ +typedef NS_ENUM(NSUInteger, RLMSyncUserState) { + /// The user is logged out. Call `logInWithCredentials:...` with valid credentials to log the user back in. + RLMSyncUserStateLoggedOut, + /// The user is logged in, and any Realms associated with it are syncing with the Realm Object Server. + RLMSyncUserStateActive, + /// The user has encountered a fatal error state, and cannot be used. + RLMSyncUserStateError, +}; + +/// A block type used for APIs which asynchronously vend an `RLMSyncUser`. +typedef void(^RLMUserCompletionBlock)(RLMSyncUser * _Nullable, NSError * _Nullable); + +NS_ASSUME_NONNULL_BEGIN + +/** + A `RLMSyncUser` instance represents a single Realm Object Server user account. + + A user may have one or more credentials associated with it. These credentials + uniquely identify the user to the authentication provider, and are used to sign + into a Realm Object Server user account. + + Note that user objects are only vended out via SDK APIs, and cannot be directly + initialized. User objects can be accessed from any thread. + */ +@interface RLMSyncUser : NSObject + +/** + A dictionary of all valid, logged-in user identities corresponding to their user objects. + */ ++ (NSDictionary *)allUsers NS_REFINED_FOR_SWIFT; + +/** + The logged-in user. `nil` if none exists. + + @warning Throws an exception if more than one logged-in user exists. + */ ++ (nullable RLMSyncUser *)currentUser NS_REFINED_FOR_SWIFT; + +/** + The unique Realm Object Server user ID string identifying this user. + */ +@property (nullable, nonatomic, readonly) NSString *identity; + +/** + The URL of the authentication server this user will communicate with. + */ +@property (nullable, nonatomic, readonly) NSURL *authenticationServer; + +/** + Whether the user is a Realm Object Server administrator. Value reflects the + state at the time of the last successful login of this user. + */ +@property (nonatomic, readonly) BOOL isAdmin; + +/** + The current state of the user. + */ +@property (nonatomic, readonly) RLMSyncUserState state; + +/** + Create, log in, and asynchronously return a new user object, specifying a custom timeout for the network request. + Credentials identifying the user must be passed in. The user becomes available in the completion block, at which point + it is ready for use. + */ ++ (void)logInWithCredentials:(RLMSyncCredentials *)credentials + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + onCompletion:(RLMUserCompletionBlock)completion NS_REFINED_FOR_SWIFT; + +/** + Create, log in, and asynchronously return a new user object. Credentials identifying the user must be passed in. The + user becomes available in the completion block, at which point it is ready for use. + */ ++ (void)logInWithCredentials:(RLMSyncCredentials *)credentials + authServerURL:(NSURL *)authServerURL + onCompletion:(RLMUserCompletionBlock)completion +NS_SWIFT_UNAVAILABLE("Use the full version of this API."); + +/** + Log a user out, destroying their server state, unregistering them from the SDK, + and removing any synced Realms associated with them from on-disk storage. If + the user is already logged out or in an error state, this method does nothing. + + This method should be called whenever the application is committed to not using a user again unless they are recreated. + Failing to call this method may result in unused files and metadata needlessly taking up space. + */ +- (void)logOut; + +/** + Retrieve a valid session object belonging to this user for a given URL, or `nil` if no such object exists. + */ +- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url; + +/** + Retrieve all the valid sessions belonging to this user. + */ +- (NSArray *)allSessions; + +/** + Returns an instance of the Management Realm owned by the user. + + This Realm can be used to control access permissions for Realms managed by the user. + This includes granting other users access to Realms. + */ +- (RLMRealm *)managementRealmWithError:(NSError **)error NS_REFINED_FOR_SWIFT; + +/** + Returns an instance of the Permission Realm owned by the user. + + This read-only Realm contains `RLMSyncPermission` objects reflecting the + synchronized Realms and permission details this user has access to. + */ +- (RLMRealm *)permissionRealmWithError:(NSError **)error NS_REFINED_FOR_SWIFT; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncUser cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncUser cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser_Private.hpp new file mode 100644 index 0000000..6d9d0da --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUser_Private.hpp @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncUser.h" + +#import "RLMSyncConfiguration.h" +#import "RLMSyncUtil_Private.h" + +#include "sync/sync_config.hpp" +#include "sync/impl/sync_metadata.hpp" + +@class RLMSyncConfiguration; + +using namespace realm; + +typedef void(^RLMFetchedRealmCompletionBlock)(NSError * _Nullable, RLMRealm * _Nullable, BOOL * _Nonnull); + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncUser () + +- (void)_bindSessionWithConfig:(const SyncConfig&)config + session:(std::shared_ptr)session + completion:(RLMSyncBasicErrorReportingBlock)completion; + +- (instancetype)initWithSyncUser:(std::shared_ptr)user; +- (std::shared_ptr)_syncUser; +- (nullable NSString *)_refreshToken; + +- (void)_unregisterRefreshHandleForURLPath:(NSString *)path; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil.h new file mode 100644 index 0000000..71cf8e2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil.h @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +/// A token originating from the Realm Object Server. +typedef NSString* RLMServerToken; + +NS_ASSUME_NONNULL_BEGIN + +/// A user info key for use with `RLMSyncErrorClientResetError`. +extern NSString *const kRLMSyncPathOfRealmBackupCopyKey; + +/// A user info key for use with `RLMSyncErrorClientResetError`. +extern NSString *const kRLMSyncInitiateClientResetBlockKey; + +/// The error domain string for all SDK errors related to synchronization functionality. +extern NSString *const RLMSyncErrorDomain; + +/// An error which is related to authentication to a Realm Object Server. +typedef RLM_ERROR_ENUM(NSInteger, RLMSyncAuthError, RLMSyncErrorDomain) { + /// An error that indicates that the provided credentials are invalid. + RLMSyncAuthErrorInvalidCredential = 611, + + /// An error that indicates that the user with provided credentials does not exist. + RLMSyncAuthErrorUserDoesNotExist = 612, + + /// An error that indicates that the user cannot be registered as it exists already. + RLMSyncAuthErrorUserAlreadyExists = 613, +}; + +/// An error which is related to synchronization with a Realm Object Server. +typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) { + /// An error that indicates that the response received from the authentication server was malformed. + RLMSyncErrorBadResponse = 1, + + /// An error that indicates that the supplied Realm path was invalid, or could not be resolved by the authentication + /// server. + RLMSyncErrorBadRemoteRealmPath = 2, + + /// An error that indicates that the response received from the authentication server was an HTTP error code. The + /// `userInfo` dictionary contains the actual error code value. + RLMSyncErrorHTTPStatusCodeError = 3, + + /// An error that indicates a problem with the session (a specific Realm opened for sync). + RLMSyncErrorClientSessionError = 4, + + /// An error that indicates a problem with a specific user. + RLMSyncErrorClientUserError = 5, + + /// An error that indicates an internal, unrecoverable error with the underlying synchronization engine. + RLMSyncErrorClientInternalError = 6, + + /** + An error that indicates the Realm needs to be reset. + + A synced Realm may need to be reset because the Realm Object Server encountered an + error and had to be restored from a backup. If the backup copy of the remote Realm + is of an earlier version than the local copy of the Realm, the server will ask the + client to reset the Realm. + + The reset process is as follows: the local copy of the Realm is copied into a recovery + directory for safekeeping, and then deleted from the original location. The next time + the Realm for that URL is opened, the Realm will automatically be re-downloaded from the + Realm Object Server, and can be used as normal. + + Data written to the Realm after the local copy of the Realm diverged from the backup + remote copy will be present in the local recovery copy of the Realm file. The + re-downloaded Realm will initially contain only the data present at the time the Realm + was backed up on the server. + + The client reset process can be initiated in one of two ways. The block provided in the + `userInfo` dictionary under `kRLMSyncInitiateClientResetBlockKey` can be called to + initiate the reset process. This block can be called any time after the error is + received, but should only be called if and when your app closes and invalidates every + instance of the offending Realm on all threads (note that autorelease pools may make this + difficult to guarantee). + + If the block is not called, the client reset process will be automatically carried out + the next time the app is launched and the `RLMSyncManager` singleton is accessed. + + The value for the `kRLMSyncPathOfRealmBackupCopyKey` key in the `userInfo` dictionary + describes the path of the recovered copy of the Realm. This copy will not actually be + created until the client reset process is initiated. + + @see: `-[NSError rlmSync_clientResetBlock]`, `-[NSError rlmSync_clientResetBackedUpRealmPath]` + */ + RLMSyncErrorClientResetError = 7, +}; + +/// An enum representing the different states a sync management object can take. +typedef NS_ENUM(NSUInteger, RLMSyncManagementObjectStatus) { + /// The management object has not yet been processed by the object server. + RLMSyncManagementObjectStatusNotProcessed, + /// The operations encoded in the management object have been successfully + /// performed by the object server. + RLMSyncManagementObjectStatusSuccess, + /** + The operations encoded in the management object were not successfully + performed by the object server. + Refer to the `statusCode` and `statusMessage` properties for more details + about the error. + */ + RLMSyncManagementObjectStatusError, +}; + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.h b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.h new file mode 100644 index 0000000..ded4645 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.h @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import +#import +#import + +@class RLMSyncUser; + +typedef void(^RLMSyncCompletionBlock)(NSError * _Nullable, NSDictionary * _Nullable); +typedef void(^RLMSyncBasicErrorReportingBlock)(NSError * _Nullable); + +typedef NSString* RLMServerPath; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMRealmConfiguration (RealmSync) ++ (instancetype)managementConfigurationForUser:(RLMSyncUser *)user; ++ (instancetype)permissionConfigurationForUser:(RLMSyncUser *)user; +@end + +extern RLMIdentityProvider const RLMIdentityProviderAccessToken; +extern RLMIdentityProvider const RLMIdentityProviderRealm; + +extern NSString *const kRLMSyncAppIDKey; +extern NSString *const kRLMSyncDataKey; +extern NSString *const kRLMSyncErrorJSONKey; +extern NSString *const kRLMSyncErrorStatusCodeKey; +extern NSString *const kRLMSyncIdentityKey; +extern NSString *const kRLMSyncPasswordKey; +extern NSString *const kRLMSyncPathKey; +extern NSString *const kRLMSyncProviderKey; +extern NSString *const kRLMSyncRegisterKey; +extern NSString *const kRLMSyncUnderlyingErrorKey; + +/// Convert sync management object status code (nil, 0 and others) to +/// RLMSyncManagementObjectStatus enum +FOUNDATION_EXTERN RLMSyncManagementObjectStatus RLMMakeSyncManagementObjectStatus(NSNumber * _Nullable statusCode); + +#define RLM_SYNC_UNINITIALIZABLE \ +- (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); \ ++ (instancetype)new __attribute__((unavailable("This type cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +/// A macro to parse a string out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_STRING_OR_ABORT(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSString class]]) { return nil; } \ +self.prop_macro_val = data; \ +} \ + +#define RLM_SYNC_PARSE_OPTIONAL_STRING(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSString class]]) { data = nil; } \ +self.prop_macro_val = data; \ +} \ + +#define RLM_SYNC_PARSE_OPTIONAL_BOOL(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSNumber class]]) { data = @NO; } \ +self.prop_macro_val = [data boolValue]; \ +} \ + +/// A macro to parse a double out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_DOUBLE_OR_ABORT(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSNumber class]]) { return nil; } \ +self.prop_macro_val = [data doubleValue]; \ +} \ + +/// A macro to build a sub-model out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_MODEL_OR_ABORT(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ +{ \ +id raw = json_macro_val[key_macro_val]; \ +if (![raw isKindOfClass:[NSDictionary class]]) { return nil; } \ +id model = [[class_macro_val alloc] initWithDictionary:raw]; \ +if (!model) { return nil; } \ +self.prop_macro_val = model; \ +} \ + +#define RLM_SYNC_PARSE_OPTIONAL_MODEL(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ +{ \ +id model; \ +id raw = json_macro_val[key_macro_val]; \ +if (![raw isKindOfClass:[NSDictionary class]]) { model = nil; } \ +else { model = [[class_macro_val alloc] initWithDictionary:raw]; } \ +self.prop_macro_val = model; \ +} \ diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp new file mode 100644 index 0000000..bf4a99c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMSyncUtil_Private.h" + +#import "RLMSyncConfiguration_Private.h" + +#import "sync/sync_manager.hpp" + +namespace realm { + +SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy); +RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy); + +std::shared_ptr sync_session_for_realm(RLMRealm *realm); + +} diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference.h b/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference.h new file mode 100644 index 0000000..a935ebe --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference.h @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +@class RLMRealm; + +NS_ASSUME_NONNULL_BEGIN + +/** + Objects of types which conform to `RLMThreadConfined` can be managed by a Realm, which will make + them bound to a thread-specific `RLMRealm` instance. Managed objects must be explicitly exported + and imported to be passed between threads. + + Managed instances of objects conforming to this protocol can be converted to a thread-safe + reference for transport between threads by passing to the + `+[RLMThreadSafeReference referenceWithThreadConfined:]` constructor. + + Note that only types defined by Realm can meaningfully conform to this protocol, and defining new + classes which attempt to conform to it will not make them work with `RLMThreadSafeReference`. + */ +@protocol RLMThreadConfined +// Conformance to the `RLMThreadConfined_Private` protocol will be enforced at runtime. + +/** + The Realm which manages the object, or `nil` if the object is unmanaged. + + Unmanaged objects are not confined to a thread and cannot be passed to methods expecting a + `RLMThreadConfined` object. + */ +@property (nonatomic, readonly, nullable) RLMRealm *realm; + +/// Indicates if the object can no longer be accessed because it is now invalid. +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +@end + +/** + An object intended to be passed between threads containing a thread-safe reference to its + thread-confined object. + + To resolve a thread-safe reference on a target Realm on a different thread, pass to + `-[RLMRealm resolveThreadSafeReference:]`. + + @warning A `RLMThreadSafeReference` object must be resolved at most once. + Failing to resolve a `RLMThreadSafeReference` will result in the source version of the + Realm being pinned until the reference is deallocated. + + @note Prefer short-lived `RLMThreadSafeReference`s as the data for the version of the source Realm + will be retained until all references have been resolved or deallocated. + + @see `RLMThreadConfined` + @see `-[RLMRealm resolveThreadSafeReference:]` + */ +@interface RLMThreadSafeReference<__covariant Confined : id> : NSObject + +/** + Create a thread-safe reference to the thread-confined object. + + @param threadConfined The thread-confined object to create a thread-safe reference to. + + @note You may continue to use and access the thread-confined object after passing it to this + constructor. + */ ++ (instancetype)referenceWithThreadConfined:(Confined)threadConfined; + +/** + Indicates if the reference can no longer be resolved because an attempt to resolve it has already + occurred. References can only be resolved once. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +#pragma mark - Unavailable Methods + +/** + `-[RLMThreadSafeReference init]` is not available because `RLMThreadSafeReference` cannot be + created directly. `RLMThreadSafeReference` instances must be obtained by calling + `-[RLMRealm resolveThreadSafeReference:]`. + */ +- (instancetype)init __attribute__((unavailable("RLMThreadSafeReference cannot be created directly"))); + +/** + `-[RLMThreadSafeReference new]` is not available because `RLMThreadSafeReference` cannot be + created directly. `RLMThreadSafeReference` instances must be obtained by calling + `-[RLMRealm resolveThreadSafeReference:]`. + */ ++ (instancetype)new __attribute__((unavailable("RLMThreadSafeReference cannot be created directly"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp new file mode 100644 index 0000000..5cf78ed --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 "RLMThreadSafeReference.h" +#import "thread_safe_reference.hpp" + +NS_ASSUME_NONNULL_BEGIN + +@protocol RLMThreadConfined_Private + +// Constructs a new `ThreadSafeReference` +- (std::unique_ptr)makeThreadSafeReference; + +// The extra information needed to construct an instance of this type from the Object Store type +@property (nonatomic, readonly, nullable) id objectiveCMetadata; + +// Constructs an new instance of this type ++ (nullable instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference + metadata:(nullable id)metadata + realm:(RLMRealm *)realm; +@end + +@interface RLMThreadSafeReference () + +- (nullable id)resolveReferenceInRealm:(RLMRealm *)realm; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMTokenModels.h b/Desafio Mobile iOS/Pods/Realm/include/RLMTokenModels.h new file mode 100644 index 0000000..cfd0a5d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMTokenModels.h @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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 + +#import "RLMSyncUtil_Private.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RLMTokenDataModel; + +@interface RLMTokenModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *token; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) RLMTokenDataModel *tokenData; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +@interface RLMTokenDataModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *identity; +@property (nonatomic, nullable, readonly) NSString *appID; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) NSTimeInterval expires; +@property (nonatomic, readonly) BOOL isAdmin; +//@property (nonatomic, readonly) NSArray *access; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMUpdateChecker.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMUpdateChecker.hpp new file mode 100644 index 0000000..7f01ac7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMUpdateChecker.hpp @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously check for updates to Realm if running on a simulator +void RLMCheckForUpdates(); diff --git a/Desafio Mobile iOS/Pods/Realm/include/RLMUtil.hpp b/Desafio Mobile iOS/Pods/Realm/include/RLMUtil.hpp new file mode 100644 index 0000000..d108b7a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/RLMUtil.hpp @@ -0,0 +1,177 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 +#import +#import + +#import +#import +#import +#import +#import + +namespace realm { + class Mixed; +} + +@class RLMObjectSchema; +@class RLMProperty; + +namespace realm { + class RealmFileException; +} + +__attribute__((format(NSString, 1, 2))) +NSException *RLMException(NSString *fmt, ...); +NSException *RLMException(std::exception const& exception); + +NSError *RLMMakeError(RLMError code, std::exception const& exception); +NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError&); +NSError *RLMMakeError(RLMError code, const realm::RealmFileException&); +NSError *RLMMakeError(std::system_error const& exception); +NSError *RLMMakeError(NSException *exception); + +void RLMSetErrorOrThrow(NSError *error, NSError **outError); + +// returns if the object can be inserted as the given type +BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop); +// throw an exception if the object is not a valid value for the property +void RLMValidateValueForProperty(id obj, RLMProperty *prop); + +// gets default values for the given schema (+defaultPropertyValues) +// merges with native property defaults if Swift class +NSDictionary *RLMDefaultValuesForObjectSchema(RLMObjectSchema *objectSchema); + +BOOL RLMIsDebuggerAttached(); +BOOL RLMIsRunningInPlayground(); + +// C version of isKindOfClass +static inline BOOL RLMIsKindOfClass(Class class1, Class class2) { + while (class1) { + if (class1 == class2) return YES; + class1 = class_getSuperclass(class1); + } + return NO; +} + +// Returns whether the class is a descendent of RLMObjectBase +BOOL RLMIsObjectOrSubclass(Class klass); + +// Returns whether the class is an indirect descendant of RLMObjectBase +BOOL RLMIsObjectSubclass(Class klass); + +template +static inline T *RLMDynamicCast(__unsafe_unretained id obj) { + if ([obj isKindOfClass:[T class]]) { + return obj; + } + return nil; +} + +template +static inline T RLMCoerceToNil(__unsafe_unretained T obj) { + if (static_cast(obj) == NSNull.null) { + return nil; + } + else if (__unsafe_unretained auto optional = RLMDynamicCast(obj)) { + return RLMCoerceToNil(optional.underlyingValue); + } + return obj; +} + +// String conversion utilities +static inline NSString * RLMStringDataToNSString(realm::StringData stringData) { + static_assert(sizeof(NSUInteger) >= sizeof(size_t), + "Need runtime overflow check for size_t to NSUInteger conversion"); + if (stringData.is_null()) { + return nil; + } + else { + return [[NSString alloc] initWithBytes:stringData.data() + length:stringData.size() + encoding:NSUTF8StringEncoding]; + } +} + +static inline realm::StringData RLMStringDataWithNSString(__unsafe_unretained NSString *const string) { + static_assert(sizeof(size_t) >= sizeof(NSUInteger), + "Need runtime overflow check for NSUInteger to size_t conversion"); + return realm::StringData(string.UTF8String, + [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); +} + +// Binary conversion utilities +static inline NSData *RLMBinaryDataToNSData(realm::BinaryData binaryData) { + return binaryData ? [NSData dataWithBytes:binaryData.data() length:binaryData.size()] : nil; +} + +static inline realm::BinaryData RLMBinaryDataForNSData(__unsafe_unretained NSData *const data) { + // this is necessary to ensure that the empty NSData isn't treated by core as the null realm::BinaryData + // because data.bytes == 0 when data.length == 0 + // the casting bit ensures that we create a data with a non-null pointer + auto bytes = static_cast(data.bytes) ?: static_cast((__bridge void *)data); + return realm::BinaryData(bytes, data.length); +} + +// Date conversion utilities +// These use the reference date and shift the seconds rather than just getting +// the time interval since the epoch directly to avoid losing sub-second precision +static inline NSDate *RLMTimestampToNSDate(realm::Timestamp ts) NS_RETURNS_RETAINED { + if (ts.is_null()) + return nil; + auto timeInterval = ts.get_seconds() - NSTimeIntervalSince1970 + ts.get_nanoseconds() / 1'000'000'000.0; + return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:timeInterval]; +} + +static inline realm::Timestamp RLMTimestampForNSDate(__unsafe_unretained NSDate *const date) { + auto timeInterval = date.timeIntervalSinceReferenceDate; + if (isnan(timeInterval)) + return {0, 0}; // Arbitrary choice + + // Clamp dates that we can't represent as a Timestamp to the maximum value + if (timeInterval >= std::numeric_limits::max() - NSTimeIntervalSince1970) + return {std::numeric_limits::max(), 1'000'000'000 - 1}; + if (timeInterval - NSTimeIntervalSince1970 < std::numeric_limits::min()) + return {std::numeric_limits::min(), -1'000'000'000 + 1}; + + auto seconds = static_cast(timeInterval); + auto nanoseconds = static_cast((timeInterval - seconds) * 1'000'000'000.0); + seconds += static_cast(NSTimeIntervalSince1970); + + // Seconds and nanoseconds have to have the same sign + if (nanoseconds < 0 && seconds > 0) { + nanoseconds += 1'000'000'000; + --seconds; + } + return {seconds, nanoseconds}; +} + +static inline NSUInteger RLMConvertNotFound(size_t index) { + return index == realm::not_found ? NSNotFound : index; +} + +id RLMMixedToObjc(realm::Mixed const& value); + +// For unit testing purposes, allow an Objective-C class named FakeObject to also be used +// as the base class of managed objects. This allows for testing invalid schemas. +void RLMSetTreatFakeObjectAsRLMObject(BOOL flag); + +// Given a bundle identifier, return the base directory on the disk within which Realm database and support files should +// be stored. +NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier); diff --git a/Desafio Mobile iOS/Pods/Realm/include/Realm.h b/Desafio Mobile iOS/Pods/Realm/include/Realm.h new file mode 100644 index 0000000..33f3d41 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/Realm.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// 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 + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Desafio Mobile iOS/Pods/Realm/include/binding_callback_thread_observer.hpp b/Desafio Mobile iOS/Pods/Realm/include/binding_callback_thread_observer.hpp new file mode 100644 index 0000000..72ccba4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/binding_callback_thread_observer.hpp @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP +#define REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP + +namespace realm { +// Interface for bindings interested in registering callbacks before/after the ObjectStore thread runs. +// This is for example helpful to attach/detach the pthread to the JavaVM in order to be able to perform JNI calls. +class BindingCallbackThreadObserver { +public: + // This method is called just before the thread is started + virtual void did_create_thread() = 0; + + // This method is called just before the thread is being destroyed + virtual void will_destroy_thread() = 0; +}; + +extern BindingCallbackThreadObserver* g_binding_callback_thread_observer; +} + +#endif // REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/binding_context.hpp b/Desafio Mobile iOS/Pods/Realm/include/binding_context.hpp new file mode 100644 index 0000000..5fc59eb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/binding_context.hpp @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef BINDING_CONTEXT_HPP +#define BINDING_CONTEXT_HPP + +#include "index_set.hpp" + +#include +#include +#include + +namespace realm { +// BindingContext is the extension point for adding binding-specific behavior to +// a SharedRealm. It can be used to store additonal data associated with the +// Realm which is needed by the binding, and there are several methods which +// can be overridden to receive notifications of state changes within the Realm. +// +// A simple implementation which lets the user register functions to be +// called on refresh could look like the following: +// +// class BindingContextImplementation : public BindingContext { +// public: +// // A token returned from add_notification that can be used to remove the +// // notification later +// struct token : private std::list>::iterator { +// token(std::list>::iterator it) : std::list>::iterator(it) { } +// friend class DelegateImplementation; +// }; +// +// token add_notification(std::function func) +// { +// m_registered_notifications.push_back(std::move(func)); +// return token(std::prev(m_registered_notifications.end())); +// } +// +// void remove_notification(token entry) +// { +// m_registered_notifications.erase(entry); +// } +// +// // Override the did_change method to call each registered notification +// void did_change(std::vector const&, std::vector const&, bool) override +// { +// // Loop oddly so that unregistering a notification from within the +// // registered function works +// for (auto it = m_registered_notifications.begin(); it != m_registered_notifications.end(); ) { +// (*it++)(); +// } +// } +// +// private: +// std::list> m_registered_notifications; +// }; +class Realm; +class BindingContext { +public: + virtual ~BindingContext() = default; + + std::weak_ptr realm; + + // If the user adds a notification handler to the Realm, will it ever + // actually be called? + virtual bool can_deliver_notifications() const noexcept { return true; } + + // Called by the Realm when refresh called or a notification arrives which + // is triggered through write transaction committed by itself or a different + // Realm instance. + virtual void before_notify() { } + + // Called by the Realm when a write transaction is committed to the file by + // a different Realm instance (possibly in a different process) + virtual void changes_available() { } + + struct ObserverState; + + // Override this function if you want to receive detailed information about + // external changes to a specific set of objects. + // This is called before each operation which may advance the read + // transaction to include + // ObserverStates for each row for which detailed change information is + // desired. + virtual std::vector get_observed_rows() { return {}; } + + // Called immediately before the read transaction is advanced if detailed + // change information was requested (by returning a non-empty array from + // get_observed_rows()). + // The observers vector is the vector returned by get_observed_row(), + // updated with change information. The invalidated vector is a list of the + // `info` fields of observed rows which will be deleted. + virtual void will_change(std::vector const& observers, + std::vector const& invalidated); + + // Called immediately after the read transaction version is advanced. Unlike + // will_change(), this is called even if detailed change information was not + // requested or if the Realm is not actually in a read transaction, although + // both vectors will be empty in that case. + virtual void did_change(std::vector const& observers, + std::vector const& invalidated, + bool version_changed=true); + + // Change information for a single field of a row + struct ColumnInfo { + // The index of this column prior to the changes in the tracked + // transaction, or -1 for newly inserted columns. + size_t initial_column_index = -1; + // What kind of change occurred? + // Always Set or None for everything but LinkList columns. + enum class Kind { + None, // No change + Set, // The value or entries at `indices` were assigned to + Insert, // New values were inserted at each of the indices given + Remove, // Values were removed at each of the indices given + SetAll // The entire LinkList has been replaced with a new set of values + } kind = Kind::None; + // The indices where things happened for Set, Insert and Remove on + // LinkList columns. Not used for other types or for None or SetAll. + IndexSet indices; + }; + + // Information about an observed row in a table + // + // Each object which needs detailed change information should have an + // ObserverState entry in the vector returned from get_observed_rows(), with + // the initial table and row indexes set (and optionally the info field). + // The Realm parses the transaction log, and populates the `changes` vector + // in each ObserverState with information about what changes were made. + struct ObserverState { + // Initial table and row which is observed + // May be updated by row insertions and removals + size_t table_ndx; + size_t row_ndx; + + // Opaque userdata for the delegate's use + void* info; + + // Populated with information about which columns were changed + // May be shorter than the actual number of columns if the later columns + // are not modified + std::vector changes; + + // Simple lexographic ordering + friend bool operator<(ObserverState const& lft, ObserverState const& rgt) + { + return std::tie(lft.table_ndx, lft.row_ndx) < std::tie(rgt.table_ndx, rgt.row_ndx); + } + }; +}; + +inline void BindingContext::will_change(std::vector const&, std::vector const&) { } +inline void BindingContext::did_change(std::vector const&, std::vector const&, bool) { } +} // namespace realm + +#endif /* BINDING_CONTEXT_HPP */ diff --git a/Desafio Mobile iOS/Pods/Realm/include/collection_notifications.hpp b/Desafio Mobile iOS/Pods/Realm/include/collection_notifications.hpp new file mode 100644 index 0000000..31ad922 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/collection_notifications.hpp @@ -0,0 +1,181 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_COLLECTION_NOTIFICATIONS_HPP +#define REALM_COLLECTION_NOTIFICATIONS_HPP + +#include "index_set.hpp" +#include "util/atomic_shared_ptr.hpp" + +#include +#include +#include +#include + +namespace realm { +namespace _impl { + class CollectionNotifier; +} + +// A token which keeps an asynchronous query alive +struct NotificationToken { + NotificationToken() = default; + NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token); + ~NotificationToken(); + + NotificationToken(NotificationToken&&); + NotificationToken& operator=(NotificationToken&&); + + NotificationToken(NotificationToken const&) = delete; + NotificationToken& operator=(NotificationToken const&) = delete; + + void suppress_next(); + +private: + util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier; + size_t m_token; +}; + +struct CollectionChangeSet { + struct Move { + size_t from; + size_t to; + + bool operator==(Move m) const noexcept { return from == m.from && to == m.to; } + }; + + // Indices which were removed from the _old_ collection + IndexSet deletions; + + // Indices in the _new_ collection which are new insertions + IndexSet insertions; + + // Indices of objects in the _old_ collection which were modified + IndexSet modifications; + + // Indices in the _new_ collection which were modified. This will always + // have the same number of indices as `modifications` and conceptually + // represents the same entries, just in different versions of the collection. + // It exists for the sake of code which finds it easier to process + // modifications after processing deletions and insertions rather than before. + IndexSet modifications_new; + + // Rows in the collection which moved. + // + // Every `from` index will also be present in `deletions` and every `to` + // index will be present in `insertions`. + // + // This is currently not reliably calculated for all types of collections. A + // reported move will always actually be a move, but there may also be + // unreported moves which show up only as a delete/insert pair. + std::vector moves; + + // Per-column version of `modifications` + std::vector columns; + + bool empty() const noexcept + { + return deletions.empty() && insertions.empty() && modifications.empty() + && modifications_new.empty() && moves.empty(); + } +}; + +// A type-erasing wrapper for the callback for collection notifications. Can be +// constructed with either any callable compatible with the signature +// `void (CollectionChangeSet, std::exception_ptr)`, an object with member +// functions `void before(CollectionChangeSet)`, `void after(CollectionChangeSet)`, +// `void error(std::exception_ptr)`, or a pointer to such an object. If a pointer +// is given, the caller is responsible for ensuring that the pointed-to object +// outlives the collection. +class CollectionChangeCallback { +public: + CollectionChangeCallback(std::nullptr_t={}) { } + + template + CollectionChangeCallback(Callback cb) : m_impl(make_impl(std::move(cb))) { } + template + CollectionChangeCallback& operator=(Callback cb) { m_impl = make_impl(std::move(cb)); return *this; } + + // Explicitly default the copy/move constructors as otherwise they'll use + // the above ones and add an extra layer of wrapping + CollectionChangeCallback(CollectionChangeCallback&&) = default; + CollectionChangeCallback(CollectionChangeCallback const&) = default; + CollectionChangeCallback& operator=(CollectionChangeCallback&&) = default; + CollectionChangeCallback& operator=(CollectionChangeCallback const&) = default; + + void before(CollectionChangeSet const& c) { m_impl->before(c); } + void after(CollectionChangeSet const& c) { m_impl->after(c); } + void error(std::exception_ptr e) { m_impl->error(e); } + + explicit operator bool() const { return !!m_impl; } + +private: + struct Base { + virtual void before(CollectionChangeSet const&)=0; + virtual void after(CollectionChangeSet const&)=0; + virtual void error(std::exception_ptr)=0; + }; + + template()(CollectionChangeSet(), std::exception_ptr()))> + std::shared_ptr make_impl(Callback cb) + { + return std::make_shared>(std::move(cb)); + } + + template().after(CollectionChangeSet())), typename = void> + std::shared_ptr make_impl(Callback cb) + { + return std::make_shared>(std::move(cb)); + } + + template().after(CollectionChangeSet())), typename = void> + std::shared_ptr make_impl(Callback* cb) + { + return std::make_shared>(cb); + } + + template + struct Impl : public Base { + T impl; + Impl(T impl) : impl(std::move(impl)) { } + void before(CollectionChangeSet const&) override { } + void after(CollectionChangeSet const& change) override { impl(change, {}); } + void error(std::exception_ptr error) override { impl({}, error); } + }; + template + struct Impl2 : public Base { + T impl; + Impl2(T impl) : impl(std::move(impl)) { } + void before(CollectionChangeSet const& c) override { impl.before(c); } + void after(CollectionChangeSet const& c) override { impl.after(c); } + void error(std::exception_ptr error) override { impl.error(error); } + }; + template + struct Impl3 : public Base { + T* impl; + Impl3(T* impl) : impl(impl) { } + void before(CollectionChangeSet const& c) override { impl->before(c); } + void after(CollectionChangeSet const& c) override { impl->after(c); } + void error(std::exception_ptr error) override { impl->error(error); } + }; + + std::shared_ptr m_impl; +}; +} // namespace realm + +#endif // REALM_COLLECTION_NOTIFICATIONS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm.hpp new file mode 100644 index 0000000..4a8c692 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm.hpp @@ -0,0 +1,29 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_HPP +#define REALM_HPP + +#include +#include +#include +#include +#include +#include + +#endif // REALM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc.hpp new file mode 100644 index 0000000..5dc2597 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc.hpp @@ -0,0 +1,458 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ALLOC_HPP +#define REALM_ALLOC_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { + +class Allocator; + +class Replication; + +using ref_type = size_t; + +int_fast64_t from_ref(ref_type) noexcept; +ref_type to_ref(int_fast64_t) noexcept; +int64_t to_int64(size_t value) noexcept; + +class MemRef { +public: + MemRef() noexcept; + ~MemRef() noexcept; + + MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept; + MemRef(ref_type ref, Allocator& alloc) noexcept; + + char* get_addr(); + ref_type get_ref(); + void set_ref(ref_type ref); + void set_addr(char* addr); + +private: + char* m_addr; + ref_type m_ref; +#if REALM_ENABLE_MEMDEBUG + // Allocator that created m_ref. Used to verify that the ref is valid whenever you call + // get_ref()/get_addr and that it e.g. has not been free'ed + const Allocator* m_alloc = nullptr; +#endif +}; + + +/// The common interface for Realm allocators. +/// +/// A Realm allocator must associate a 'ref' to each allocated +/// object and be able to efficiently map any 'ref' to the +/// corresponding memory address. The 'ref' is an integer and it must +/// always be divisible by 8. Also, a value of zero is used to +/// indicate a null-reference, and must therefore never be returned by +/// Allocator::alloc(). +/// +/// The purpose of the 'refs' is to decouple the memory reference from +/// the actual address and thereby allowing objects to be relocated in +/// memory without having to modify stored references. +/// +/// \sa SlabAlloc +class Allocator { +public: + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// \throw std::bad_alloc If insufficient memory was available. + MemRef alloc(size_t size); + + /// Calls do_realloc(). + /// + /// Note: The underscore has been added because the name `realloc` + /// would conflict with a macro on the Windows platform. + MemRef realloc_(ref_type, const char* addr, size_t old_size, size_t new_size); + + /// Calls do_free(). + /// + /// Note: The underscore has been added because the name `free + /// would conflict with a macro on the Windows platform. + void free_(ref_type, const char* addr) noexcept; + + /// Shorthand for free_(mem.get_ref(), mem.get_addr()). + void free_(MemRef mem) noexcept; + + /// Calls do_translate(). + char* translate(ref_type ref) const noexcept; + + /// Returns true if, and only if the object at the specified 'ref' + /// is in the immutable part of the memory managed by this + /// allocator. The method by which some objects become part of the + /// immuatble part is entirely up to the class that implements + /// this interface. + bool is_read_only(ref_type) const noexcept; + + /// Returns a simple allocator that can be used with free-standing + /// Realm objects (such as a free-standing table). A + /// free-standing object is one that is not part of a Group, and + /// therefore, is not part of an actual database. + static Allocator& get_default() noexcept; + + virtual ~Allocator() noexcept; + + // Disable copying. Copying an allocator can produce double frees. + Allocator(const Allocator&) = delete; + Allocator& operator=(const Allocator&) = delete; + + virtual void verify() const = 0; + +#ifdef REALM_DEBUG + /// Terminate the program precisely when the specified 'ref' is + /// freed (or reallocated). You can use this to detect whether the + /// ref is freed (or reallocated), and even to get a stacktrace at + /// the point where it happens. Call watch(0) to stop watching + /// that ref. + void watch(ref_type ref) + { + m_debug_watch = ref; + } +#endif + + Replication* get_replication() noexcept; + + /// \brief The version of the format of the node structure (in file or in + /// memory) in use by Realm objects associated with this allocator. + /// + /// Every allocator contains a file format version field, which is returned + /// by this function. In some cases (as mentioned below) the file format can + /// change. + /// + /// A value of zero means that the file format is not yet decided. This is + /// only possible for empty Realms where top-ref is zero. + /// + /// For the default allocator (get_default()), the file format version field + /// can never change, is never zero, and is set to whatever + /// Group::get_target_file_format_version_for_session() would return if the + /// original file format version was undecided and the request history type + /// was Replication::hist_None. + /// + /// For the slab allocator (AllocSlab), the file format version field is set + /// to the file format version specified by the attached file (or attached + /// memory buffer) at the time of attachment. If no file (or buffer) is + /// currently attached, the returned value has no meaning. If the Realm file + /// format is later upgraded, the file format version filed must be updated + /// to reflect that fact. + /// + /// In shared mode (when a Realm file is opened via a SharedGroup instance) + /// it can happen that the file format is upgraded asyncronously (via + /// another SharedGroup instance), and in that case the file format version + /// field of the allocator can get out of date, but only for a short + /// while. It is always garanteed to be, and remain up to date after the + /// opening process completes (when SharedGroup::do_open() returns). + /// + /// An empty Realm file (one whose top-ref is zero) may specify a file + /// format version of zero to indicate that the format is not yet + /// decided. In that case, this function will return zero immediately after + /// AllocSlab::attach_file() returns. It shall be guaranteed, however, that + /// the zero is changed to a proper file format version before the opening + /// process completes (Group::open() or SharedGroup::open()). It is the duty + /// of the caller of AllocSlab::attach_file() to ensure this. + /// + /// File format versions: + /// + /// 1 Initial file format version + /// + /// 2 Various changes. + /// + /// 3 Supporting null on string columns broke the file format in following + /// way: Index appends an 'X' character to all strings except the null + /// string, to be able to distinguish between null and empty + /// string. Bumped to 3 because of null support of String columns and + /// because of new format of index. + /// + /// 4 Introduction of optional in-Realm history of changes (additional + /// entries in Group::m_top). Since this change is not forward + /// compatible, the file format version had to be bumped. This change is + /// implemented in a way that achieves backwards compatibility with + /// version 3 (and in turn with version 2). + /// + /// 5 Introduced the new Timestamp column type that replaces DateTime. + /// When opening an older database file, all DateTime columns will be + /// automatically upgraded Timestamp columns. + /// + /// 6 Introduced a new structure for the StringIndex. Moved the commit + /// logs into the Realm file. Changes to the transaction log format + /// including reshuffling instructions. This is the format used in + /// milestone 2.0.0. + /// + /// 7 Introduced "history schema version" as 10th entry in top array. + /// + /// IMPORTANT: When introducing a new file format version, be sure to review + /// the file validity checks in AllocSlab::validate_buffer(), the file + /// format selection logic in + /// Group::get_target_file_format_version_for_session(), and the file format + /// upgrade logic in Group::upgrade_file_format(). + int get_file_format_version() const noexcept; + +protected: + size_t m_baseline = 0; // Separation line between immutable and mutable refs. + + Replication* m_replication = nullptr; + + /// See get_file_format_version(). + int m_file_format_version = 0; + + ref_type m_debug_watch = 0; + + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// \throw std::bad_alloc If insufficient memory was available. + virtual MemRef do_alloc(const size_t size) = 0; + + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// The default version of this function simply allocates a new + /// chunk of memory, copies over the old contents, and then frees + /// the old chunk. + /// + /// \throw std::bad_alloc If insufficient memory was available. + virtual MemRef do_realloc(ref_type, const char* addr, size_t old_size, size_t new_size) = 0; + + /// Release the specified chunk of memory. + virtual void do_free(ref_type, const char* addr) noexcept = 0; + + /// Map the specified \a ref to the corresponding memory + /// address. Note that if is_read_only(ref) returns true, then the + /// referenced object is to be considered immutable, and it is + /// then entirely the responsibility of the caller that the memory + /// is not modified by way of the returned memory pointer. + virtual char* do_translate(ref_type ref) const noexcept = 0; + + Allocator() noexcept; + + // FIXME: This really doesn't belong in an allocator, but it is the best + // place for now, because every table has a pointer leading here. It would + // be more obvious to place it in Group, but that would add a runtime overhead, + // and access is time critical. + // + // This means that multiple threads that allocate Realm objects through the + // default allocator will share this variable, which is a logical design flaw + // that can make sync_if_needed() re-run queries even though it is not required. + // It must be atomic because it's shared. + std::atomic m_table_versioning_counter; + + /// Bump the global version counter. This method should be called when + /// version bumping is initiated. Then following calls to should_propagate_version() + /// can be used to prune the version bumping. + void bump_global_version() noexcept; + + /// Determine if the "local_version" is out of sync, so that it should + /// be updated. In that case: also update it. Called from Table::bump_version + /// to control propagation of version updates on tables within the group. + bool should_propagate_version(uint_fast64_t& local_version) noexcept; + + friend class Table; + friend class Group; +}; + +inline void Allocator::bump_global_version() noexcept +{ + m_table_versioning_counter += 1; +} + + +inline bool Allocator::should_propagate_version(uint_fast64_t& local_version) noexcept +{ + if (local_version != m_table_versioning_counter) { + local_version = m_table_versioning_counter; + return true; + } + else { + return false; + } +} + + +// Implementation: + +inline int_fast64_t from_ref(ref_type v) noexcept +{ + // Check that v is divisible by 8 (64-bit aligned). + REALM_ASSERT_DEBUG(v % 8 == 0); + + static_assert(std::is_same::value, + "If ref_type changes, from_ref and to_ref should probably be updated"); + + // Make sure that we preserve the bit pattern of the ref_type (without sign extension). + return util::from_twos_compl(uint_fast64_t(v)); +} + +inline ref_type to_ref(int_fast64_t v) noexcept +{ + // Check that v is divisible by 8 (64-bit aligned). + REALM_ASSERT_DEBUG(v % 8 == 0); + + // C++11 standard, paragraph 4.7.2 [conv.integral]: + // If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source + // integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's + // complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is + // no truncation). - end note ] + static_assert(std::is_unsigned::value, + "If ref_type changes, from_ref and to_ref should probably be updated"); + return ref_type(v); +} + +inline int64_t to_int64(size_t value) noexcept +{ + // FIXME: Enable once we get clang warning flags correct + // REALM_ASSERT_DEBUG(value <= std::numeric_limits::max()); + return static_cast(value); +} + + +inline MemRef::MemRef() noexcept + : m_addr(nullptr) + , m_ref(0) +{ +} + +inline MemRef::~MemRef() noexcept +{ +} + +inline MemRef::MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept + : m_addr(addr) + , m_ref(ref) +{ + static_cast(alloc); +#if REALM_ENABLE_MEMDEBUG + m_alloc = &alloc; +#endif +} + +inline MemRef::MemRef(ref_type ref, Allocator& alloc) noexcept + : m_addr(alloc.translate(ref)) + , m_ref(ref) +{ + static_cast(alloc); +#if REALM_ENABLE_MEMDEBUG + m_alloc = &alloc; +#endif +} + +inline char* MemRef::get_addr() +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(m_ref); +#endif + return m_addr; +} + +inline ref_type MemRef::get_ref() +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(m_ref); +#endif + return m_ref; +} + +inline void MemRef::set_ref(ref_type ref) +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(ref); +#endif + m_ref = ref; +} + +inline void MemRef::set_addr(char* addr) +{ + m_addr = addr; +} + +inline MemRef Allocator::alloc(size_t size) +{ + return do_alloc(size); +} + +inline MemRef Allocator::realloc_(ref_type ref, const char* addr, size_t old_size, size_t new_size) +{ +#ifdef REALM_DEBUG + if (ref == m_debug_watch) + REALM_TERMINATE("Allocator watch: Ref was reallocated"); +#endif + return do_realloc(ref, addr, old_size, new_size); +} + +inline void Allocator::free_(ref_type ref, const char* addr) noexcept +{ +#ifdef REALM_DEBUG + if (ref == m_debug_watch) + REALM_TERMINATE("Allocator watch: Ref was freed"); +#endif + return do_free(ref, addr); +} + +inline void Allocator::free_(MemRef mem) noexcept +{ + free_(mem.get_ref(), mem.get_addr()); +} + +inline char* Allocator::translate(ref_type ref) const noexcept +{ + return do_translate(ref); +} + +inline bool Allocator::is_read_only(ref_type ref) const noexcept +{ + REALM_ASSERT_DEBUG(ref != 0); + REALM_ASSERT_DEBUG(m_baseline != 0); // Attached SlabAlloc + return ref < m_baseline; +} + +inline Allocator::Allocator() noexcept +{ + m_table_versioning_counter = 0; +} + +inline Allocator::~Allocator() noexcept +{ +} + +inline Replication* Allocator::get_replication() noexcept +{ + return m_replication; +} + +inline int Allocator::get_file_format_version() const noexcept +{ + return m_file_format_version; +} + + +} // namespace realm + +#endif // REALM_ALLOC_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc_slab.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc_slab.hpp new file mode 100644 index 0000000..0e030a5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/alloc_slab.hpp @@ -0,0 +1,602 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ALLOC_SLAB_HPP +#define REALM_ALLOC_SLAB_HPP + +#include // unint8_t etc +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { + +// Pre-declarations +class Group; +class GroupWriter; + + +/// Thrown by Group and SharedGroup constructors if the specified file +/// (or memory buffer) does not appear to contain a valid Realm +/// database. +struct InvalidDatabase; + + +/// The allocator that is used to manage the memory of a Realm +/// group, i.e., a Realm database. +/// +/// Optionally, it can be attached to an pre-existing database (file +/// or memory buffer) which then becomes an immuatble part of the +/// managed memory. +/// +/// To attach a slab allocator to a pre-existing database, call +/// attach_file() or attach_buffer(). To create a new database +/// in-memory, call attach_empty(). +/// +/// For efficiency, this allocator manages its mutable memory as a set +/// of slabs. +class SlabAlloc : public Allocator { +public: + ~SlabAlloc() noexcept override; + SlabAlloc(); + + // Disable copying. Copying an allocator can produce double frees. + SlabAlloc(const SlabAlloc&) = delete; + SlabAlloc& operator=(const SlabAlloc&) = delete; + + /// \struct Config + /// \brief Storage for combining setup flags for initialization to + /// the SlabAlloc. + /// + /// \var Config::is_shared + /// Must be true if, and only if we are called on behalf of SharedGroup. + /// + /// \var Config::read_only + /// Open the file in read-only mode. This implies \a Config::no_create. + /// + /// \var Config::no_create + /// Fail if the file does not already exist. + /// + /// \var Config::skip_validate + /// Skip validation of file header. In a + /// set of overlapping SharedGroups, only the first one (the one + /// that creates/initlializes the coordination file) may validate + /// the header, otherwise it will result in a race condition. + /// + /// \var Config::encryption_key + /// 32-byte key to use to encrypt and decrypt the backing storage, + /// or nullptr to disable encryption. + /// + /// \var Config::session_initiator + /// If set, the caller is the session initiator and + /// guarantees exclusive access to the file. If attaching in + /// read/write mode, the file is modified: files on streaming form + /// is changed to non-streaming form, and if needed the file size + /// is adjusted to match mmap boundaries. + /// Must be set to false if is_shared is false. + /// + /// \var Config::clear_file + /// Always initialize the file as if it was a newly + /// created file and ignore any pre-existing contents. Requires that + /// Config::session_initiator be true as well. + struct Config { + bool is_shared = false; + bool read_only = false; + bool no_create = false; + bool skip_validate = false; + bool session_initiator = false; + bool clear_file = false; + const char* encryption_key = nullptr; + }; + + struct Retry { + }; + + /// \brief Attach this allocator to the specified file. + /// + /// It is an error if this function is called at a time where the specified + /// Realm file (file system inode) is modified asynchronously. + /// + /// In non-shared mode (when this function is called on behalf of a + /// free-standing Group instance), it is the responsibility of the + /// application to ensure that the Realm file is not modified concurrently + /// from any other thread or process. + /// + /// In shared mode (when this function is called on behalf of a SharedGroup + /// instance), the caller (SharedGroup::do_open()) must take steps to ensure + /// cross-process mutual exclusion. + /// + /// If the attached file contains an empty Realm (one whose top-ref is + /// zero), the file format version may remain undecided upon return from + /// this function. The file format is undecided if, and only if + /// get_file_format_version() returns zero. The caller is required to check + /// for this case, and decide on a file format version. This must happen + /// before the Realm opening process completes, and the decided file format + /// must be set in the allocator by calling set_file_format_version(). + /// + /// Except for \a file_path, the parameters are passed in through a + /// configuration object. + /// + /// \return The `ref` of the root node, or zero if there is none. + /// + /// Please note that attach_file can fail to attach to a file due to a + /// collision with a writer extending the file. This can only happen if the + /// caller is *not* the session initiator. When this happens, attach_file() + /// throws SlabAlloc::Retry, and the caller must retry the call. The caller + /// should check if it has become the session initiator before retrying. + /// This can happen if the conflicting thread (or process) terminates or + /// crashes before the next retry. + /// + /// \throw util::File::AccessError + /// \throw SlabAlloc::Retry + ref_type attach_file(const std::string& file_path, Config& cfg); + + /// Get the attached file. Only valid when called on an allocator with + /// an attached file. + util::File& get_file(); + + /// Attach this allocator to the specified memory buffer. + /// + /// If the attached buffer contains an empty Realm (one whose top-ref is + /// zero), the file format version may remain undecided upon return from + /// this function. The file format is undecided if, and only if + /// get_file_format_version() returns zero. The caller is required to check + /// for this case, and decide on a file format version. This must happen + /// before the Realm opening process completes, and the decided file format + /// must be set in the allocator by calling set_file_format_version(). + /// + /// It is an error to call this function on an attached + /// allocator. Doing so will result in undefined behavor. + /// + /// \return The `ref` of the root node, or zero if there is none. + /// + /// \sa own_buffer() + /// + /// \throw InvalidDatabase + ref_type attach_buffer(const char* data, size_t size); + + /// Reads file format from file header. Must be called from within a write + /// transaction. + int get_committed_file_format_version() const noexcept; + + /// Attach this allocator to an empty buffer. + /// + /// Upon return from this function, the file format is undecided + /// (get_file_format_version() returns zero). The caller is required to + /// decide on a file format version. This must happen before the Realm + /// opening process completes, and the decided file format must be set in + /// the allocator by calling set_file_format_version(). + /// + /// It is an error to call this function on an attached + /// allocator. Doing so will result in undefined behavor. + void attach_empty(); + + /// Detach from a previously attached file or buffer. + /// + /// This function does not reset free space tracking. To + /// completely reset the allocator, you must also call + /// reset_free_space_tracking(). + /// + /// This function has no effect if the allocator is already in the + /// detached state (idempotency). + void detach() noexcept; + + class DetachGuard; + + /// If a memory buffer has been attached using attach_buffer(), + /// mark it as owned by this slab allocator. Behaviour is + /// undefined if this function is called on a detached allocator, + /// one that is not attached using attach_buffer(), or one for + /// which this function has already been called during the latest + /// attachment. + void own_buffer() noexcept; + + /// Returns true if, and only if this allocator is currently + /// in the attached state. + bool is_attached() const noexcept; + + /// Returns true if, and only if this allocator is currently in + /// the attached state and attachment was not established using + /// attach_empty(). + bool nonempty_attachment() const noexcept; + + /// Reserve disk space now to avoid allocation errors at a later + /// point in time, and to minimize on-disk fragmentation. In some + /// cases, less fragmentation translates into improved + /// performance. On flash or SSD-drives this is likely a waste. + /// + /// Note: File::prealloc() may misbehave under race conditions (see + /// documentation of File::prealloc()). For that reason, to avoid race + /// conditions, when this allocator is used in a transactional mode, this + /// function may be called only when the caller has exclusive write + /// access. In non-transactional mode it is the responsibility of the user + /// to ensure non-concurrent file mutation. + /// + /// This function will call File::sync(). + /// + /// It is an error to call this function on an allocator that is not + /// attached to a file. Doing so will result in undefined behavior. + void resize_file(size_t new_file_size); + + /// Reserve disk space now to avoid allocation errors at a later point in + /// time, and to minimize on-disk fragmentation. In some cases, less + /// fragmentation translates into improved performance. On SSD-drives + /// preallocation is likely a waste. + /// + /// When supported by the system, a call to this function will make the + /// database file at least as big as the specified size, and cause space on + /// the target device to be allocated (note that on many systems on-disk + /// allocation is done lazily by default). If the file is already bigger + /// than the specified size, the size will be unchanged, and on-disk + /// allocation will occur only for the initial section that corresponds to + /// the specified size. On systems that do not support preallocation, this + /// function has no effect. To know whether preallocation is supported by + /// Realm on your platform, call util::File::is_prealloc_supported(). + /// + /// This function will call File::sync() if it changes the size of the file. + /// + /// It is an error to call this function on an allocator that is not + /// attached to a file. Doing so will result in undefined behavior. + void reserve_disk_space(size_t size_in_bytes); + + /// Get the size of the attached database file or buffer in number + /// of bytes. This size is not affected by new allocations. After + /// attachment, it can only be modified by a call to update_reader_view(). + /// + /// It is an error to call this function on a detached allocator, + /// or one that was attached using attach_empty(). Doing so will + /// result in undefined behavior. + size_t get_baseline() const noexcept; + + /// Get the total amount of managed memory. This is the baseline plus the + /// sum of the sizes of the allocated slabs. It includes any free space. + /// + /// It is an error to call this function on a detached + /// allocator. Doing so will result in undefined behavior. + size_t get_total_size() const noexcept; + + /// Mark all mutable memory (ref-space outside the attached file) as free + /// space. + void reset_free_space_tracking(); + + /// Update the readers view of the file: + /// + /// Remap the attached file such that a prefix of the specified + /// size becomes available in memory. If sucessfull, + /// get_baseline() will return the specified new file size. + /// + /// It is an error to call this function on a detached allocator, + /// or one that was not attached using attach_file(). Doing so + /// will result in undefined behavior. + /// + /// The file_size argument must be aligned to a *section* boundary: + /// The database file is logically split into sections, each section + /// guaranteed to be mapped as a contiguous address range. The allocation + /// of memory in the file must ensure that no allocation crosses the + /// boundary between two sections. + /// + /// Clears any allocator specicific caching of address translations + /// and force any later address translations to trigger decryption if required. + void update_reader_view(size_t file_size); + + /// Returns true initially, and after a call to reset_free_space_tracking() + /// up until the point of the first call to SlabAlloc::alloc(). Note that a + /// call to SlabAlloc::alloc() corresponds to a mutation event. + bool is_free_space_clean() const noexcept; + + /// \brief Update the file format version field of the allocator. + /// + /// This must be done during the opening of the Realm if the stored file + /// format version is zero (empty Realm), or after the file format is + /// upgraded. + /// + /// Note that this does not modify the attached file, only the "cached" + /// value subsequenty returned by get_file_format_version(). + /// + /// \sa get_file_format_version() + void set_file_format_version(int) noexcept; + + void verify() const override; +#ifdef REALM_DEBUG + void enable_debug(bool enable) + { + m_debug_out = enable; + } + bool is_all_free() const; + void print() const; +#endif + struct MappedFile; + +protected: + MemRef do_alloc(const size_t size) override; + MemRef do_realloc(ref_type, const char*, size_t old_size, size_t new_size) override; + // FIXME: It would be very nice if we could detect an invalid free operation in debug mode + void do_free(ref_type, const char*) noexcept override; + char* do_translate(ref_type) const noexcept override; + +private: + void internal_invalidate_cache() noexcept; + enum AttachMode { + attach_None, // Nothing is attached + attach_OwnedBuffer, // We own the buffer (m_data = nullptr for empty buffer) + attach_UsersBuffer, // We do not own the buffer + attach_SharedFile, // On behalf of SharedGroup + attach_UnsharedFile // Not on behalf of SharedGroup + }; + + // A slab is a dynamically allocated contiguous chunk of memory used to + // extend the amount of space available for database node + // storage. Inter-node references are represented as file offsets + // (a.k.a. "refs"), and each slab creates an apparently seamless extension + // of this file offset addressable space. Slabes are stored as rows in the + // Slabs table in order of ascending file offsets. + struct Slab { + ref_type ref_end; + char* addr; + }; + struct Chunk { + ref_type ref; + size_t size; + }; + + // Values of each used bit in m_flags + enum { + flags_SelectBit = 1, + }; + + // 24 bytes + struct Header { + uint64_t m_top_ref[2]; // 2 * 8 bytes + // Info-block 8-bytes + uint8_t m_mnemonic[4]; // "T-DB" + uint8_t m_file_format[2]; // See `library_file_format` + uint8_t m_reserved; + // bit 0 of m_flags is used to select between the two top refs. + uint8_t m_flags; + }; + + // 16 bytes + struct StreamingFooter { + uint64_t m_top_ref; + uint64_t m_magic_cookie; + }; + + static_assert(sizeof(Header) == 24, "Bad header size"); + static_assert(sizeof(StreamingFooter) == 16, "Bad footer size"); + + static const Header empty_file_header; + static void init_streaming_header(Header*, int file_format_version); + + static const uint_fast64_t footer_magic_cookie = 0x3034125237E526C8ULL; + + // The mappings are shared, if they are from a file + std::shared_ptr m_file_mappings; + + // We are caching local copies of all the additional mappings to allow + // for lock-free lookup during ref->address translation (we do not need + // to cache the first mapping, because it is immutable) (well, all the + // mappings are immutable, but the array holding them is not - it may + // have to be relocated) + std::unique_ptr>[]> m_local_mappings; + size_t m_num_local_mappings = 0; + + const char* m_data = nullptr; + size_t m_initial_chunk_size = 0; + size_t m_initial_section_size = 0; + int m_section_shifts = 0; + std::unique_ptr m_section_bases; + size_t m_num_section_bases = 0; + AttachMode m_attach_mode = attach_None; + bool m_file_on_streaming_form = false; + enum FeeeSpaceState { + free_space_Clean, + free_space_Dirty, + free_space_Invalid, + }; + + /// When set to free_space_Invalid, the free lists are no longer + /// up-to-date. This happens if do_free() or + /// reset_free_space_tracking() fails, presumably due to + /// std::bad_alloc being thrown during updating of the free space + /// list. In this this case, alloc(), realloc_(), and + /// get_free_read_only() must throw. This member is deliberately + /// placed here (after m_attach_mode) in the hope that it leads to + /// less padding between members due to alignment requirements. + FeeeSpaceState m_free_space_state = free_space_Clean; + + typedef std::vector slabs; + typedef std::vector chunks; + slabs m_slabs; + chunks m_free_space; + chunks m_free_read_only; + + bool m_debug_out = false; + struct hash_entry { + ref_type ref = 0; + const char* addr = nullptr; + size_t version = 0; + }; + mutable hash_entry cache[256]; + mutable size_t version = 1; + + /// Throws if free-lists are no longer valid. + void consolidate_free_read_only(); + /// Throws if free-lists are no longer valid. + const chunks& get_free_read_only() const; + + /// Throws InvalidDatabase if the file is not a Realm file, if the file is + /// corrupted, or if the specified encryption key is incorrect. This + /// function will not detect all forms of corruption, though. + void validate_buffer(const char* data, size_t len, const std::string& path, bool is_shared); + + /// Read the top_ref from the given buffer and set m_file_on_streaming_form + /// if the buffer contains a file in streaming form + ref_type get_top_ref(const char* data, size_t len); + + class ChunkRefEq; + class ChunkRefEndEq; + class SlabRefEndEq; + static bool ref_less_than_slab_ref_end(ref_type, const Slab&) noexcept; + + Replication* get_replication() const noexcept + { + return m_replication; + } + void set_replication(Replication* r) noexcept + { + m_replication = r; + } + + /// Returns the first section boundary *above* the given position. + size_t get_upper_section_boundary(size_t start_pos) const noexcept; + + /// Returns the first section boundary *at or below* the given position. + size_t get_lower_section_boundary(size_t start_pos) const noexcept; + + /// Returns true if the given position is at a section boundary + bool matches_section_boundary(size_t pos) const noexcept; + + /// Returns the index of the section holding a given address. + /// The section index is determined solely by the minimal section size, + /// and does not necessarily reflect the mapping. A mapping may + /// cover multiple sections - the initial mapping often does. + size_t get_section_index(size_t pos) const noexcept; + + /// Reverse: get the base offset of a section at a given index. Since the + /// computation is very time critical, this method just looks it up in + /// a table. The actual computation and setup of that table is done + /// during initialization with the help of compute_section_base() below. + inline size_t get_section_base(size_t index) const noexcept; + + /// Actually compute the starting offset of a section. Only used to initialize + /// a table of predefined results, which are then used by get_section_base(). + size_t compute_section_base(size_t index) const noexcept; + + /// Find a possible allocation of 'request_size' that will fit into a section + /// which is inside the range from 'start_pos' to 'start_pos'+'free_chunk_size' + /// If found return the position, if not return 0. + size_t find_section_in_range(size_t start_pos, size_t free_chunk_size, size_t request_size) const noexcept; + + friend class Group; + friend class SharedGroup; + friend class GroupWriter; +}; + +inline void SlabAlloc::internal_invalidate_cache() noexcept +{ + ++version; +} + +class SlabAlloc::DetachGuard { +public: + DetachGuard(SlabAlloc& alloc) noexcept + : m_alloc(&alloc) + { + } + ~DetachGuard() noexcept; + SlabAlloc* release() noexcept; + +private: + SlabAlloc* m_alloc; +}; + + +// Implementation: + +struct InvalidDatabase : util::File::AccessError { + InvalidDatabase(const std::string& msg, const std::string& path) + : util::File::AccessError(msg, path) + { + } +}; + +inline void SlabAlloc::own_buffer() noexcept +{ + REALM_ASSERT_3(m_attach_mode, ==, attach_UsersBuffer); + REALM_ASSERT(m_data); + REALM_ASSERT(m_file_mappings == nullptr); + m_attach_mode = attach_OwnedBuffer; +} + +inline bool SlabAlloc::is_attached() const noexcept +{ + return m_attach_mode != attach_None; +} + +inline bool SlabAlloc::nonempty_attachment() const noexcept +{ + return is_attached() && m_data; +} + +inline size_t SlabAlloc::get_baseline() const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + return m_baseline; +} + +inline bool SlabAlloc::is_free_space_clean() const noexcept +{ + return m_free_space_state == free_space_Clean; +} + +inline SlabAlloc::DetachGuard::~DetachGuard() noexcept +{ + if (m_alloc) + m_alloc->detach(); +} + +inline SlabAlloc* SlabAlloc::DetachGuard::release() noexcept +{ + SlabAlloc* alloc = m_alloc; + m_alloc = nullptr; + return alloc; +} + +inline bool SlabAlloc::ref_less_than_slab_ref_end(ref_type ref, const Slab& slab) noexcept +{ + return ref < slab.ref_end; +} + +inline size_t SlabAlloc::get_upper_section_boundary(size_t start_pos) const noexcept +{ + return get_section_base(1 + get_section_index(start_pos)); +} + +inline size_t SlabAlloc::get_lower_section_boundary(size_t start_pos) const noexcept +{ + return get_section_base(get_section_index(start_pos)); +} + +inline bool SlabAlloc::matches_section_boundary(size_t pos) const noexcept +{ + return pos == get_lower_section_boundary(pos); +} + +inline size_t SlabAlloc::get_section_base(size_t index) const noexcept +{ + return m_section_bases[index]; +} + +} // namespace realm + +#endif // REALM_ALLOC_SLAB_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array.hpp new file mode 100644 index 0000000..458d716 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array.hpp @@ -0,0 +1,3098 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +/* +Searching: The main finding function is: + template + void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState *state, Callback callback) const + + cond: One of Equal, NotEqual, Greater, etc. classes + Action: One of act_ReturnFirst, act_FindAll, act_Max, act_CallbackIdx, etc, constants + Callback: Optional function to call for each search result. Will be called if action == act_CallbackIdx + + find() will call find_action_pattern() or find_action() that again calls match() for each search result which + optionally calls callback(): + + find() -> find_action() -------> bool match() -> bool callback() + | ^ + +-> find_action_pattern()----+ + + If callback() returns false, find() will exit, otherwise it will keep searching remaining items in array. +*/ + +#ifndef REALM_ARRAY_HPP +#define REALM_ARRAY_HPP + +#include +#include // size_t +#include +#include +#include +#include + +#include // unint8_t etc + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + MMX: mmintrin.h + SSE: xmmintrin.h + SSE2: emmintrin.h + SSE3: pmmintrin.h + SSSE3: tmmintrin.h + SSE4A: ammintrin.h + SSE4.1: smmintrin.h + SSE4.2: nmmintrin.h +*/ +#ifdef REALM_COMPILER_SSE +#include // SSE2 +#include // SSE42 +#endif + +namespace realm { + +enum Action { + act_ReturnFirst, + act_Sum, + act_Max, + act_Min, + act_Count, + act_FindAll, + act_CallIdx, + act_CallbackIdx, + act_CallbackVal, + act_CallbackNone, + act_CallbackBoth, + act_Average +}; + +template +inline T no0(T v) +{ + return v == 0 ? 1 : v; +} + +/// Special index value. It has various meanings depending on +/// context. It is returned by some search functions to indicate 'not +/// found'. It is similar in function to std::string::npos. +const size_t npos = size_t(-1); + +// Maximum number of bytes that the payload of an array can be +const size_t max_array_payload = 0x00ffffffL; +const size_t max_array_payload_aligned = 0x00fffff8L; + +/// Alias for realm::npos. +const size_t not_found = npos; + +// Pre-definitions +class Array; +class StringColumn; +class GroupWriter; +template +class QueryState; +namespace _impl { +class ArrayWriterBase; +} + + +#ifdef REALM_DEBUG +struct MemStats { + size_t allocated = 0; + size_t used = 0; + size_t array_count = 0; +}; +template +std::basic_ostream& operator<<(std::basic_ostream& out, MemStats stats); +#endif + + +// Stores a value obtained from Array::get(). It is a ref if the least +// significant bit is clear, otherwise it is a tagged integer. A tagged interger +// is obtained from a logical integer value by left shifting by one bit position +// (multiplying by two), and then setting the least significant bit to +// one. Clearly, this means that the maximum value that can be stored as a +// tagged integer is 2**63 - 1. +class RefOrTagged { +public: + bool is_ref() const noexcept; + bool is_tagged() const noexcept; + ref_type get_as_ref() const noexcept; + uint_fast64_t get_as_int() const noexcept; + + static RefOrTagged make_ref(ref_type) noexcept; + static RefOrTagged make_tagged(uint_fast64_t) noexcept; + +private: + int_fast64_t m_value; + RefOrTagged(int_fast64_t) noexcept; + friend class Array; +}; + + +class ArrayParent { +public: + virtual ~ArrayParent() noexcept + { + } + +protected: + virtual void update_child_ref(size_t child_ndx, ref_type new_ref) = 0; + + virtual ref_type get_child_ref(size_t child_ndx) const noexcept = 0; + + // Used only by Array::to_dot(). + virtual std::pair get_to_dot_parent(size_t ndx_in_parent) const = 0; + + friend class Array; +}; + +struct TreeInsertBase { + size_t m_split_offset; + size_t m_split_size; +}; + +/// Provides access to individual array nodes of the database. +/// +/// This class serves purely as an accessor, it assumes no ownership of the +/// referenced memory. +/// +/// An array accessor can be in one of two states: attached or unattached. It is +/// in the attached state if, and only if is_attached() returns true. Most +/// non-static member functions of this class have undefined behaviour if the +/// accessor is in the unattached state. The exceptions are: is_attached(), +/// detach(), create(), init_from_ref(), init_from_mem(), init_from_parent(), +/// has_parent(), get_parent(), set_parent(), get_ndx_in_parent(), +/// set_ndx_in_parent(), adjust_ndx_in_parent(), and get_ref_from_parent(). +/// +/// An array accessor contains information about the parent of the referenced +/// array node. This 'reverse' reference is not explicitely present in the +/// underlying node hierarchy, but it is needed when modifying an array. A +/// modification may lead to relocation of the underlying array node, and the +/// parent must be updated accordingly. Since this applies recursivly all the +/// way to the root node, it is essential that the entire chain of parent +/// accessors is constructed and propperly maintained when a particular array is +/// modified. +/// +/// The parent reference (`pointer to parent`, `index in parent`) is updated +/// independently from the state of attachment to an underlying node. In +/// particular, the parent reference remains valid and is unannfected by changes +/// in attachment. These two aspects of the state of the accessor is updated +/// independently, and it is entirely the responsibility of the caller to update +/// them such that they are consistent with the underlying node hierarchy before +/// calling any method that modifies the underlying array node. +/// +/// FIXME: This class currently has fragments of ownership, in particular the +/// constructors that allocate underlying memory. On the other hand, the +/// destructor never frees the memory. This is a problematic situation, because +/// it so easily becomes an obscure source of leaks. There are three options for +/// a fix of which the third is most attractive but hardest to implement: (1) +/// Remove all traces of ownership semantics, that is, remove the constructors +/// that allocate memory, but keep the trivial copy constructor. For this to +/// work, it is important that the constness of the accessor has nothing to do +/// with the constness of the underlying memory, otherwise constness can be +/// violated simply by copying the accessor. (2) Disallov copying but associate +/// the constness of the accessor with the constness of the underlying +/// memory. (3) Provide full ownership semantics like is done for Table +/// accessors, and provide a proper copy constructor that really produces a copy +/// of the array. For this to work, the class should assume ownership if, and +/// only if there is no parent. A copy produced by a copy constructor will not +/// have a parent. Even if the original was part of a database, the copy will be +/// free-standing, that is, not be part of any database. For intra, or inter +/// database copying, one would have to also specify the target allocator. +class Array : public ArrayParent { +public: + // void state_init(int action, QueryState *state); + // bool match(int action, size_t index, int64_t value, QueryState *state); + + /// Create an array accessor in the unattached state. + explicit Array(Allocator&) noexcept; + + ~Array() noexcept override + { + } + + enum Type { + type_Normal, + + /// This array is the main array of an innner node of a B+-tree as used + /// in table columns. + type_InnerBptreeNode, + + /// This array may contain refs to subarrays. An element whose least + /// significant bit is zero, is a ref pointing to a subarray. An element + /// whose least significant bit is one, is just a value. It is the + /// responsibility of the application to ensure that non-ref values have + /// their least significant bit set. This will generally be done by + /// shifting the desired vlue to the left by one bit position, and then + /// setting the vacated bit to one. + type_HasRefs + }; + + /// Create a new integer array of the specified type and size, and filled + /// with the specified value, and attach this accessor to it. This does not + /// modify the parent reference information of this accessor. + /// + /// Note that the caller assumes ownership of the allocated underlying + /// node. It is not owned by the accessor. + void create(Type, bool context_flag = false, size_t size = 0, int_fast64_t value = 0); + + /// Reinitialize this array accessor to point to the specified new + /// underlying memory. This does not modify the parent reference information + /// of this accessor. + void init_from_ref(ref_type) noexcept; + + /// Same as init_from_ref(ref_type) but avoid the mapping of 'ref' to memory + /// pointer. + void init_from_mem(MemRef) noexcept; + + /// Same as `init_from_ref(get_ref_from_parent())`. + void init_from_parent() noexcept; + + /// Update the parents reference to this child. This requires, of course, + /// that the parent information stored in this child is up to date. If the + /// parent pointer is set to null, this function has no effect. + void update_parent(); + + /// Called in the context of Group::commit() to ensure that attached + /// accessors stay valid across a commit. Please note that this works only + /// for non-transactional commits. Accessors obtained during a transaction + /// are always detached when the transaction ends. + /// + /// Returns true if, and only if the array has changed. If the array has not + /// changed, then its children are guaranteed to also not have changed. + bool update_from_parent(size_t old_baseline) noexcept; + + /// Change the type of an already attached array node. + /// + /// The effect of calling this function on an unattached accessor is + /// undefined. + void set_type(Type); + + /// Construct a complete copy of this array (including its subarrays) using + /// the specified target allocator and return just the reference to the + /// underlying memory. + MemRef clone_deep(Allocator& target_alloc) const; + + /// Construct an empty integer array of the specified type, and return just + /// the reference to the underlying memory. + static MemRef create_empty_array(Type, bool context_flag, Allocator&); + + /// Construct an integer array of the specified type and size, and return + /// just the reference to the underlying memory. All elements will be + /// initialized to the specified value. + static MemRef create_array(Type, bool context_flag, size_t size, int_fast64_t value, Allocator&); + + /// Construct a shallow copy of the specified slice of this array using the + /// specified target allocator. Subarrays will **not** be cloned. See + /// slice_and_clone_children() for an alternative. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + /// Construct a deep copy of the specified slice of this array using the + /// specified target allocator. Subarrays will be cloned. + MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + // Parent tracking + bool has_parent() const noexcept; + ArrayParent* get_parent() const noexcept; + + /// Setting a new parent affects ownership of the attached array node, if + /// any. If a non-null parent is specified, and there was no parent + /// originally, then the caller passes ownership to the parent, and vice + /// versa. This assumes, of course, that the change in parentship reflects a + /// corresponding change in the list of children in the affected parents. + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t) noexcept; + void adjust_ndx_in_parent(int diff) noexcept; + + /// Get the ref of this array as known to the parent. The caller must ensure + /// that the parent information ('pointer to parent' and 'index in parent') + /// is correct before calling this function. + ref_type get_ref_from_parent() const noexcept; + + bool is_attached() const noexcept; + + /// Detach from the underlying array node. This method has no effect if the + /// accessor is currently unattached (idempotency). + void detach() noexcept; + + size_t size() const noexcept; + bool is_empty() const noexcept; + Type get_type() const noexcept; + + static void add_to_column(IntegerColumn* column, int64_t value); + + void insert(size_t ndx, int_fast64_t value); + void add(int_fast64_t value); + + /// This function is guaranteed to not throw if the current width is + /// sufficient for the specified value (e.g. if you have called + /// ensure_minimum_width(value)) and get_alloc().is_read_only(get_ref()) + /// returns false (noexcept:array-set). Note that for a value of zero, the + /// first criterion is trivially satisfied. + void set(size_t ndx, int64_t value); + + void set_as_ref(size_t ndx, ref_type ref); + + template + void set(size_t ndx, int64_t value); + + int64_t get(size_t ndx) const noexcept; + + template + int64_t get(size_t ndx) const noexcept; + + void get_chunk(size_t ndx, int64_t res[8]) const noexcept; + + template + void get_chunk(size_t ndx, int64_t res[8]) const noexcept; + + ref_type get_as_ref(size_t ndx) const noexcept; + + RefOrTagged get_as_ref_or_tagged(size_t ndx) const noexcept; + void set(size_t ndx, RefOrTagged); + void add(RefOrTagged); + void ensure_minimum_width(RefOrTagged); + + int64_t front() const noexcept; + int64_t back() const noexcept; + + /// Remove the element at the specified index, and move elements at higher + /// indexes to the next lower index. + /// + /// This function does **not** destroy removed subarrays. That is, if the + /// erased element is a 'ref' pointing to a subarray, then that subarray + /// will not be destroyed automatically. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the + /// call. This is automatically guaranteed if the array is used in a + /// non-transactional context, or if the array has already been successfully + /// modified within the current write transaction. + void erase(size_t ndx); + + /// Same as erase(size_t), but remove all elements in the specified + /// range. + /// + /// Please note that this function does **not** destroy removed subarrays. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void erase(size_t begin, size_t end); + + /// Reduce the size of this array to the specified number of elements. It is + /// an error to specify a size that is greater than the current size of this + /// array. The effect of doing so is undefined. This is just a shorthand for + /// calling the ranged erase() function with appropriate arguments. + /// + /// Please note that this function does **not** destroy removed + /// subarrays. See clear_and_destroy_children() for an alternative. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void truncate(size_t new_size); + + /// Reduce the size of this array to the specified number of elements. It is + /// an error to specify a size that is greater than the current size of this + /// array. The effect of doing so is undefined. Subarrays will be destroyed + /// recursively, as if by a call to `destroy_deep(subarray_ref, alloc)`. + /// + /// This function is guaranteed not to throw if + /// get_alloc().is_read_only(get_ref()) returns false. + void truncate_and_destroy_children(size_t new_size); + + /// Remove every element from this array. This is just a shorthand for + /// calling truncate(0). + /// + /// Please note that this function does **not** destroy removed + /// subarrays. See clear_and_destroy_children() for an alternative. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void clear(); + + /// Remove every element in this array. Subarrays will be destroyed + /// recursively, as if by a call to `destroy_deep(subarray_ref, + /// alloc)`. This is just a shorthand for calling + /// truncate_and_destroy_children(0). + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void clear_and_destroy_children(); + + /// If neccessary, expand the representation so that it can store the + /// specified value. + void ensure_minimum_width(int_fast64_t value); + + /// This one may change the represenation of the array, so be carefull if + /// you call it after ensure_minimum_width(). + void set_all_to_zero(); + + /// Add \a diff to the element at the specified index. + void adjust(size_t ndx, int_fast64_t diff); + + /// Add \a diff to all the elements in the specified index range. + void adjust(size_t begin, size_t end, int_fast64_t diff); + + /// Add signed \a diff to all elements that are greater than, or equal to \a + /// limit. + void adjust_ge(int_fast64_t limit, int_fast64_t diff); + + //@{ + /// These are similar in spirit to std::move() and std::move_backward from + /// ``. \a dest_begin must not be in the range [`begin`,`end`), and + /// \a dest_end must not be in the range (`begin`,`end`]. + /// + /// These functions are guaranteed to not throw if + /// `get_alloc().is_read_only(get_ref())` returns false. + void move(size_t begin, size_t end, size_t dest_begin); + void move_backward(size_t begin, size_t end, size_t dest_end); + //@} + + /// move_rotate moves one element from \a from to be located at index \a to, + /// shifting all elements inbetween by one. + /// + /// If \a from is larger than \a to, the elements inbetween are shifted down. + /// If \a to is larger than \a from, the elements inbetween are shifted up. + /// + /// This function is guaranteed to not throw if + /// `get_alloc().is_read_only(get_ref())` returns false. + void move_rotate(size_t from, size_t to, size_t num_elems = 1); + + //@{ + /// Find the lower/upper bound of the specified value in a sequence of + /// integers which must already be sorted ascendingly. + /// + /// For an integer value '`v`', lower_bound_int(v) returns the index '`l`' + /// of the first element such that `get(l) ≥ v`, and upper_bound_int(v) + /// returns the index '`u`' of the first element such that `get(u) > + /// v`. In both cases, if no such element is found, the returned value is + /// the number of elements in the array. + /// + /// 3 3 3 4 4 4 5 6 7 9 9 9 + /// ^ ^ ^ ^ ^ + /// | | | | | + /// | | | | -- Lower and upper bound of 15 + /// | | | | + /// | | | -- Lower and upper bound of 8 + /// | | | + /// | | -- Upper bound of 4 + /// | | + /// | -- Lower bound of 4 + /// | + /// -- Lower and upper bound of 1 + /// + /// These functions are similar to std::lower_bound() and + /// std::upper_bound(). + /// + /// We currently use binary search. See for example + /// http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary. + /// + /// FIXME: It may be worth considering if overall efficiency can be improved + /// by doing a linear search for short sequences. + size_t lower_bound_int(int64_t value) const noexcept; + size_t upper_bound_int(int64_t value) const noexcept; + //@} + + /// \brief Search the \c Array for a value greater or equal than \a target, + /// starting the search at the \a start index. If \a indirection is + /// provided, use it as a look-up table to iterate over the \c Array. + /// + /// If \a indirection is not provided, then the \c Array must be sorted in + /// ascending order. If \a indirection is provided, then its values should + /// point to indices in this \c Array in such a way that iteration happens + /// in ascending order. + /// + /// Behaviour is undefined if: + /// - a value in \a indirection is out of bounds for this \c Array; + /// - \a indirection does not contain at least as many elements as this \c + /// Array; + /// - sorting conditions are not respected; + /// - \a start is greater than the number of elements in this \c Array or + /// \a indirection (if provided). + /// + /// \param target the smallest value to search for + /// \param start the offset at which to start searching in the array + /// \param indirection an \c Array containing valid indices of values in + /// this \c Array, sorted in ascending order + /// \return the index of the value if found, or realm::not_found otherwise + size_t find_gte(const int64_t target, size_t start, size_t end = size_t(-1)) const; + + void preset(int64_t min, int64_t max, size_t num_items); + void preset(size_t bitwidth, size_t num_items); + + int64_t sum(size_t start = 0, size_t end = size_t(-1)) const; + size_t count(int64_t value) const noexcept; + + bool maximum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const; + + bool minimum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const; + + /// This information is guaranteed to be cached in the array accessor. + bool is_inner_bptree_node() const noexcept; + + /// Returns true if type is either type_HasRefs or type_InnerColumnNode. + /// + /// This information is guaranteed to be cached in the array accessor. + bool has_refs() const noexcept; + void set_has_refs(bool) noexcept; + + /// This information is guaranteed to be cached in the array accessor. + /// + /// Columns and indexes can use the context bit to differentiate leaf types. + bool get_context_flag() const noexcept; + void set_context_flag(bool) noexcept; + + ref_type get_ref() const noexcept; + MemRef get_mem() const noexcept; + + /// Destroy only the array that this accessor is attached to, not the + /// children of that array. See non-static destroy_deep() for an + /// alternative. If this accessor is already in the detached state, this + /// function has no effect (idempotency). + void destroy() noexcept; + + /// Recursively destroy children (as if calling + /// clear_and_destroy_children()), then put this accessor into the detached + /// state (as if calling detach()), then free the allocated memory. If this + /// accessor is already in the detached state, this function has no effect + /// (idempotency). + void destroy_deep() noexcept; + + /// Shorthand for `destroy(MemRef(ref, alloc), alloc)`. + static void destroy(ref_type ref, Allocator& alloc) noexcept; + + /// Destroy only the specified array node, not its children. See also + /// destroy_deep(MemRef, Allocator&). + static void destroy(MemRef, Allocator&) noexcept; + + /// Shorthand for `destroy_deep(MemRef(ref, alloc), alloc)`. + static void destroy_deep(ref_type ref, Allocator& alloc) noexcept; + + /// Destroy the specified array node and all of its children, recursively. + /// + /// This is done by freeing the specified array node after calling + /// destroy_deep() for every contained 'ref' element. + static void destroy_deep(MemRef, Allocator&) noexcept; + + Allocator& get_alloc() const noexcept + { + return m_alloc; + } + + // Serialization + + /// Returns the ref (position in the target stream) of the written copy of + /// this array, or the ref of the original array if \a only_if_modified is + /// true, and this array is unmodified (Alloc::is_read_only()). + /// + /// The number of bytes that will be written by a non-recursive invocation + /// of this function is exactly the number returned by get_byte_size(). + /// + /// \param out The destination stream (writer). + /// + /// \param deep If true, recursively write out subarrays, but still subject + /// to \a only_if_modified. + /// + /// \param only_if_modified Set to `false` to always write, or to `true` to + /// only write the array if it has been modified. + ref_type write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const; + + /// Same as non-static write() with `deep` set to true. This is for the + /// cases where you do not already have an array accessor available. + static ref_type write(ref_type, Allocator&, _impl::ArrayWriterBase&, bool only_if_modified); + + // Main finding function - used for find_first, find_all, sum, max, min, etc. + bool find(int cond, Action action, int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, bool nullable_array = false, bool find_null = false) const; + + // Templated find function to avoid conversion to and from integer represenation of condition + template + bool find(Action action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + bool nullable_array = false, bool find_null = false) const + { + if (action == act_ReturnFirst) { + REALM_TEMPEX3(return find, cond, act_ReturnFirst, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Sum) { + REALM_TEMPEX3(return find, cond, act_Sum, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Min) { + REALM_TEMPEX3(return find, cond, act_Min, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Max) { + REALM_TEMPEX3(return find, cond, act_Max, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Count) { + REALM_TEMPEX3(return find, cond, act_Count, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_FindAll) { + REALM_TEMPEX3(return find, cond, act_FindAll, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_CallbackIdx) { + REALM_TEMPEX3(return find, cond, act_CallbackIdx, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + REALM_ASSERT_DEBUG(false); + return false; + } + + + /* + bool find(int cond, Action action, null, size_t start, size_t end, size_t baseindex, + QueryState* state) const; + */ + + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + // This is the one installed into the m_vtable->finder slots. + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + /* + template + bool find(null, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const; + */ + + // Optimized implementation for release mode + template + bool find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + // Called for each search result + template + bool find_action(size_t index, util::Optional value, QueryState* state, + Callback callback) const; + + template + bool find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const; + + // Wrappers for backwards compatibility and for simple use without + // setting up state initialization etc + template + size_t find_first(int64_t value, size_t start = 0, size_t end = size_t(-1)) const; + + void find_all(IntegerColumn* result, int64_t value, size_t col_offset = 0, size_t begin = 0, + size_t end = size_t(-1)) const; + + size_t find_first(int64_t value, size_t begin = 0, size_t end = size_t(-1)) const; + + // Non-SSE find for the four functions Equal/NotEqual/Less/Greater + template + bool compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Non-SSE find for Equal/NotEqual + template + inline bool compare_equality(int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const; + + // Non-SSE find for Less/Greater + template + bool compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + +// SSE find for the four functions Equal/NotEqual/Less/Greater +#ifdef REALM_COMPILER_SSE + template + bool find_sse(int64_t value, __m128i* data, size_t items, QueryState* state, size_t baseindex, + Callback callback) const; + + template + REALM_FORCEINLINE bool find_sse_intern(__m128i* action_data, __m128i* data, size_t items, + QueryState* state, size_t baseindex, Callback callback) const; + +#endif + + template + inline bool test_zero(uint64_t value) const; // Tests value for 0-elements + + template + size_t find_zero(uint64_t v) const; // Finds position of 0/non-zero element + + template + uint64_t cascade(uint64_t a) const; // Sets lowermost bits of zero or non-zero elements + + template + int64_t + find_gtlt_magic(int64_t v) const; // Compute magic constant needed for searching for value 'v' using bit hacks + + template + inline int64_t lower_bits() const; // Return chunk with lower bit set in each element + + size_t first_set_bit(unsigned int v) const; + size_t first_set_bit64(int64_t v) const; + + template + int64_t get_universal(const char* const data, const size_t ndx) const; + + // Find value greater/less in 64-bit chunk - only works for positive values + template + bool find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, + Callback callback) const; + + // Find value greater/less in 64-bit chunk - no constraints + template + bool find_gtlt(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const; + + ref_type bptree_leaf_insert(size_t ndx, int64_t, TreeInsertBase& state); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static int_fast64_t get(const char* header, size_t ndx) noexcept; + + /// Like get(const char*, size_t) but gets two consecutive + /// elements. + static std::pair get_two(const char* header, size_t ndx) noexcept; + + static void get_three(const char* data, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept; + + /// The meaning of 'width' depends on the context in which this + /// array is used. + size_t get_width() const noexcept + { + return m_width; + } + + static char* get_data_from_header(char*) noexcept; + static char* get_header_from_data(char*) noexcept; + static const char* get_data_from_header(const char*) noexcept; + + enum WidthType { + wtype_Bits = 0, + wtype_Multiply = 1, + wtype_Ignore = 2, + }; + + static bool get_is_inner_bptree_node_from_header(const char*) noexcept; + static bool get_hasrefs_from_header(const char*) noexcept; + static bool get_context_flag_from_header(const char*) noexcept; + static WidthType get_wtype_from_header(const char*) noexcept; + static uint_least8_t get_width_from_header(const char*) noexcept; + static size_t get_size_from_header(const char*) noexcept; + + static Type get_type_from_header(const char*) noexcept; + + /// Get the number of bytes currently in use by this array. This + /// includes the array header, but it does not include allocated + /// bytes corresponding to excess capacity. The result is + /// guaranteed to be a multiple of 8 (i.e., 64-bit aligned). + /// + /// This number is exactly the number of bytes that will be + /// written by a non-recursive invocation of write(). + size_t get_byte_size() const noexcept; + + /// Get the maximum number of bytes that can be written by a + /// non-recursive invocation of write() on an array with the + /// specified number of elements, that is, the maximum value that + /// can be returned by get_byte_size(). + static size_t get_max_byte_size(size_t num_elems) noexcept; + + /// FIXME: Belongs in IntegerArray + static size_t calc_aligned_byte_size(size_t size, int width); + +#ifdef REALM_DEBUG + void print() const; + void verify() const; + typedef size_t (*LeafVerifier)(MemRef, Allocator&); + void verify_bptree(LeafVerifier) const; + class MemUsageHandler { + public: + virtual void handle(ref_type ref, size_t allocated, size_t used) = 0; + }; + void report_memory_usage(MemUsageHandler&) const; + void stats(MemStats& stats_dest) const; + typedef void (*LeafDumper)(MemRef, Allocator&, std::ostream&, int level); + void dump_bptree_structure(std::ostream&, int level, LeafDumper) const; + void to_dot(std::ostream&, StringData title = StringData()) const; + class ToDotHandler { + public: + virtual void to_dot(MemRef leaf_mem, ArrayParent*, size_t ndx_in_parent, std::ostream&) = 0; + ~ToDotHandler() + { + } + }; + void bptree_to_dot(std::ostream&, ToDotHandler&) const; + void to_dot_parent_edge(std::ostream&) const; +#endif + + static const int header_size = 8; // Number of bytes used by header + + // The encryption layer relies on headers always fitting within a single page. + static_assert(header_size == 8, "Header must always fit in entirely on a page"); + + Array& operator=(const Array&) = delete; // not allowed + Array(const Array&) = delete; // not allowed +protected: + typedef bool (*CallbackDummy)(int64_t); + +protected: + // Includes array header. Not necessarily 8-byte aligned. + virtual size_t calc_byte_len(size_t num_items, size_t width) const; + + virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept; + + bool get_is_inner_bptree_node_from_header() const noexcept; + bool get_hasrefs_from_header() const noexcept; + bool get_context_flag_from_header() const noexcept; + WidthType get_wtype_from_header() const noexcept; + uint_least8_t get_width_from_header() const noexcept; + size_t get_size_from_header() const noexcept; + + // Undefined behavior if m_alloc.is_read_only(m_ref) returns true + size_t get_capacity_from_header() const noexcept; + + void set_header_is_inner_bptree_node(bool value) noexcept; + void set_header_hasrefs(bool value) noexcept; + void set_header_context_flag(bool value) noexcept; + void set_header_wtype(WidthType value) noexcept; + void set_header_width(int value) noexcept; + void set_header_size(size_t value) noexcept; + void set_header_capacity(size_t value) noexcept; + + static void set_header_is_inner_bptree_node(bool value, char* header) noexcept; + static void set_header_hasrefs(bool value, char* header) noexcept; + static void set_header_context_flag(bool value, char* header) noexcept; + static void set_header_wtype(WidthType value, char* header) noexcept; + static void set_header_width(int value, char* header) noexcept; + static void set_header_size(size_t value, char* header) noexcept; + static void set_header_capacity(size_t value, char* header) noexcept; + + static void init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag, + WidthType width_type, int width, size_t size, size_t capacity) noexcept; + + + // This returns the minimum value ("lower bound") of the representable values + // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64. + template + static int_fast64_t lbound_for_width() noexcept; + + static int_fast64_t lbound_for_width(size_t width) noexcept; + + // This returns the maximum value ("inclusive upper bound") of the representable values + // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64. + template + static int_fast64_t ubound_for_width() noexcept; + + static int_fast64_t ubound_for_width(size_t width) noexcept; + + template + void set_width() noexcept; + void set_width(size_t) noexcept; + void alloc(size_t init_size, size_t width); + void copy_on_write(); + +private: + void do_copy_on_write(size_t minimum_size = 0); + void do_ensure_minimum_width(int_fast64_t); + + template + int64_t sum(size_t start, size_t end) const; + + template + bool minmax(int64_t& result, size_t start, size_t end, size_t* return_ndx) const; + + template + size_t find_gte(const int64_t target, size_t start, size_t end) const; + + template + size_t adjust_ge(size_t start, size_t end, int_fast64_t limit, int_fast64_t diff); + +protected: + /// The total size in bytes (including the header) of a new empty + /// array. Must be a multiple of 8 (i.e., 64-bit aligned). + static const size_t initial_capacity = 128; + + /// It is an error to specify a non-zero value unless the width + /// type is wtype_Bits. It is also an error to specify a non-zero + /// size if the width type is wtype_Ignore. + static MemRef create(Type, bool context_flag, WidthType, size_t size, int_fast64_t value, Allocator&); + + static MemRef clone(MemRef header, Allocator& alloc, Allocator& target_alloc); + + /// Get the address of the header of this array. + char* get_header() noexcept; + + /// Same as get_byte_size(). + static size_t get_byte_size_from_header(const char*) noexcept; + + // Undefined behavior if array is in immutable memory + static size_t get_capacity_from_header(const char*) noexcept; + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + void destroy_children(size_t offset = 0) noexcept; + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + + bool is_read_only() const noexcept; + +protected: + // Getters and Setters for adaptive-packed arrays + typedef int64_t (Array::*Getter)(size_t) const; // Note: getters must not throw + typedef void (Array::*Setter)(size_t, int64_t); + typedef bool (Array::*Finder)(int64_t, size_t, size_t, size_t, QueryState*) const; + typedef void (Array::*ChunkGetter)(size_t, int64_t res[8]) const; // Note: getters must not throw + + struct VTable { + Getter getter; + ChunkGetter chunk_getter; + Setter setter; + Finder finder[cond_VTABLE_FINDER_COUNT]; // one for each active function pointer + }; + template + struct VTableForWidth; + +protected: + /// Takes a 64-bit value and returns the minimum number of bits needed + /// to fit the value. For alignment this is rounded up to nearest + /// log2. Posssible results {0, 1, 2, 4, 8, 16, 32, 64} + static size_t bit_width(int64_t value); + +#ifdef REALM_DEBUG + void report_memory_usage_2(MemUsageHandler&) const; +#endif + +private: + Getter m_getter = nullptr; // cached to avoid indirection + const VTable* m_vtable = nullptr; + +public: + // FIXME: Should not be public + char* m_data = nullptr; // Points to first byte after header + +#if REALM_ENABLE_MEMDEBUG + // If m_no_relocation is false, then copy_on_write() will always relocate this array, regardless if it's + // required or not. If it's true, then it will never relocate, which is currently only expeted inside + // GroupWriter::write_group() due to a unique chicken/egg problem (see description there). + bool m_no_relocation = false; +#endif + +protected: + int64_t m_lbound; // min number that can be stored with current m_width + int64_t m_ubound; // max number that can be stored with current m_width + + size_t m_size = 0; // Number of elements currently stored. + size_t m_capacity = 0; // Number of elements that fit inside the allocated memory. + + Allocator& m_alloc; + +private: + size_t m_ref; + ArrayParent* m_parent = nullptr; + size_t m_ndx_in_parent = 0; // Ignored if m_parent is null. + +protected: + uint_least8_t m_width = 0; // Size of an element (meaning depend on type of array). + bool m_is_inner_bptree_node; // This array is an inner node of B+-tree. + bool m_has_refs; // Elements whose first bit is zero are refs to subarrays. + bool m_context_flag; // Meaning depends on context. + +private: + ref_type do_write_shallow(_impl::ArrayWriterBase&) const; + ref_type do_write_deep(_impl::ArrayWriterBase&, bool only_if_modified) const; + static size_t calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept; + + friend class SlabAlloc; + friend class GroupWriter; + friend class StringColumn; +}; + + +// Implementation: + +class QueryStateBase { + virtual void dyncast() + { + } +}; + +template <> +class QueryState : public QueryStateBase { +public: + int64_t m_state; + size_t m_match_count; + size_t m_limit; + size_t m_minmax_index; // used only for min/max, to save index of current min/max value + + template + bool uses_val() + { + if (action == act_Max || action == act_Min || action == act_Sum) + return true; + else + return false; + } + + void init(Action action, IntegerColumn* akku, size_t limit) + { + m_match_count = 0; + m_limit = limit; + m_minmax_index = not_found; + + if (action == act_Max) + m_state = -0x7fffffffffffffffLL - 1LL; + else if (action == act_Min) + m_state = 0x7fffffffffffffffLL; + else if (action == act_ReturnFirst) + m_state = not_found; + else if (action == act_Sum) + m_state = 0; + else if (action == act_Count) + m_state = 0; + else if (action == act_FindAll) + m_state = reinterpret_cast(akku); + else if (action == act_CallbackIdx) { + } + else { + REALM_ASSERT_DEBUG(false); + } + } + + template + inline bool match(size_t index, uint64_t indexpattern, int64_t value) + { + if (pattern) { + if (action == act_Count) { + // If we are close to 'limit' argument in query, we cannot count-up a complete chunk. Count up single + // elements instead + if (m_match_count + 64 >= m_limit) + return false; + + m_state += fast_popcount64(indexpattern); + m_match_count = size_t(m_state); + return true; + } + // Other aggregates cannot (yet) use bit pattern for anything. Make Array-finder call with pattern = false + // instead + return false; + } + + ++m_match_count; + + if (action == act_Max) { + if (value > m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Sum) + m_state += value; + else if (action == act_Count) { + m_state++; + m_match_count = size_t(m_state); + } + else if (action == act_FindAll) { + Array::add_to_column(reinterpret_cast(m_state), index); + } + else if (action == act_ReturnFirst) { + m_state = index; + return false; + } + else { + REALM_ASSERT_DEBUG(false); + } + return (m_limit > m_match_count); + } + + template + inline bool match(size_t index, uint64_t indexpattern, util::Optional value) + { + // FIXME: This is a temporary hack for nullable integers. + if (value) { + return match(index, indexpattern, *value); + } + + // If value is null, the only sensible actions are count, find_all, and return first. + // Max, min, and sum should all have no effect. + if (action == act_Count) { + m_state++; + m_match_count = size_t(m_state); + } + else if (action == act_FindAll) { + Array::add_to_column(reinterpret_cast(m_state), index); + } + else if (action == act_ReturnFirst) { + m_match_count++; + m_state = index; + return false; + } + return m_limit > m_match_count; + } +}; + +// Used only for Basic-types: currently float and double +template +class QueryState : public QueryStateBase { +public: + R m_state; + size_t m_match_count; + size_t m_limit; + size_t m_minmax_index; // used only for min/max, to save index of current min/max value + + template + bool uses_val() + { + return (action == act_Max || action == act_Min || action == act_Sum || action == act_Count); + } + + void init(Action action, Array*, size_t limit) + { + REALM_ASSERT((std::is_same::value || std::is_same::value)); + m_match_count = 0; + m_limit = limit; + m_minmax_index = not_found; + + if (action == act_Max) + m_state = -std::numeric_limits::infinity(); + else if (action == act_Min) + m_state = std::numeric_limits::infinity(); + else if (action == act_Sum) + m_state = 0.0; + else { + REALM_ASSERT_DEBUG(false); + } + } + + template + inline bool match(size_t index, uint64_t /*indexpattern*/, resulttype value) + { + if (pattern) + return false; + + static_assert(action == act_Sum || action == act_Max || action == act_Min || action == act_Count, + "Search action not supported"); + + if (action == act_Count) { + ++m_match_count; + } + else if (!null::is_null_float(value)) { + ++m_match_count; + if (action == act_Max) { + if (value > m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Sum) + m_state += value; + else { + REALM_ASSERT_DEBUG(false); + } + } + + return (m_limit > m_match_count); + } +}; + +inline bool RefOrTagged::is_ref() const noexcept +{ + return (m_value & 1) == 0; +} + +inline bool RefOrTagged::is_tagged() const noexcept +{ + return !is_ref(); +} + +inline ref_type RefOrTagged::get_as_ref() const noexcept +{ + // to_ref() is defined in + return to_ref(m_value); +} + +inline uint_fast64_t RefOrTagged::get_as_int() const noexcept +{ + // The bitwise AND is there in case uint_fast64_t is wider than 64 bits. + return (uint_fast64_t(m_value) & 0xFFFFFFFFFFFFFFFFULL) >> 1; +} + +inline RefOrTagged RefOrTagged::make_ref(ref_type ref) noexcept +{ + // from_ref() is defined in + int_fast64_t value = from_ref(ref); + return RefOrTagged(value); +} + +inline RefOrTagged RefOrTagged::make_tagged(uint_fast64_t i) noexcept +{ + REALM_ASSERT(i < (1ULL << 63)); + int_fast64_t value = util::from_twos_compl((i << 1) | 1); + return RefOrTagged(value); +} + +inline RefOrTagged::RefOrTagged(int_fast64_t value) noexcept + : m_value(value) +{ +} + +inline Array::Array(Allocator& allocator) noexcept + : m_alloc(allocator) +{ +} + +inline void Array::create(Type type, bool context_flag, size_t length, int_fast64_t value) +{ + MemRef mem = create_array(type, context_flag, length, value, m_alloc); // Throws + init_from_mem(mem); +} + + +inline void Array::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT_DEBUG(ref); + char* header = m_alloc.translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); +} + + +inline void Array::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + + +inline Array::Type Array::get_type() const noexcept +{ + if (m_is_inner_bptree_node) { + REALM_ASSERT_DEBUG(m_has_refs); + return type_InnerBptreeNode; + } + if (m_has_refs) + return type_HasRefs; + return type_Normal; +} + + +inline void Array::get_chunk(size_t ndx, int64_t res[8]) const noexcept +{ + REALM_ASSERT_DEBUG(ndx < m_size); + (this->*(m_vtable->chunk_getter))(ndx, res); +} + + +inline int64_t Array::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + REALM_ASSERT_DEBUG(ndx < m_size); + return (this->*m_getter)(ndx); + + // Two ideas that are not efficient but may be worth looking into again: + /* + // Assume correct width is found early in REALM_TEMPEX, which is the case for B tree offsets that + // are probably either 2^16 long. Turns out to be 25% faster if found immediately, but 50-300% slower + // if found later + REALM_TEMPEX(return get, (ndx)); + */ + /* + // Slightly slower in both of the if-cases. Also needs an matchcount m_size check too, to avoid + // reading beyond array. + if (m_width >= 8 && m_size > ndx + 7) + return get<64>(ndx >> m_shift) & m_widthmask; + else + return (this->*(m_vtable->getter))(ndx); + */ +} + +inline int64_t Array::front() const noexcept +{ + return get(0); +} + +inline int64_t Array::back() const noexcept +{ + return get(m_size - 1); +} + +inline ref_type Array::get_as_ref(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + REALM_ASSERT_DEBUG(m_has_refs); + int64_t v = get(ndx); + return to_ref(v); +} + +inline RefOrTagged Array::get_as_ref_or_tagged(size_t ndx) const noexcept +{ + REALM_ASSERT(has_refs()); + return RefOrTagged(get(ndx)); +} + +inline void Array::set(size_t ndx, RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + set(ndx, ref_or_tagged.m_value); // Throws +} + +inline void Array::add(RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + add(ref_or_tagged.m_value); // Throws +} + +inline void Array::ensure_minimum_width(RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + ensure_minimum_width(ref_or_tagged.m_value); // Throws +} + +inline bool Array::is_inner_bptree_node() const noexcept +{ + return m_is_inner_bptree_node; +} + +inline bool Array::has_refs() const noexcept +{ + return m_has_refs; +} + +inline void Array::set_has_refs(bool value) noexcept +{ + if (m_has_refs != value) { + REALM_ASSERT(!is_read_only()); + m_has_refs = value; + set_header_hasrefs(value); + } +} + +inline bool Array::get_context_flag() const noexcept +{ + return m_context_flag; +} + +inline void Array::set_context_flag(bool value) noexcept +{ + if (m_context_flag != value) { + REALM_ASSERT(!is_read_only()); + m_context_flag = value; + set_header_context_flag(value); + } +} + +inline ref_type Array::get_ref() const noexcept +{ + return m_ref; +} + +inline MemRef Array::get_mem() const noexcept +{ + return MemRef(get_header_from_data(m_data), m_ref, m_alloc); +} + +inline void Array::destroy() noexcept +{ + if (!is_attached()) + return; + char* header = get_header_from_data(m_data); + m_alloc.free_(m_ref, header); + m_data = nullptr; +} + +inline void Array::destroy_deep() noexcept +{ + if (!is_attached()) + return; + + if (m_has_refs) + destroy_children(); + + char* header = get_header_from_data(m_data); + m_alloc.free_(m_ref, header); + m_data = nullptr; +} + +inline ref_type Array::write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const +{ + REALM_ASSERT(is_attached()); + + if (only_if_modified && m_alloc.is_read_only(m_ref)) + return m_ref; + + if (!deep || !m_has_refs) + return do_write_shallow(out); // Throws + + return do_write_deep(out, only_if_modified); // Throws +} + +inline ref_type Array::write(ref_type ref, Allocator& alloc, _impl::ArrayWriterBase& out, bool only_if_modified) +{ + if (only_if_modified && alloc.is_read_only(ref)) + return ref; + + Array array(alloc); + array.init_from_ref(ref); + + if (!array.m_has_refs) + return array.do_write_shallow(out); // Throws + + return array.do_write_deep(out, only_if_modified); // Throws +} + +inline void Array::add(int_fast64_t value) +{ + insert(m_size, value); +} + +inline void Array::erase(size_t ndx) +{ + // This can throw, but only if array is currently in read-only + // memory. + move(ndx + 1, size(), ndx); + + // Update size (also in header) + --m_size; + set_header_size(m_size); +} + + +inline void Array::erase(size_t begin, size_t end) +{ + if (begin != end) { + // This can throw, but only if array is currently in read-only memory. + move(end, size(), begin); // Throws + + // Update size (also in header) + m_size -= end - begin; + set_header_size(m_size); + } +} + +inline void Array::clear() +{ + truncate(0); // Throws +} + +inline void Array::clear_and_destroy_children() +{ + truncate_and_destroy_children(0); +} + +inline void Array::destroy(ref_type ref, Allocator& alloc) noexcept +{ + destroy(MemRef(ref, alloc), alloc); +} + +inline void Array::destroy(MemRef mem, Allocator& alloc) noexcept +{ + alloc.free_(mem); +} + +inline void Array::destroy_deep(ref_type ref, Allocator& alloc) noexcept +{ + destroy_deep(MemRef(ref, alloc), alloc); +} + +inline void Array::destroy_deep(MemRef mem, Allocator& alloc) noexcept +{ + if (!get_hasrefs_from_header(mem.get_addr())) { + alloc.free_(mem); + return; + } + Array array(alloc); + array.init_from_mem(mem); + array.destroy_deep(); +} + + +inline void Array::adjust(size_t ndx, int_fast64_t diff) +{ + REALM_ASSERT_3(ndx, <=, m_size); + if (diff != 0) { + // FIXME: Should be optimized + int_fast64_t v = get(ndx); + set(ndx, int64_t(v + diff)); // Throws + } +} + +inline void Array::adjust(size_t begin, size_t end, int_fast64_t diff) +{ + if (diff != 0) { + // FIXME: Should be optimized + for (size_t i = begin; i != end; ++i) + adjust(i, diff); // Throws + } +} + + +//------------------------------------------------- + +inline bool Array::get_is_inner_bptree_node_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x80) != 0; +} +inline bool Array::get_hasrefs_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x40) != 0; +} +inline bool Array::get_context_flag_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x20) != 0; +} +inline Array::WidthType Array::get_wtype_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return WidthType((int(h[4]) & 0x18) >> 3); +} +inline uint_least8_t Array::get_width_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return uint_least8_t((1 << (int(h[4]) & 0x07)) >> 1); +} +inline size_t Array::get_size_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (size_t(h[5]) << 16) + (size_t(h[6]) << 8) + h[7]; +} +inline size_t Array::get_capacity_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (size_t(h[0]) << 16) + (size_t(h[1]) << 8) + h[2]; +} + + +inline char* Array::get_data_from_header(char* header) noexcept +{ + return header + header_size; +} +inline char* Array::get_header_from_data(char* data) noexcept +{ + return data - header_size; +} +inline const char* Array::get_data_from_header(const char* header) noexcept +{ + return get_data_from_header(const_cast(header)); +} + + +inline bool Array::get_is_inner_bptree_node_from_header() const noexcept +{ + return get_is_inner_bptree_node_from_header(get_header_from_data(m_data)); +} +inline bool Array::get_hasrefs_from_header() const noexcept +{ + return get_hasrefs_from_header(get_header_from_data(m_data)); +} +inline bool Array::get_context_flag_from_header() const noexcept +{ + return get_context_flag_from_header(get_header_from_data(m_data)); +} +inline Array::WidthType Array::get_wtype_from_header() const noexcept +{ + return get_wtype_from_header(get_header_from_data(m_data)); +} +inline uint_least8_t Array::get_width_from_header() const noexcept +{ + return get_width_from_header(get_header_from_data(m_data)); +} +inline size_t Array::get_size_from_header() const noexcept +{ + return get_size_from_header(get_header_from_data(m_data)); +} +inline size_t Array::get_capacity_from_header() const noexcept +{ + return get_capacity_from_header(get_header_from_data(m_data)); +} + + +inline void Array::set_header_is_inner_bptree_node(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x80) | int(value) << 7); +} + +inline void Array::set_header_hasrefs(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x40) | int(value) << 6); +} + +inline void Array::set_header_context_flag(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x20) | int(value) << 5); +} + +inline void Array::set_header_wtype(WidthType value, char* header) noexcept +{ + // Indicates how to calculate size in bytes based on width + // 0: bits (width/8) * size + // 1: multiply width * size + // 2: ignore 1 * size + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x18) | int(value) << 3); +} + +inline void Array::set_header_width(int value, char* header) noexcept +{ + // Pack width in 3 bits (log2) + int w = 0; + while (value) { + ++w; + value >>= 1; + } + REALM_ASSERT_3(w, <, 8); + + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x7) | w); +} + +inline void Array::set_header_size(size_t value, char* header) noexcept +{ + REALM_ASSERT_3(value, <=, max_array_payload); + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[5] = uchar((value >> 16) & 0x000000FF); + h[6] = uchar((value >> 8) & 0x000000FF); + h[7] = uchar(value & 0x000000FF); +} + +// Note: There is a copy of this function is test_alloc.cpp +inline void Array::set_header_capacity(size_t value, char* header) noexcept +{ + REALM_ASSERT_3(value, <=, max_array_payload); + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[0] = uchar((value >> 16) & 0x000000FF); + h[1] = uchar((value >> 8) & 0x000000FF); + h[2] = uchar(value & 0x000000FF); +} + + +inline void Array::set_header_is_inner_bptree_node(bool value) noexcept +{ + set_header_is_inner_bptree_node(value, get_header_from_data(m_data)); +} +inline void Array::set_header_hasrefs(bool value) noexcept +{ + set_header_hasrefs(value, get_header_from_data(m_data)); +} +inline void Array::set_header_context_flag(bool value) noexcept +{ + set_header_context_flag(value, get_header_from_data(m_data)); +} +inline void Array::set_header_wtype(WidthType value) noexcept +{ + set_header_wtype(value, get_header_from_data(m_data)); +} +inline void Array::set_header_width(int value) noexcept +{ + set_header_width(value, get_header_from_data(m_data)); +} +inline void Array::set_header_size(size_t value) noexcept +{ + set_header_size(value, get_header_from_data(m_data)); +} +inline void Array::set_header_capacity(size_t value) noexcept +{ + set_header_capacity(value, get_header_from_data(m_data)); +} + + +inline Array::Type Array::get_type_from_header(const char* header) noexcept +{ + if (get_is_inner_bptree_node_from_header(header)) + return type_InnerBptreeNode; + if (get_hasrefs_from_header(header)) + return type_HasRefs; + return type_Normal; +} + + +inline char* Array::get_header() noexcept +{ + return get_header_from_data(m_data); +} + +inline size_t Array::calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept +{ + size_t num_bytes = 0; + switch (wtype) { + case wtype_Bits: { + // Current assumption is that size is at most 2^24 and that width is at most 64. + // In that case the following will never overflow. (Assuming that size_t is at least 32 bits) + REALM_ASSERT_3(size, <, 0x1000000); + size_t num_bits = size * width; + num_bytes = (num_bits + 7) >> 3; + break; + } + case wtype_Multiply: { + num_bytes = size * width; + break; + } + case wtype_Ignore: + num_bytes = size; + break; + } + + // Ensure 8-byte alignment + num_bytes = (num_bytes + 7) & ~size_t(7); + + num_bytes += header_size; + + return num_bytes; +} + +inline size_t Array::get_byte_size() const noexcept +{ + const char* header = get_header_from_data(m_data); + WidthType wtype = get_wtype_from_header(header); + size_t num_bytes = calc_byte_size(wtype, m_size, m_width); + + REALM_ASSERT_7(m_alloc.is_read_only(m_ref), ==, true, ||, num_bytes, <=, get_capacity_from_header(header)); + + return num_bytes; +} + + +inline size_t Array::get_byte_size_from_header(const char* header) noexcept +{ + size_t size = get_size_from_header(header); + uint_least8_t width = get_width_from_header(header); + WidthType wtype = get_wtype_from_header(header); + size_t num_bytes = calc_byte_size(wtype, size, width); + + return num_bytes; +} + + +inline void Array::init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag, + WidthType width_type, int width, size_t size, size_t capacity) noexcept +{ + // Note: Since the header layout contains unallocated bit and/or + // bytes, it is important that we put the entire header into a + // well defined state initially. + std::fill(header, header + header_size, 0); + set_header_is_inner_bptree_node(is_inner_bptree_node, header); + set_header_hasrefs(has_refs, header); + set_header_context_flag(context_flag, header); + set_header_wtype(width_type, header); + set_header_width(width, header); + set_header_size(size, header); + set_header_capacity(capacity, header); +} + + +//------------------------------------------------- + +inline MemRef Array::clone_deep(Allocator& target_alloc) const +{ + char* header = get_header_from_data(m_data); + return clone(MemRef(header, m_ref, m_alloc), m_alloc, target_alloc); // Throws +} + +inline MemRef Array::create_empty_array(Type type, bool context_flag, Allocator& alloc) +{ + size_t size = 0; + int_fast64_t value = 0; + return create_array(type, context_flag, size, value, alloc); // Throws +} + +inline MemRef Array::create_array(Type type, bool context_flag, size_t size, int_fast64_t value, Allocator& alloc) +{ + return create(type, context_flag, wtype_Bits, size, value, alloc); // Throws +} + +inline bool Array::has_parent() const noexcept +{ + return m_parent != nullptr; +} + +inline ArrayParent* Array::get_parent() const noexcept +{ + return m_parent; +} + +inline void Array::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_parent = parent; + m_ndx_in_parent = ndx_in_parent; +} + +inline size_t Array::get_ndx_in_parent() const noexcept +{ + return m_ndx_in_parent; +} + +inline void Array::set_ndx_in_parent(size_t ndx) noexcept +{ + m_ndx_in_parent = ndx; +} + +inline void Array::adjust_ndx_in_parent(int diff) noexcept +{ + // Note that `diff` is promoted to an unsigned type, and that + // C++03 still guarantees the expected result regardless of the + // sizes of `int` and `decltype(m_ndx_in_parent)`. + m_ndx_in_parent += diff; +} + +inline ref_type Array::get_ref_from_parent() const noexcept +{ + ref_type ref = m_parent->get_child_ref(m_ndx_in_parent); + return ref; +} + +inline bool Array::is_attached() const noexcept +{ + return m_data != nullptr; +} + +inline void Array::detach() noexcept +{ + m_data = nullptr; +} + +inline size_t Array::size() const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + return m_size; +} + +inline bool Array::is_empty() const noexcept +{ + return size() == 0; +} + +inline size_t Array::get_max_byte_size(size_t num_elems) noexcept +{ + int max_bytes_per_elem = 8; + return header_size + num_elems * max_bytes_per_elem; +} + +inline void Array::update_parent() +{ + if (m_parent) + m_parent->update_child_ref(m_ndx_in_parent, m_ref); +} + + +inline void Array::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + set(child_ndx, new_ref); +} + +inline ref_type Array::get_child_ref(size_t child_ndx) const noexcept +{ + return get_as_ref(child_ndx); +} + +inline bool Array::is_read_only() const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + return m_alloc.is_read_only(m_ref); +} + +inline void Array::copy_on_write() +{ +#if REALM_ENABLE_MEMDEBUG + // We want to relocate this array regardless if there is a need or not, in order to catch use-after-free bugs. + // Only exception is inside GroupWriter::write_group() (see explanation at the definition of the m_no_relocation + // member) + if (!m_no_relocation) { +#else + if (is_read_only()) { +#endif + do_copy_on_write(); + } +} + +inline void Array::ensure_minimum_width(int_fast64_t value) +{ + if (value >= m_lbound && value <= m_ubound) + return; + do_ensure_minimum_width(value); +} + + +//************************************************************************************* +// Finding code * +//************************************************************************************* + +template +int64_t Array::get(size_t ndx) const noexcept +{ + return get_universal(m_data, ndx); +} + +template +int64_t Array::get_universal(const char* data, size_t ndx) const +{ + if (w == 0) { + return 0; + } + else if (w == 1) { + size_t offset = ndx >> 3; + return (data[offset] >> (ndx & 7)) & 0x01; + } + else if (w == 2) { + size_t offset = ndx >> 2; + return (data[offset] >> ((ndx & 3) << 1)) & 0x03; + } + else if (w == 4) { + size_t offset = ndx >> 1; + return (data[offset] >> ((ndx & 1) << 2)) & 0x0F; + } + else if (w == 8) { + return *reinterpret_cast(data + ndx); + } + else if (w == 16) { + size_t offset = ndx * 2; + return *reinterpret_cast(data + offset); + } + else if (w == 32) { + size_t offset = ndx * 4; + return *reinterpret_cast(data + offset); + } + else if (w == 64) { + size_t offset = ndx * 8; + return *reinterpret_cast(data + offset); + } + else { + REALM_ASSERT_DEBUG(false); + return int64_t(-1); + } +} + +/* +find() (calls find_optimized()) will call match() for each search result. + +If pattern == true: + 'indexpattern' contains a 64-bit chunk of elements, each of 'width' bits in size where each element indicates a + match if its lower bit is set, otherwise it indicates a non-match. 'index' tells the database row index of the + first element. You must return true if you chose to 'consume' the chunk or false if not. If not, then Array-finder + will afterwards call match() successive times with pattern == false. + +If pattern == false: + 'index' tells the row index of a single match and 'value' tells its value. Return false to make Array-finder break + its search or return true to let it continue until 'end' or 'limit'. + +Array-finder decides itself if - and when - it wants to pass you an indexpattern. It depends on array bit width, match +frequency, and whether the arithemetic and computations for the given search criteria makes it feasible to construct +such a pattern. +*/ + +// These wrapper functions only exist to enable a possibility to make the compiler see that 'value' and/or 'index' are +// unused, such that caller's computation of these values will not be made. Only works if find_action() and +// find_action_pattern() rewritten as macros. Note: This problem has been fixed in next upcoming array.hpp version +template +bool Array::find_action(size_t index, util::Optional value, QueryState* state, + Callback callback) const +{ + if (action == act_CallbackIdx) + return callback(index); + else + return state->match(index, 0, value); +} +template +bool Array::find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const +{ + static_cast(callback); + if (action == act_CallbackIdx) { + // Possible future optimization: call callback(index) like in above find_action(), in a loop for each bit set + // in 'pattern' + return false; + } + return state->match(index, pattern, 0); +} + + +template +uint64_t Array::cascade(uint64_t a) const +{ + // Takes a chunk of values as argument and sets the least significant bit for each + // element which is zero or non-zero, depending on the template parameter. + // Example for zero=true: + // width == 4 and a = 0x5fd07a107610f610 + // will return: 0x0001000100010001 + + // static values needed for fast population count + const uint64_t m1 = 0x5555555555555555ULL; + + if (width == 1) { + return zero ? ~a : a; + } + else if (width == 2) { + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0x3 * 0x1; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a &= m1; // isolate single bit in each segment + if (zero) + a ^= m1; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 4) { + const uint64_t m = ~0ULL / 0xF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xF * 0x7; + const uint64_t c2 = ~0ULL / 0xF * 0x3; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 8) { + const uint64_t m = ~0ULL / 0xFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFF * 0x7F; + const uint64_t c2 = ~0ULL / 0xFF * 0x3F; + const uint64_t c3 = ~0ULL / 0xFF * 0x0F; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 16) { + const uint64_t m = ~0ULL / 0xFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFFFF * 0x7FFF; + const uint64_t c2 = ~0ULL / 0xFFFF * 0x3FFF; + const uint64_t c3 = ~0ULL / 0xFFFF * 0x0FFF; + const uint64_t c4 = ~0ULL / 0xFFFF * 0x00FF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + + else if (width == 32) { + const uint64_t m = ~0ULL / 0xFFFFFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFFFFFFFF * 0x7FFFFFFF; + const uint64_t c2 = ~0ULL / 0xFFFFFFFF * 0x3FFFFFFF; + const uint64_t c3 = ~0ULL / 0xFFFFFFFF * 0x0FFFFFFF; + const uint64_t c4 = ~0ULL / 0xFFFFFFFF * 0x00FFFFFF; + const uint64_t c5 = ~0ULL / 0xFFFFFFFF * 0x0000FFFF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a |= (a >> 16) & c5; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 64) { + return (a == 0) == zero; + } + else { + REALM_ASSERT_DEBUG(false); + return uint64_t(-1); + } +} + +// This is the main finding function for Array. Other finding functions are just wrappers around this one. +// Search for 'value' using condition cond (Equal, NotEqual, Less, etc) and call find_action() or +// find_action_pattern() for each match. Break and return if find_action() returns false or 'end' is reached. + +// If nullable_array is set, then find_optimized() will treat the array is being nullable, i.e. it will skip the +// first entry and compare correctly against null, etc. +// +// If find_null is set, it means that we search for a null. In that case, `value` is ignored. If find_null is set, +// then nullable_array must be set too. +template +bool Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + REALM_ASSERT(!(find_null && !nullable_array)); + REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + + size_t start2 = start; + cond c; + + if (end == npos) + end = nullable_array ? size() - 1 : size(); + + if (nullable_array) { + // We were called by find() of a nullable array. So skip first entry, take nulls in count, etc, etc. Fixme: + // Huge speed optimizations are possible here! This is a very simple generic method. + for (; start2 < end; start2++) { + int64_t v = get(start2 + 1); + if (c(v, value, v == get(0), find_null)) { + util::Optional v2(v == get(0) ? util::none : util::make_optional(v)); + if (!find_action(start2 + baseindex, v2, state, callback)) + return false; // tell caller to stop aggregating/search + } + } + return true; // tell caller to continue aggregating/search (on next array leafs) + } + + + // Test first few items with no initial time overhead + if (start2 > 0) { + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + } + + if (!(m_size > start2 && start2 < end)) + return true; + + if (end == size_t(-1)) + end = m_size; + + // Return immediately if no items in array can match (such as if cond == Greater && value == 100 && + // m_ubound == 15) + if (!c.can_match(value, m_lbound, m_ubound)) + return true; + + // optimization if all items are guaranteed to match (such as cond == NotEqual && value == 100 && m_ubound == 15) + if (c.will_match(value, m_lbound, m_ubound)) { + size_t end2; + + if (action == act_CallbackIdx) + end2 = end; + else { + REALM_ASSERT_DEBUG(state->m_match_count < state->m_limit); + size_t process = state->m_limit - state->m_match_count; + end2 = end - start2 > process ? start2 + process : end; + } + if (action == act_Sum || action == act_Max || action == act_Min) { + int64_t res; + size_t res_ndx = 0; + if (action == act_Sum) + res = Array::sum(start2, end2); + if (action == act_Max) + Array::maximum(res, start2, end2, &res_ndx); + if (action == act_Min) + Array::minimum(res, start2, end2, &res_ndx); + + find_action(res_ndx + baseindex, res, state, callback); + // find_action will increment match count by 1, so we need to `-1` from the number of elements that + // we performed the fast Array methods on. + state->m_match_count += end2 - start2 - 1; + } + else if (action == act_Count) { + state->m_state += end2 - start2; + } + else { + for (; start2 < end2; start2++) + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + return true; + } + + // finder cannot handle this bitwidth + REALM_ASSERT_3(m_width, !=, 0); + +#if defined(REALM_COMPILER_SSE) + // Only use SSE if payload is at least one SSE chunk (128 bits) in size. Also note taht SSE doesn't support + // Less-than comparison for 64-bit values. + if ((!(std::is_same::value && m_width == 64)) && end - start2 >= sizeof(__m128i) && m_width >= 8 && + (sseavx<42>() || (sseavx<30>() && std::is_same::value && m_width < 64))) { + + // find_sse() must start2 at 16-byte boundary, so search area before that using compare_equality() + __m128i* const a = reinterpret_cast<__m128i*>(round_up(m_data + start2 * bitwidth / 8, sizeof(__m128i))); + __m128i* const b = reinterpret_cast<__m128i*>(round_down(m_data + end * bitwidth / 8, sizeof(__m128i))); + + if (!compare( + value, start2, (reinterpret_cast(a) - m_data) * 8 / no0(bitwidth), baseindex, state, callback)) + return false; + + // Search aligned area with SSE + if (b > a) { + if (sseavx<42>()) { + if (!find_sse( + value, a, b - a, state, + baseindex + ((reinterpret_cast(a) - m_data) * 8 / no0(bitwidth)), callback)) + return false; + } + else if (sseavx<30>()) { + + if (!find_sse( + value, a, b - a, state, + baseindex + ((reinterpret_cast(a) - m_data) * 8 / no0(bitwidth)), callback)) + return false; + } + } + + // Search remainder with compare_equality() + if (!compare( + value, (reinterpret_cast(b) - m_data) * 8 / no0(bitwidth), end, baseindex, state, callback)) + return false; + + return true; + } + else { + return compare(value, start2, end, baseindex, state, callback); + } +#else + return compare(value, start2, end, baseindex, state, callback); +#endif +} + +template +inline int64_t Array::lower_bits() const +{ + if (width == 1) + return 0xFFFFFFFFFFFFFFFFULL; + else if (width == 2) + return 0x5555555555555555ULL; + else if (width == 4) + return 0x1111111111111111ULL; + else if (width == 8) + return 0x0101010101010101ULL; + else if (width == 16) + return 0x0001000100010001ULL; + else if (width == 32) + return 0x0000000100000001ULL; + else if (width == 64) + return 0x0000000000000001ULL; + else { + REALM_ASSERT_DEBUG(false); + return int64_t(-1); + } +} + +// Tests if any chunk in 'value' is 0 +template +inline bool Array::test_zero(uint64_t value) const +{ + uint64_t hasZeroByte; + uint64_t lower = lower_bits(); + uint64_t upper = lower_bits() * 1ULL << (width == 0 ? 0 : (width - 1ULL)); + hasZeroByte = (value - lower) & ~value & upper; + return hasZeroByte != 0; +} + +// Finds first zero (if eq == true) or non-zero (if eq == false) element in v and returns its position. +// IMPORTANT: This function assumes that at least 1 item matches (test this with test_zero() or other means first)! +template +size_t Array::find_zero(uint64_t v) const +{ + size_t start = 0; + uint64_t hasZeroByte; + // Warning free way of computing (1ULL << width) - 1 + uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); + + if (eq == (((v >> (width * start)) & mask) == 0)) { + return 0; + } + + // Bisection optimization, speeds up small bitwidths with high match frequency. More partions than 2 do NOT pay + // off because the work done by test_zero() is wasted for the cases where the value exists in first half, but + // useful if it exists in last half. Sweet spot turns out to be the widths and partitions below. + if (width <= 8) { + hasZeroByte = test_zero(v | 0xffffffff00000000ULL); + if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) { + // 00?? -> increasing + start += 64 / no0(width) / 2; + if (width <= 4) { + hasZeroByte = test_zero(v | 0xffff000000000000ULL); + if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) { + // 000? + start += 64 / no0(width) / 4; + } + } + } + else { + if (width <= 4) { + // ??00 + hasZeroByte = test_zero(v | 0xffffffffffff0000ULL); + if (eq ? !hasZeroByte : (v & 0x000000000000ffffULL) == 0) { + // 0?00 + start += 64 / no0(width) / 4; + } + } + } + } + + while (eq == (((v >> (width * start)) & mask) != 0)) { + // You must only call find_zero() if you are sure that at least 1 item matches + REALM_ASSERT_3(start, <=, 8 * sizeof(v)); + start++; + } + + return start; +} + +// Generate a magic constant used for later bithacks +template +int64_t Array::find_gtlt_magic(int64_t v) const +{ + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t magic = gt ? (~0ULL / no0(mask1) * (mask2 - v)) : (~0ULL / no0(mask1) * v); + return magic; +} + +template +bool Array::find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, + Callback callback) const +{ + // Tests if a a chunk of values contains values that are greater (if gt == true) or less (if gt == false) than v. + // Fast, but limited to work when all values in the chunk are positive. + + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t m = gt ? (((chunk + magic) | chunk) & ~0ULL / no0(mask1) * (mask2 + 1)) + : ((chunk - magic) & ~chunk & ~0ULL / no0(mask1) * (mask2 + 1)); + size_t p = 0; + while (m) { + if (find_action_pattern(baseindex, m >> (no0(width) - 1), state, callback)) + break; // consumed, so do not call find_action() + + size_t t = first_set_bit64(m) / no0(width); + p += t; + if (!find_action(p + baseindex, (chunk >> (p * width)) & mask1, state, callback)) + return false; + + if ((t + 1) * width == 64) + m = 0; + else + m >>= (t + 1) * width; + p++; + } + + return true; +} + +// clang-format off +template +bool Array::find_gtlt(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const +{ + // Find items in 'chunk' that are greater (if gt == true) or smaller (if gt == false) than 'v'. Fixme, __forceinline can make it crash in vS2010 - find out why + if (width == 1) { + for (size_t t = 0; t < 64; t++) { + if (gt ? static_cast(chunk & 0x1) > v : static_cast(chunk & 0x1) < v) {if (!find_action( t + baseindex, static_cast(chunk & 0x1), state, callback)) return false;} + chunk >>= 1; + } + } + else if (width == 2) { + // Alot (50% +) faster than loop/compiler-unrolled loop + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 0 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 1 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 2 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 3 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 4 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 5 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 6 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 7 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 8 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 9 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 10 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 11 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 12 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 13 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 14 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 15 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 16 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 17 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 18 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 19 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 20 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 21 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 22 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 23 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 24 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 25 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 26 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 27 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 28 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 29 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 30 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 31 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + } + else if (width == 4) { + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 0 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 1 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 2 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 3 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 4 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 5 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 6 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 7 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 8 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 9 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 10 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 11 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 12 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 13 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 14 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 15 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + } + else if (width == 8) { + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 0 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 1 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 2 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 3 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 4 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 5 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 6 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 7 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + } + else if (width == 16) { + + if (gt ? static_cast(chunk >> 0 * 16) > v : static_cast(chunk >> 0 * 16) < v) {if (!find_action( 0 + baseindex, static_cast(chunk >> 0 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 1 * 16) > v : static_cast(chunk >> 1 * 16) < v) {if (!find_action( 1 + baseindex, static_cast(chunk >> 1 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 2 * 16) > v : static_cast(chunk >> 2 * 16) < v) {if (!find_action( 2 + baseindex, static_cast(chunk >> 2 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 3 * 16) > v : static_cast(chunk >> 3 * 16) < v) {if (!find_action( 3 + baseindex, static_cast(chunk >> 3 * 16), state, callback)) return false;}; + } + else if (width == 32) { + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 0 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 32; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 1 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 32; + } + else if (width == 64) { + if (gt ? static_cast(v) > v : static_cast(v) < v) {if (!find_action( 0 + baseindex, static_cast(v), state, callback)) return false;}; + } + + return true; +} +// clang-format on + +/// Find items in this Array that are equal (eq == true) or different (eq = false) from 'value' +template +inline bool Array::compare_equality(int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + + size_t ee = round_up(start, 64 / no0(width)); + ee = ee > end ? end : ee; + for (; start < ee; ++start) + if (eq ? (get(start) == value) : (get(start) != value)) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + + if (start >= end) + return true; + + if (width != 32 && width != 64) { + const int64_t* p = reinterpret_cast(m_data + (start * width / 8)); + const int64_t* const e = reinterpret_cast(m_data + (end * width / 8)) - 1; + const uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + const uint64_t valuemask = + ~0ULL / no0(mask) * (value & mask); // the "== ? :" is to avoid division by 0 compiler error + + while (p < e) { + uint64_t chunk = *p; + uint64_t v2 = chunk ^ valuemask; + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(width); + size_t a = 0; + + while (eq ? test_zero(v2) : v2) { + + if (find_action_pattern(start + baseindex, cascade(v2), state, callback)) + break; // consumed + + size_t t = find_zero(v2); + a += t; + + if (a >= 64 / no0(width)) + break; + + if (!find_action(a + start + baseindex, get(start + t), state, callback)) + return false; + v2 >>= (t + 1) * width; + a += 1; + } + + ++p; + } + + // Loop ended because we are near end or end of array. No need to optimize search in remainder in this case + // because end of array means that + // lots of search work has taken place prior to ending here. So time spent searching remainder is relatively + // tiny + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(width); + } + + while (start < end) { + if (eq ? get(start) == value : get(start) != value) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + ++start; + } + + return true; +} + +// There exists a couple of find() functions that take more or less template arguments. Always call the one that +// takes as most as possible to get best performance. + +// This is the one installed into the m_vtable->finder slots. +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const +{ + return find(value, start, end, baseindex, state, CallbackDummy()); +} + +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + REALM_TEMPEX4(return find, cond, action, m_width, Callback, + (value, start, end, baseindex, state, callback, nullable_array, find_null)); +} + +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + return find_optimized(value, start, end, baseindex, state, callback, + nullable_array, find_null); +} + +#ifdef REALM_COMPILER_SSE +// 'items' is the number of 16-byte SSE chunks. Returns index of packed element relative to first integer of first +// chunk +template +bool Array::find_sse(int64_t value, __m128i* data, size_t items, QueryState* state, size_t baseindex, + Callback callback) const +{ + __m128i search = {0}; + + if (width == 8) + search = _mm_set1_epi8(static_cast(value)); + else if (width == 16) + search = _mm_set1_epi16(static_cast(value)); + else if (width == 32) + search = _mm_set1_epi32(static_cast(value)); + else if (width == 64) { + if (std::is_same::value) + REALM_ASSERT(false); + else + search = _mm_set_epi64x(value, value); + } + + return find_sse_intern(data, &search, items, state, baseindex, callback); +} + +// Compares packed action_data with packed data (equal, less, etc) and performs aggregate action (max, min, sum, +// find_all, etc) on value inside action_data for first match, if any +template +REALM_FORCEINLINE bool Array::find_sse_intern(__m128i* action_data, __m128i* data, size_t items, + QueryState* state, size_t baseindex, Callback callback) const +{ + size_t i = 0; + __m128i compare_result = {0}; + unsigned int resmask; + + // Search loop. Unrolling it has been tested to NOT increase performance (apparently mem bound) + for (i = 0; i < items; ++i) { + // equal / not-equal + if (std::is_same::value || std::is_same::value) { + if (width == 8) + compare_result = _mm_cmpeq_epi8(action_data[i], *data); + if (width == 16) + compare_result = _mm_cmpeq_epi16(action_data[i], *data); + if (width == 32) + compare_result = _mm_cmpeq_epi32(action_data[i], *data); + if (width == 64) { + compare_result = _mm_cmpeq_epi64(action_data[i], *data); // SSE 4.2 only + } + } + + // greater + else if (std::is_same::value) { + if (width == 8) + compare_result = _mm_cmpgt_epi8(action_data[i], *data); + if (width == 16) + compare_result = _mm_cmpgt_epi16(action_data[i], *data); + if (width == 32) + compare_result = _mm_cmpgt_epi32(action_data[i], *data); + if (width == 64) + compare_result = _mm_cmpgt_epi64(action_data[i], *data); + } + // less + else if (std::is_same::value) { + if (width == 8) + compare_result = _mm_cmplt_epi8(action_data[i], *data); + else if (width == 16) + compare_result = _mm_cmplt_epi16(action_data[i], *data); + else if (width == 32) + compare_result = _mm_cmplt_epi32(action_data[i], *data); + else + REALM_ASSERT(false); + } + + resmask = _mm_movemask_epi8(compare_result); + + if (std::is_same::value) + resmask = ~resmask & 0x0000ffff; + + size_t s = i * sizeof(__m128i) * 8 / no0(width); + + while (resmask != 0) { + + uint64_t upper = lower_bits() << (no0(width / 8) - 1); + uint64_t pattern = + resmask & + upper; // fixme, bits at wrong offsets. Only OK because we only use them in 'count' aggregate + if (find_action_pattern(s + baseindex, pattern, state, callback)) + break; + + size_t idx = first_set_bit(resmask) * 8 / no0(width); + s += idx; + if (!find_action( + s + baseindex, get_universal(reinterpret_cast(action_data), s), state, callback)) + return false; + resmask >>= (idx + 1) * no0(width) / 8; + ++s; + } + } + + return true; +} +#endif // REALM_COMPILER_SSE + +template +bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + cond c; + REALM_ASSERT_3(start, <=, end); + if (start == end) + return true; + + + int64_t v; + + // We can compare first element without checking for out-of-range + v = get(start); + if (c(v, foreign->get(start))) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + + start++; + + if (start + 3 < end) { + v = get(start); + if (c(v, foreign->get(start))) + if (!find_action(start + baseindex, v, state, callback)) + return false; + + v = get(start + 1); + if (c(v, foreign->get(start + 1))) + if (!find_action(start + 1 + baseindex, v, state, callback)) + return false; + + v = get(start + 2); + if (c(v, foreign->get(start + 2))) + if (!find_action(start + 2 + baseindex, v, state, callback)) + return false; + + start += 3; + } + else if (start == end) { + return true; + } + + bool r; + REALM_TEMPEX4(r = compare_leafs, cond, action, m_width, Callback, + (foreign, start, end, baseindex, state, callback)) + return r; +} + + +template +bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + size_t fw = foreign->m_width; + bool r; + REALM_TEMPEX5(r = compare_leafs_4, cond, action, width, Callback, fw, + (foreign, start, end, baseindex, state, callback)) + return r; +} + + +template +bool Array::compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + cond c; + char* foreign_m_data = foreign->m_data; + + if (width == 0 && foreign_width == 0) { + if (c(0, 0)) { + while (start < end) { + if (!find_action(start + baseindex, 0, state, callback)) + return false; + start++; + } + } + else { + return true; + } + } + + +#if defined(REALM_COMPILER_SSE) + if (sseavx<42>() && width == foreign_width && (width == 8 || width == 16 || width == 32)) { + // We can only use SSE if both bitwidths are equal and above 8 bits and all values are signed + while (start < end && (((reinterpret_cast(m_data) & 0xf) * 8 + start * width) % (128) != 0)) { + int64_t v = get_universal(m_data, start); + int64_t fv = get_universal(foreign_m_data, start); + if (c(v, fv)) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + start++; + } + if (start == end) + return true; + + + size_t sse_items = (end - start) * width / 128; + size_t sse_end = start + sse_items * 128 / no0(width); + + while (start < sse_end) { + __m128i* a = reinterpret_cast<__m128i*>(m_data + start * width / 8); + __m128i* b = reinterpret_cast<__m128i*>(foreign_m_data + start * width / 8); + + bool continue_search = + find_sse_intern(a, b, 1, state, baseindex + start, callback); + + if (!continue_search) + return false; + + start += 128 / no0(width); + } + } +#endif + + while (start < end) { + int64_t v = get_universal(m_data, start); + int64_t fv = get_universal(foreign_m_data, start); + + if (c(v, fv)) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + + start++; + } + + return true; +} + + +template +bool Array::compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + bool ret = false; + + if (std::is_same::value) + ret = compare_equality(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_equality(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_relation(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_relation(value, start, end, baseindex, state, callback); + else + REALM_ASSERT_DEBUG(false); + + return ret; +} + +template +bool Array::compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + uint64_t mask = (bitwidth == 64 ? ~0ULL : ((1ULL << (bitwidth == 64 ? 0 : bitwidth)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + + size_t ee = round_up(start, 64 / no0(bitwidth)); + ee = ee > end ? end : ee; + for (; start < ee; start++) { + if (gt ? (get(start) > value) : (get(start) < value)) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + } + + if (start >= end) + return true; // none found, continue (return true) regardless what find_action() would have returned on match + + const int64_t* p = reinterpret_cast(m_data + (start * bitwidth / 8)); + const int64_t* const e = reinterpret_cast(m_data + (end * bitwidth / 8)) - 1; + + // Matches are rare enough to setup fast linear search for remaining items. We use + // bit hacks from http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + + if (bitwidth == 1 || bitwidth == 2 || bitwidth == 4 || bitwidth == 8 || bitwidth == 16) { + uint64_t magic = find_gtlt_magic(value); + + // Bit hacks only work if searched item has its most significant bit clear for 'greater than' or + // 'item <= 1 << bitwidth' for 'less than' + if (value != int64_t((magic & mask)) && value >= 0 && bitwidth >= 2 && + value <= static_cast((mask >> 1) - (gt ? 1 : 0))) { + // 15 ms + while (p < e) { + uint64_t upper = lower_bits() << (no0(bitwidth) - 1); + + const int64_t v = *p; + size_t idx; + + // Bit hacks only works if all items in chunk have their most significant bit clear. Test this: + upper = upper & v; + + if (!upper) { + idx = find_gtlt_fast( + v, magic, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback); + } + else + idx = find_gtlt( + value, v, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback); + + if (!idx) + return false; + ++p; + } + } + else { + // 24 ms + while (p < e) { + int64_t v = *p; + if (!find_gtlt( + value, v, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback)) + return false; + ++p; + } + } + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth); + } + + // matchcount logic in SIMD no longer pays off for 32/64 bit ints because we have just 4/2 elements + + // Test unaligned end and/or values of width > 16 manually + while (start < end) { + if (gt ? get(start) > value : get(start) < value) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + ++start; + } + return true; +} + +template +size_t Array::find_first(int64_t value, size_t start, size_t end) const +{ + REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + QueryState state; + state.init(act_ReturnFirst, nullptr, + 1); // todo, would be nice to avoid this in order to speed up find_first loops + Finder finder = m_vtable->finder[cond::condition]; + (this->*finder)(value, start, end, 0, &state); + + return static_cast(state.m_state); +} + +//************************************************************************************* +// Finding code ends * +//************************************************************************************* + + +} // namespace realm + +#endif // REALM_ARRAY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic.hpp new file mode 100644 index 0000000..6b9c212 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic.hpp @@ -0,0 +1,120 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BASIC_HPP +#define REALM_ARRAY_BASIC_HPP + +#include + +namespace realm { + +/// A BasicArray can currently only be used for simple unstructured +/// types like float, double. +template +class BasicArray : public Array { +public: + explicit BasicArray(Allocator&) noexcept; + ~BasicArray() noexcept override + { + } + + // Disable copying, this is not allowed. + BasicArray& operator=(const BasicArray&) = delete; + BasicArray(const BasicArray&) = delete; + + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept; + void add(T value); + void set(size_t ndx, T value); + void set_null(size_t ndx); + void insert(size_t ndx, T value); + void erase(size_t ndx); + void truncate(size_t size); + void clear(); + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn* result, T value, size_t add_offset = 0, size_t begin = 0, size_t end = npos) const; + + size_t count(T value, size_t begin = 0, size_t end = npos) const; + bool maximum(T& result, size_t begin = 0, size_t end = npos) const; + bool minimum(T& result, size_t begin = 0, size_t end = npos) const; + + /// Compare two arrays for equality. + bool compare(const BasicArray&) const; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static T get(const char* header, size_t ndx) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, T, TreeInsertBase& state); + + size_t lower_bound(T value) const noexcept; + size_t upper_bound(T value) const noexcept; + + /// Construct a basic array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to `T()`. + static MemRef create_array(size_t size, Allocator&); + + static MemRef create_array(Array::Type leaf_type, bool context_flag, size_t size, T value, Allocator&); + + /// Create a new empty array and attach this accessor to it. This + /// does not modify the parent reference information of this + /// accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(Array::Type = type_Normal, bool context_flag = false); + + /// Construct a copy of the specified slice of this basic array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t size, Allocator& target_alloc) const; + MemRef slice_and_clone_children(size_t offset, size_t size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t find(T target, size_t begin, size_t end) const; + + size_t calc_byte_len(size_t count, size_t width) const override; + virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept override; + + template + bool minmax(T& result, size_t begin, size_t end) const; + + /// Calculate the total number of bytes needed for a basic array + /// with the specified number of elements. This includes the size + /// of the header. The result will be upwards aligned to the + /// closest 8-byte boundary. + static size_t calc_aligned_byte_size(size_t size); +}; + + +// Class typedefs for BasicArray's: ArrayFloat and ArrayDouble +typedef BasicArray ArrayFloat; +typedef BasicArray ArrayDouble; + +} // namespace realm + +#include + +#endif // REALM_ARRAY_BASIC_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp new file mode 100644 index 0000000..dae629c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp @@ -0,0 +1,460 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BASIC_TPL_HPP +#define REALM_ARRAY_BASIC_TPL_HPP + +#include +#include +#include +#include + +#include + +namespace realm { + +template +inline BasicArray::BasicArray(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +template +inline MemRef BasicArray::create_array(size_t init_size, Allocator& allocator) +{ + size_t byte_size_0 = calc_aligned_byte_size(init_size); // Throws + // Adding zero to Array::initial_capacity to avoid taking the + // address of that member + size_t byte_size = std::max(byte_size_0, Array::initial_capacity + 0); // Throws + + MemRef mem = allocator.alloc(byte_size); // Throws + + bool is_inner_bptree_node = false; + bool has_refs = false; + bool context_flag = false; + int width = sizeof(T); + init_header(mem.get_addr(), is_inner_bptree_node, has_refs, context_flag, wtype_Multiply, width, init_size, + byte_size); + + return mem; +} + + +template +inline MemRef BasicArray::create_array(Array::Type type, bool context_flag, size_t init_size, T value, + Allocator& allocator) +{ + REALM_ASSERT(type == Array::type_Normal); + REALM_ASSERT(!context_flag); + MemRef mem = create_array(init_size, allocator); + if (init_size) { + BasicArray tmp(allocator); + tmp.init_from_mem(mem); + T* p = reinterpret_cast(tmp.m_data); + T* end = p + init_size; + while (p < end) { + *p++ = value; + } + } + return mem; +} + + +template +inline void BasicArray::create(Array::Type type, bool context_flag) +{ + REALM_ASSERT(type == Array::type_Normal); + REALM_ASSERT(!context_flag); + size_t length = 0; + MemRef mem = create_array(length, get_alloc()); // Throws + init_from_mem(mem); +} + + +template +MemRef BasicArray::slice(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + REALM_ASSERT(is_attached()); + + // FIXME: This can be optimized as a single contiguous copy + // operation. + BasicArray array_slice(target_alloc); + _impl::ShallowArrayDestroyGuard dg(&array_slice); + array_slice.create(); // Throws + size_t begin = offset; + size_t end = offset + slice_size; + for (size_t i = begin; i != end; ++i) { + T value = get(i); + array_slice.add(value); // Throws + } + dg.release(); + return array_slice.get_mem(); +} + +template +MemRef BasicArray::slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + // BasicArray never contains refs, so never has children. + return slice(offset, slice_size, target_alloc); +} + + +template +inline void BasicArray::add(T value) +{ + insert(m_size, value); +} + + +template +inline T BasicArray::get(size_t ndx) const noexcept +{ + return *(reinterpret_cast(m_data) + ndx); +} + + +template +inline bool BasicArray::is_null(size_t ndx) const noexcept +{ + // FIXME: This assumes BasicArray will only ever be instantiated for float-like T. + static_assert(realm::is_any::value, "T can only be float or double"); + auto x = get(ndx); + return null::is_null_float(x); +} + + +template +inline T BasicArray::get(const char* header, size_t ndx) noexcept +{ + const char* data = get_data_from_header(header); + // This casting assumes that T can be aliged on an 8-bype + // boundary (since data is aligned on an 8-byte boundary.) + return *(reinterpret_cast(data) + ndx); +} + + +template +inline void BasicArray::set(size_t ndx, T value) +{ + REALM_ASSERT_3(ndx, <, m_size); + if (get(ndx) == value) + return; + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // Set the value + T* data = reinterpret_cast(m_data) + ndx; + *data = value; +} + +template +inline void BasicArray::set_null(size_t ndx) +{ + // FIXME: This assumes BasicArray will only ever be instantiated for float-like T. + set(ndx, null::get_null_float()); +} + +template +void BasicArray::insert(size_t ndx, T value) +{ + REALM_ASSERT_3(ndx, <=, m_size); + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // Make room for the new value + alloc(m_size + 1, m_width); // Throws + + // Move values below insertion + if (ndx != m_size) { + char* src_begin = m_data + ndx * m_width; + char* src_end = m_data + m_size * m_width; + char* dst_end = src_end + m_width; + std::copy_backward(src_begin, src_end, dst_end); + } + + // Set the value + T* data = reinterpret_cast(m_data) + ndx; + *data = value; + + ++m_size; +} + +template +void BasicArray::erase(size_t ndx) +{ + REALM_ASSERT_3(ndx, <, m_size); + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // move data under deletion up + if (ndx < m_size - 1) { + char* dst_begin = m_data + ndx * m_width; + const char* src_begin = dst_begin + m_width; + const char* src_end = m_data + m_size * m_width; + realm::safe_copy_n(src_begin, src_end - src_begin, dst_begin); + } + + // Update size (also in header) + --m_size; + set_header_size(m_size); +} + +template +void BasicArray::truncate(size_t to_size) +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT_3(to_size, <=, m_size); + + copy_on_write(); // Throws + + // Update size in accessor and in header. This leaves the capacity + // unchanged. + m_size = to_size; + set_header_size(to_size); +} + +template +inline void BasicArray::clear() +{ + truncate(0); // Throws +} + +template +bool BasicArray::compare(const BasicArray& a) const +{ + size_t n = size(); + if (a.size() != n) + return false; + const T* data_1 = reinterpret_cast(m_data); + const T* data_2 = reinterpret_cast(a.m_data); + return realm::safe_equal(data_1, data_1 + n, data_2); +} + + +template +size_t BasicArray::calc_byte_len(size_t for_size, size_t) const +{ + // FIXME: Consider calling `calc_aligned_byte_size(size)` + // instead. Note however, that calc_byte_len() is supposed to return + // the unaligned byte size. It is probably the case that no harm + // is done by returning the aligned version, and most callers of + // calc_byte_len() will actually benefit if calc_byte_len() was + // changed to always return the aligned byte size. + return header_size + for_size * sizeof(T); +} + +template +size_t BasicArray::calc_item_count(size_t bytes, size_t) const noexcept +{ + size_t bytes_without_header = bytes - header_size; + return bytes_without_header / sizeof(T); +} + +template +size_t BasicArray::find(T value, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + const T* i = std::find(data + begin, data + end, value); + return i == data + end ? not_found : size_t(i - data); +} + +template +inline size_t BasicArray::find_first(T value, size_t begin, size_t end) const +{ + return this->find(value, begin, end); +} + +template +void BasicArray::find_all(IntegerColumn* result, T value, size_t add_offset, size_t begin, size_t end) const +{ + size_t first = begin - 1; + for (;;) { + first = this->find(value, first + 1, end); + if (first == not_found) + break; + + Array::add_to_column(result, first + add_offset); + } +} + +template +size_t BasicArray::count(T value, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + return std::count(data + begin, data + end, value); +} + +#if 0 +// currently unused +template +double BasicArray::sum(size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + return std::accumulate(data + begin, data + end, double(0)); +} +#endif + +template +template +bool BasicArray::minmax(T& result, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + if (m_size == 0) + return false; + REALM_ASSERT(begin < m_size && end <= m_size && begin < end); + + T m = get(begin); + ++begin; + for (; begin < end; ++begin) { + T val = get(begin); + if (find_max ? val > m : val < m) + m = val; + } + result = m; + return true; +} + +template +bool BasicArray::maximum(T& result, size_t begin, size_t end) const +{ + return minmax(result, begin, end); +} + +template +bool BasicArray::minimum(T& result, size_t begin, size_t end) const +{ + return minmax(result, begin, end); +} + + +template +ref_type BasicArray::bptree_leaf_insert(size_t ndx, T value, TreeInsertBase& state) +{ + size_t leaf_size = size(); + REALM_ASSERT_3(leaf_size, <=, REALM_MAX_BPNODE_SIZE); + if (leaf_size < ndx) + ndx = leaf_size; + if (REALM_LIKELY(leaf_size < REALM_MAX_BPNODE_SIZE)) { + insert(ndx, value); + return 0; // Leaf was not split + } + + // Split leaf node + BasicArray new_leaf(get_alloc()); + new_leaf.create(); // Throws + if (ndx == leaf_size) { + new_leaf.add(value); + state.m_split_offset = ndx; + } + else { + // FIXME: Could be optimized by first resizing the target + // array, then copy elements with std::copy(). + for (size_t i = ndx; i != leaf_size; ++i) + new_leaf.add(get(i)); + truncate(ndx); + add(value); + state.m_split_offset = ndx + 1; + } + state.m_split_size = leaf_size + 1; + return new_leaf.get_ref(); +} + +template +inline size_t BasicArray::lower_bound(T value) const noexcept +{ + const T* begin = reinterpret_cast(m_data); + const T* end = begin + size(); + return std::lower_bound(begin, end, value) - begin; +} + +template +inline size_t BasicArray::upper_bound(T value) const noexcept +{ + const T* begin = reinterpret_cast(m_data); + const T* end = begin + size(); + return std::upper_bound(begin, end, value) - begin; +} + +template +inline size_t BasicArray::calc_aligned_byte_size(size_t size) +{ + size_t max = std::numeric_limits::max(); + size_t max_2 = max & ~size_t(7); // Allow for upwards 8-byte alignment + if (size > (max_2 - header_size) / sizeof(T)) + throw std::runtime_error("Byte size overflow"); + size_t byte_size = header_size + size * sizeof(T); + REALM_ASSERT_3(byte_size, >, 0); + size_t aligned_byte_size = ((byte_size - 1) | 7) + 1; // 8-byte alignment + return aligned_byte_size; +} + + +#ifdef REALM_DEBUG + +// LCOV_EXCL_START +template +void BasicArray::to_dot(std::ostream& out, StringData title) const +{ + ref_type ref = get_ref(); + if (title.size() != 0) { + out << "subgraph cluster_" << ref << " {\n"; + out << " label = \"" << title << "\";\n"; + out << " color = white;\n"; + } + + out << "n" << std::hex << ref << std::dec << "[shape=none,label=<"; + out << "
\n"; + + // Header + out << "\n"; + + // Values + size_t n = m_size; + for (size_t i = 0; i != n; ++i) + out << "\n"; + + out << "
"; + out << "0x" << std::hex << ref << std::dec << "
"; + out << "
" << get(i) << "
>];\n"; + + if (title.size() != 0) + out << "}\n"; + + to_dot_parent_edge(out); +} +// LCOV_EXCL_STOP + +#endif // REALM_DEBUG + + +} // namespace realm + +#endif // REALM_ARRAY_BASIC_TPL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_binary.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_binary.hpp new file mode 100644 index 0000000..41b4626 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_binary.hpp @@ -0,0 +1,259 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BINARY_HPP +#define REALM_ARRAY_BINARY_HPP + +#include +#include +#include +#include + +namespace realm { + +/* +STORAGE FORMAT +--------------------------------------------------------------------------------------- +ArrayBinary stores binary elements using two ArrayInteger and one ArrayBlob. The ArrayBlob can only store one +single concecutive array of bytes (contrary to its 'Array' name that misleadingly indicates it could store multiple +elements). + +Assume we have the strings "a", "", "abc", null, "ab". Then the three arrays will contain: + +ArrayInteger m_offsets 1, 1, 5, 5, 6 +ArrayBlob m_blob aabcab +ArrayInteger m_nulls 0, 0, 0, 1, 0 // 1 indicates null, 0 indicates non-null + +So for each element the ArrayInteger, the ArrayInteger points into the ArrayBlob at the position of the first +byte of the next element. + +m_nulls is always present (except for old database files; see below), so any ArrayBinary is always nullable! +The nullable property (such as throwing exception upon set(null) on non-nullable column, etc) is handled on +column level only. + +DATABASE FILE VERSION CHANGES +--------------------------------------------------------------------------------------- +Old database files do not have any m_nulls array. To be backwardscompatible, many methods will have tests like +`if(Array::size() == 3)` and have a backwards compatible code paths for these (e.g. avoid writing to m_nulls +in set(), etc). This way no file format upgrade is needed to support nulls for BinaryData. +*/ + +class ArrayBinary : public Array { +public: + explicit ArrayBinary(Allocator&) noexcept; + ~ArrayBinary() noexcept override + { + } + + // Disable copying, this is not allowed. + ArrayBinary& operator=(const ArrayBinary&) = delete; + ArrayBinary(const ArrayBinary&) = delete; + + /// Create a new empty binary array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + // Old database files will not have the m_nulls array, so we need code paths for + // backwards compatibility for these cases. + bool legacy_array_type() const noexcept; + + //@{ + /// Overriding functions of Array + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + //@} + + bool is_empty() const noexcept; + size_t size() const noexcept; + + BinaryData get(size_t ndx) const noexcept; + size_t read(size_t ndx, size_t pos, char* buffer, size_t max_size) const noexcept; + + void add(BinaryData value, bool add_zero_term = false); + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void insert(size_t ndx, BinaryData value, bool add_zero_term = false); + void erase(size_t ndx); + void truncate(size_t new_size); + void clear(); + void destroy(); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static BinaryData get(const char* header, size_t ndx, Allocator&) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, BinaryData, bool add_zero_term, TreeInsertBase& state); + + static size_t get_size_from_header(const char*, Allocator&) noexcept; + + /// Construct a binary array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to the binary value `defaults`, which can be either + /// null or zero-length non-null (value with size > 0 is not allowed as + /// initialization value). + static MemRef create_array(size_t size, Allocator&, BinaryData defaults); + + /// Construct a copy of the specified slice of this binary array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, bool is_strings, StringData title = StringData()) const; +#endif + bool update_from_parent(size_t old_baseline) noexcept; + +private: + ArrayInteger m_offsets; + ArrayBlob m_blob; + ArrayInteger m_nulls; +}; + + +// Implementation: + +inline ArrayBinary::ArrayBinary(Allocator& allocator) noexcept + : Array(allocator) + , m_offsets(allocator) + , m_blob(allocator) + , m_nulls(allocator) +{ + m_offsets.set_parent(this, 0); + m_blob.set_parent(this, 1); + m_nulls.set_parent(this, 2); +} + +inline void ArrayBinary::create() +{ + size_t init_size = 0; + BinaryData defaults = BinaryData{}; // This init value is ignored because size = 0 + MemRef mem = create_array(init_size, get_alloc(), defaults); // Throws + init_from_mem(mem); +} + +inline void ArrayBinary::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT(ref); + char* header = get_alloc().translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); +} + +inline void ArrayBinary::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + +inline bool ArrayBinary::is_empty() const noexcept +{ + return m_offsets.is_empty(); +} + +// Old database files will not have the m_nulls array, so we need code paths for +// backwards compatibility for these cases. We can test if m_nulls exists by looking +// at number of references in this ArrayBinary. +inline bool ArrayBinary::legacy_array_type() const noexcept +{ + if (Array::size() == 3) + return false; // New database file + else if (Array::size() == 2) + return true; // Old database file + else + REALM_ASSERT(false); // Should never happen + return false; +} + +inline size_t ArrayBinary::size() const noexcept +{ + return m_offsets.size(); +} + +inline BinaryData ArrayBinary::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_offsets.size()); + + if (!legacy_array_type() && m_nulls.get(ndx)) { + return BinaryData(); + } + else { + size_t begin = ndx ? to_size_t(m_offsets.get(ndx - 1)) : 0; + size_t end = to_size_t(m_offsets.get(ndx)); + + BinaryData bd = BinaryData(m_blob.get(begin), end - begin); + // Old database file (non-nullable column should never return null) + REALM_ASSERT(!bd.is_null()); + return bd; + } +} + +inline void ArrayBinary::truncate(size_t new_size) +{ + REALM_ASSERT_3(new_size, <, m_offsets.size()); + + size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + + m_offsets.truncate(new_size); + m_blob.truncate(blob_size); + if (!legacy_array_type()) + m_nulls.truncate(new_size); +} + +inline void ArrayBinary::clear() +{ + m_blob.clear(); + m_offsets.clear(); + if (!legacy_array_type()) + m_nulls.clear(); +} + +inline void ArrayBinary::destroy() +{ + m_blob.destroy(); + m_offsets.destroy(); + if (!legacy_array_type()) + m_nulls.destroy(); + Array::destroy(); +} + +inline size_t ArrayBinary::get_size_from_header(const char* header, Allocator& alloc) noexcept +{ + ref_type offsets_ref = to_ref(Array::get(header, 0)); + const char* offsets_header = alloc.translate(offsets_ref); + return Array::get_size_from_header(offsets_header); +} + +inline bool ArrayBinary::update_from_parent(size_t old_baseline) noexcept +{ + bool res = Array::update_from_parent(old_baseline); + if (res) { + m_blob.update_from_parent(old_baseline); + m_offsets.update_from_parent(old_baseline); + if (!legacy_array_type()) + m_nulls.update_from_parent(old_baseline); + } + return res; +} + +} // namespace realm + +#endif // REALM_ARRAY_BINARY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blob.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blob.hpp new file mode 100644 index 0000000..3ee6e54 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blob.hpp @@ -0,0 +1,147 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BLOB_HPP +#define REALM_ARRAY_BLOB_HPP + +#include + +namespace realm { + + +class ArrayBlob : public Array { +public: + static constexpr size_t max_binary_size = 0xFFFFF8 - Array::header_size; + + explicit ArrayBlob(Allocator&) noexcept; + ~ArrayBlob() noexcept override + { + } + + // Disable copying, this is not allowed. + ArrayBlob& operator=(const ArrayBlob&) = delete; + ArrayBlob(const ArrayBlob&) = delete; + + const char* get(size_t index) const noexcept; + BinaryData get_at(size_t& pos) const noexcept; + bool is_null(size_t index) const noexcept; + ref_type add(const char* data, size_t data_size, bool add_zero_term = false); + void insert(size_t pos, const char* data, size_t data_size, bool add_zero_term = false); + ref_type replace(size_t begin, size_t end, const char* data, size_t data_size, bool add_zero_term = false); + void erase(size_t begin, size_t end); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static const char* get(const char* header, size_t index) noexcept; + + /// Create a new empty blob (binary) array and attach this + /// accessor to it. This does not modify the parent reference + /// information of this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a blob of the specified size and return just the + /// reference to the underlying memory. All bytes will be + /// initialized to zero. + static MemRef create_array(size_t init_size, Allocator&); + + size_t blob_size() const noexcept; +#ifdef REALM_DEBUG + void verify() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t calc_byte_len(size_t for_size, size_t width) const override; + size_t calc_item_count(size_t bytes, size_t width) const noexcept override; +}; + + +// Implementation: + +// Creates new array (but invalid, call init_from_ref() to init) +inline ArrayBlob::ArrayBlob(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +inline bool ArrayBlob::is_null(size_t index) const noexcept +{ + return (get(index) == nullptr); +} + +inline const char* ArrayBlob::get(size_t index) const noexcept +{ + return m_data + index; +} + +inline ref_type ArrayBlob::add(const char* data, size_t data_size, bool add_zero_term) +{ + return replace(m_size, m_size, data, data_size, add_zero_term); +} + +inline void ArrayBlob::insert(size_t pos, const char* data, size_t data_size, bool add_zero_term) +{ + replace(pos, pos, data, data_size, add_zero_term); +} + +inline void ArrayBlob::erase(size_t begin, size_t end) +{ + const char* data = nullptr; + size_t data_size = 0; + replace(begin, end, data, data_size); +} + +inline const char* ArrayBlob::get(const char* header, size_t pos) noexcept +{ + const char* data = get_data_from_header(header); + return data + pos; +} + +inline void ArrayBlob::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc()); // Throws + init_from_mem(mem); +} + +inline MemRef ArrayBlob::create_array(size_t init_size, Allocator& allocator) +{ + bool context_flag = false; + int_fast64_t value = 0; + return Array::create(type_Normal, context_flag, wtype_Ignore, init_size, value, allocator); // Throws +} + +inline size_t ArrayBlob::calc_byte_len(size_t for_size, size_t) const +{ + return header_size + for_size; +} + +inline size_t ArrayBlob::calc_item_count(size_t bytes, size_t) const noexcept +{ + return bytes - header_size; +} + + +} // namespace realm + +#endif // REALM_ARRAY_BLOB_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp new file mode 100644 index 0000000..5e0d520 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp @@ -0,0 +1,222 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BIG_BLOBS_HPP +#define REALM_ARRAY_BIG_BLOBS_HPP + +#include + +namespace realm { + + +class ArrayBigBlobs : public Array { +public: + typedef BinaryData value_type; + + explicit ArrayBigBlobs(Allocator&, bool nullable) noexcept; + + // Disable copying, this is not allowed. + ArrayBigBlobs& operator=(const ArrayBigBlobs&) = delete; + ArrayBigBlobs(const ArrayBigBlobs&) = delete; + + BinaryData get(size_t ndx) const noexcept; + BinaryData get_at(size_t ndx, size_t& pos) const noexcept; + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void add(BinaryData value, bool add_zero_term = false); + void insert(size_t ndx, BinaryData value, bool add_zero_term = false); + void erase(size_t ndx); + void truncate(size_t new_size); + void clear(); + void destroy(); + + size_t count(BinaryData value, bool is_string = false, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(BinaryData value, bool is_string = false, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, BinaryData value, bool is_string = false, size_t add_offset = 0, + size_t begin = 0, size_t end = npos); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static BinaryData get(const char* header, size_t ndx, Allocator&) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, BinaryData, bool add_zero_term, TreeInsertBase& state); + + //@{ + /// Those that return a string, discard the terminating zero from + /// the stored value. Those that accept a string argument, add a + /// terminating zero before storing the value. + StringData get_string(size_t ndx) const noexcept; + void add_string(StringData value); + void set_string(size_t ndx, StringData value); + void insert_string(size_t ndx, StringData value); + static StringData get_string(const char* header, size_t ndx, Allocator&, bool nullable) noexcept; + ref_type bptree_leaf_insert_string(size_t ndx, StringData, TreeInsertBase& state); + //@} + + /// Create a new empty big blobs array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a copy of the specified slice of this big blobs + /// array using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void verify() const; + void to_dot(std::ostream&, bool is_strings, StringData title = StringData()) const; +#endif + +private: + bool m_nullable; +}; + + +// Implementation: + +inline ArrayBigBlobs::ArrayBigBlobs(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_nullable(nullable) +{ +} + +inline BinaryData ArrayBigBlobs::get(size_t ndx) const noexcept +{ + ref_type ref = get_as_ref(ndx); + if (ref == 0) + return {}; // realm::null(); + + const char* blob_header = get_alloc().translate(ref); + if (!get_context_flag_from_header(blob_header)) { + const char* value = ArrayBlob::get(blob_header, 0); + size_t blob_size = get_size_from_header(blob_header); + return BinaryData(value, blob_size); + } + return {}; +} + +inline BinaryData ArrayBigBlobs::get(const char* header, size_t ndx, Allocator& alloc) noexcept +{ + ref_type blob_ref = to_ref(Array::get(header, ndx)); + if (blob_ref == 0) + return {}; + + const char* blob_header = alloc.translate(blob_ref); + if (!get_context_flag_from_header(blob_header)) { + const char* blob_data = Array::get_data_from_header(blob_header); + size_t blob_size = Array::get_size_from_header(blob_header); + return BinaryData(blob_data, blob_size); + } + return {}; +} + +inline void ArrayBigBlobs::erase(size_t ndx) +{ + ref_type blob_ref = Array::get_as_ref(ndx); + if (blob_ref != 0) { // nothing to destroy if null + Array::destroy_deep(blob_ref, get_alloc()); // Deep + } + Array::erase(ndx); +} + +inline void ArrayBigBlobs::truncate(size_t new_size) +{ + Array::truncate_and_destroy_children(new_size); +} + +inline void ArrayBigBlobs::clear() +{ + Array::clear_and_destroy_children(); +} + +inline void ArrayBigBlobs::destroy() +{ + Array::destroy_deep(); +} + +inline StringData ArrayBigBlobs::get_string(size_t ndx) const noexcept +{ + BinaryData bin = get(ndx); + if (bin.is_null()) + return realm::null(); + else + return StringData(bin.data(), bin.size() - 1); // Do not include terminating zero +} + +inline void ArrayBigBlobs::set_string(size_t ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + set(ndx, bin, add_zero_term); +} + +inline void ArrayBigBlobs::add_string(StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + add(bin, add_zero_term); +} + +inline void ArrayBigBlobs::insert_string(size_t ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + insert(ndx, bin, add_zero_term); +} + +inline StringData ArrayBigBlobs::get_string(const char* header, size_t ndx, Allocator& alloc, bool nullable) noexcept +{ + static_cast(nullable); + BinaryData bin = get(header, ndx, alloc); + REALM_ASSERT_DEBUG(!(!nullable && bin.is_null())); + if (bin.is_null()) + return realm::null(); + else + return StringData(bin.data(), bin.size() - 1); // Do not include terminating zero +} + +inline ref_type ArrayBigBlobs::bptree_leaf_insert_string(size_t ndx, StringData value, TreeInsertBase& state) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + return bptree_leaf_insert(ndx, bin, add_zero_term, state); +} + +inline void ArrayBigBlobs::create() +{ + bool context_flag = true; + Array::create(type_HasRefs, context_flag); // Throws +} + +inline MemRef ArrayBigBlobs::slice(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + return slice_and_clone_children(offset, slice_size, target_alloc); +} + + +} // namespace realm + +#endif // REALM_ARRAY_BIG_BLOBS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_direct.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_direct.hpp new file mode 100644 index 0000000..e337392 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_direct.hpp @@ -0,0 +1,372 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_DIRECT_HPP +#define REALM_ARRAY_DIRECT_HPP + +#include +#include + +using namespace realm::util; + +// clang-format off +/* wid == 16/32 likely when accessing offsets in B tree */ +#define REALM_TEMPEX(fun, wid, arg) \ + if (wid == 16) {fun<16> arg;} \ + else if (wid == 32) {fun<32> arg;} \ + else if (wid == 0) {fun<0> arg;} \ + else if (wid == 1) {fun<1> arg;} \ + else if (wid == 2) {fun<2> arg;} \ + else if (wid == 4) {fun<4> arg;} \ + else if (wid == 8) {fun<8> arg;} \ + else if (wid == 64) {fun<64> arg;} \ + else {REALM_ASSERT_DEBUG(false); fun<0> arg;} + +#define REALM_TEMPEX2(fun, targ, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX3(fun, targ1, targ2, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX4(fun, targ1, targ2, wid, targ3, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX5(fun, targ1, targ2, targ3, targ4, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} +// clang-format on + +namespace realm { + +// Direct access methods + +template +void set_direct(char* data, size_t ndx, int_fast64_t value) noexcept +{ + if (width == 0) { + REALM_ASSERT_DEBUG(value == 0); + return; + } + else if (width == 1) { + REALM_ASSERT_DEBUG(0 <= value && value <= 0x01); + size_t byte_ndx = ndx / 8; + size_t bit_ndx = ndx % 8; + typedef unsigned char uchar; + uchar* p = reinterpret_cast(data) + byte_ndx; + *p = uchar((*p & ~(0x01 << bit_ndx)) | (int(value) & 0x01) << bit_ndx); + } + else if (width == 2) { + REALM_ASSERT_DEBUG(0 <= value && value <= 0x03); + size_t byte_ndx = ndx / 4; + size_t bit_ndx = ndx % 4 * 2; + typedef unsigned char uchar; + uchar* p = reinterpret_cast(data) + byte_ndx; + *p = uchar((*p & ~(0x03 << bit_ndx)) | (int(value) & 0x03) << bit_ndx); + } + else if (width == 4) { + REALM_ASSERT_DEBUG(0 <= value && value <= 0x0F); + size_t byte_ndx = ndx / 2; + size_t bit_ndx = ndx % 2 * 4; + typedef unsigned char uchar; + uchar* p = reinterpret_cast(data) + byte_ndx; + *p = uchar((*p & ~(0x0F << bit_ndx)) | (int(value) & 0x0F) << bit_ndx); + } + else if (width == 8) { + REALM_ASSERT_DEBUG(std::numeric_limits::min() <= value && + value <= std::numeric_limits::max()); + *(reinterpret_cast(data) + ndx) = int8_t(value); + } + else if (width == 16) { + REALM_ASSERT_DEBUG(std::numeric_limits::min() <= value && + value <= std::numeric_limits::max()); + *(reinterpret_cast(data) + ndx) = int16_t(value); + } + else if (width == 32) { + REALM_ASSERT_DEBUG(std::numeric_limits::min() <= value && + value <= std::numeric_limits::max()); + *(reinterpret_cast(data) + ndx) = int32_t(value); + } + else if (width == 64) { + REALM_ASSERT_DEBUG(std::numeric_limits::min() <= value && + value <= std::numeric_limits::max()); + *(reinterpret_cast(data) + ndx) = int64_t(value); + } + else { + REALM_ASSERT_DEBUG(false); + } +} + +template +void fill_direct(char* data, size_t begin, size_t end, int_fast64_t value) noexcept +{ + for (size_t i = begin; i != end; ++i) + set_direct(data, i, value); +} + +template +int64_t get_direct(const char* data, size_t ndx) noexcept +{ + if (w == 0) { + return 0; + } + if (w == 1) { + size_t offset = ndx >> 3; + return (data[offset] >> (ndx & 7)) & 0x01; + } + if (w == 2) { + size_t offset = ndx >> 2; + return (data[offset] >> ((ndx & 3) << 1)) & 0x03; + } + if (w == 4) { + size_t offset = ndx >> 1; + return (data[offset] >> ((ndx & 1) << 2)) & 0x0F; + } + if (w == 8) { + return *reinterpret_cast(data + ndx); + } + if (w == 16) { + size_t offset = ndx * 2; + return *reinterpret_cast(data + offset); + } + if (w == 32) { + size_t offset = ndx * 4; + return *reinterpret_cast(data + offset); + } + if (w == 64) { + size_t offset = ndx * 8; + return *reinterpret_cast(data + offset); + } + REALM_ASSERT_DEBUG(false); + return int64_t(-1); +} + +inline int64_t get_direct(const char* data, size_t width, size_t ndx) noexcept +{ + REALM_TEMPEX(return get_direct, width, (data, ndx)); +} + + +template +inline std::pair get_two(const char* data, size_t ndx) noexcept +{ + return std::make_pair(to_size_t(get_direct(data, ndx + 0)), to_size_t(get_direct(data, ndx + 1))); +} + +inline std::pair get_two(const char* data, size_t width, size_t ndx) noexcept +{ + REALM_TEMPEX(return get_two, width, (data, ndx)); +} + + +template +inline void get_three(const char* data, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept +{ + v0 = to_ref(get_direct(data, ndx + 0)); + v1 = to_ref(get_direct(data, ndx + 1)); + v2 = to_ref(get_direct(data, ndx + 2)); +} + +inline void get_three(const char* data, size_t width, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept +{ + REALM_TEMPEX(get_three, width, (data, ndx, v0, v1, v2)); +} + + +// Lower/upper bound in sorted sequence +// ------------------------------------ +// +// 3 3 3 4 4 4 5 6 7 9 9 9 +// ^ ^ ^ ^ ^ +// | | | | | +// | | | | -- Lower and upper bound of 15 +// | | | | +// | | | -- Lower and upper bound of 8 +// | | | +// | | -- Upper bound of 4 +// | | +// | -- Lower bound of 4 +// | +// -- Lower and upper bound of 1 +// +// These functions are semantically identical to std::lower_bound() and +// std::upper_bound(). +// +// We currently use binary search. See for example +// http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary. +template +inline size_t lower_bound(const char* data, size_t size, int64_t value) noexcept +{ + // The binary search used here is carefully optimized. Key trick is to use a single + // loop controlling variable (size) instead of high/low pair, and to keep updates + // to size done inside the loop independent of comparisons. Further key to speed + // is to avoid branching inside the loop, using conditional moves instead. This + // provides robust performance for random searches, though predictable searches + // might be slightly faster if we used branches instead. The loop unrolling yields + // a final 5-20% speedup depending on circumstances. + + size_t low = 0; + + while (size >= 8) { + // The following code (at X, Y and Z) is 3 times manually unrolled instances of (A) below. + // These code blocks must be kept in sync. Meassurements indicate 3 times unrolling to give + // the best performance. See (A) for comments on the loop body. + // (X) + size_t half = size / 2; + size_t other_half = size - half; + size_t probe = low + half; + size_t other_low = low + other_half; + int64_t v = get_direct(data, probe); + size = half; + low = (v < value) ? other_low : low; + + // (Y) + half = size / 2; + other_half = size - half; + probe = low + half; + other_low = low + other_half; + v = get_direct(data, probe); + size = half; + low = (v < value) ? other_low : low; + + // (Z) + half = size / 2; + other_half = size - half; + probe = low + half; + other_low = low + other_half; + v = get_direct(data, probe); + size = half; + low = (v < value) ? other_low : low; + } + while (size > 0) { + // (A) + // To understand the idea in this code, please note that + // for performance, computation of size for the next iteration + // MUST be INDEPENDENT of the conditional. This allows the + // processor to unroll the loop as fast as possible, and it + // minimizes the length of dependence chains leading up to branches. + // Making the unfolding of the loop independent of the data being + // searched, also minimizes the delays incurred by branch + // mispredictions, because they can be determined earlier + // and the speculation corrected earlier. + + // Counterintuitive: + // To make size independent of data, we cannot always split the + // range at the theoretical optimal point. When we determine that + // the key is larger than the probe at some index K, and prepare + // to search the upper part of the range, you would normally start + // the search at the next index, K+1, to get the shortest range. + // We can only do this when splitting a range with odd number of entries. + // If there is an even number of entries we search from K instead of K+1. + // This potentially leads to redundant comparisons, but in practice we + // gain more performance by making the changes to size predictable. + + // if size is even, half and other_half are the same. + // if size is odd, half is one less than other_half. + size_t half = size / 2; + size_t other_half = size - half; + size_t probe = low + half; + size_t other_low = low + other_half; + int64_t v = get_direct(data, probe); + size = half; + // for max performance, the line below should compile into a conditional + // move instruction. Not all compilers do this. To maximize chance + // of succes, no computation should be done in the branches of the + // conditional. + low = (v < value) ? other_low : low; + }; + + return low; +} + +// See lower_bound() +template +inline size_t upper_bound(const char* data, size_t size, int64_t value) noexcept +{ + size_t low = 0; + while (size >= 8) { + size_t half = size / 2; + size_t other_half = size - half; + size_t probe = low + half; + size_t other_low = low + other_half; + int64_t v = get_direct(data, probe); + size = half; + low = (value >= v) ? other_low : low; + + half = size / 2; + other_half = size - half; + probe = low + half; + other_low = low + other_half; + v = get_direct(data, probe); + size = half; + low = (value >= v) ? other_low : low; + + half = size / 2; + other_half = size - half; + probe = low + half; + other_low = low + other_half; + v = get_direct(data, probe); + size = half; + low = (value >= v) ? other_low : low; + } + + while (size > 0) { + size_t half = size / 2; + size_t other_half = size - half; + size_t probe = low + half; + size_t other_low = low + other_half; + int64_t v = get_direct(data, probe); + size = half; + low = (value >= v) ? other_low : low; + }; + + return low; +} +} + +#endif /* ARRAY_TPL_HPP_ */ diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_integer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_integer.hpp new file mode 100644 index 0000000..e460c53 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_integer.hpp @@ -0,0 +1,629 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_INTEGER_HPP +#define REALM_ARRAY_INTEGER_HPP + +#include +#include +#include + +namespace realm { + +class ArrayInteger : public Array { +public: + typedef int64_t value_type; + + explicit ArrayInteger(Allocator&) noexcept; + ~ArrayInteger() noexcept override + { + } + + // Disable copying, this is not allowed. + ArrayInteger& operator=(const ArrayInteger&) = delete; + ArrayInteger(const ArrayInteger&) = delete; + + void create(Type type = type_Normal, bool context_flag = false); + + void add(int64_t value); + void set(size_t ndx, int64_t value); + void set_uint(size_t ndx, uint_fast64_t value) noexcept; + int64_t get(size_t ndx) const noexcept; + uint64_t get_uint(size_t ndx) const noexcept; + static int64_t get(const char* header, size_t ndx) noexcept; + bool compare(const ArrayInteger& a) const noexcept; + + /// Add \a diff to the element at the specified index. + void adjust(size_t ndx, int_fast64_t diff); + + /// Add \a diff to all the elements in the specified index range. + void adjust(size_t begin, size_t end, int_fast64_t diff); + + /// Add signed \a diff to all elements that are greater than, or equal to \a + /// limit. + void adjust_ge(int_fast64_t limit, int_fast64_t diff); + + int64_t operator[](size_t ndx) const noexcept + { + return get(ndx); + } + int64_t front() const noexcept; + int64_t back() const noexcept; + + size_t lower_bound(int64_t value) const noexcept; + size_t upper_bound(int64_t value) const noexcept; + + std::vector to_vector() const; + +private: + template + bool minmax(size_t from, size_t to, uint64_t maxdiff, int64_t* min, int64_t* max) const; +}; + +class ArrayIntNull : public Array { +public: + using value_type = util::Optional; + + explicit ArrayIntNull(Allocator&) noexcept; + ~ArrayIntNull() noexcept override; + + /// Construct an array of the specified type and size, and return just the + /// reference to the underlying memory. All elements will be initialized to + /// the specified value. + static MemRef create_array(Type, bool context_flag, size_t size, value_type value, Allocator&); + void create(Type = type_Normal, bool context_flag = false); + + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + + size_t size() const noexcept; + bool is_empty() const noexcept; + + void insert(size_t ndx, value_type value); + void add(value_type value); + void set(size_t ndx, value_type value) noexcept; + value_type get(size_t ndx) const noexcept; + static value_type get(const char* header, size_t ndx) noexcept; + void get_chunk(size_t ndx, value_type res[8]) const noexcept; + void set_null(size_t ndx) noexcept; + bool is_null(size_t ndx) const noexcept; + int64_t null_value() const noexcept; + + value_type operator[](size_t ndx) const noexcept; + value_type front() const noexcept; + value_type back() const noexcept; + void erase(size_t ndx); + void erase(size_t begin, size_t end); + void truncate(size_t size); + void clear(); + void set_all_to_zero(); + + void move(size_t begin, size_t end, size_t dest_begin); + void move_backward(size_t begin, size_t end, size_t dest_end); + + size_t lower_bound(int64_t value) const noexcept; + size_t upper_bound(int64_t value) const noexcept; + + int64_t sum(size_t start = 0, size_t end = npos) const; + size_t count(int64_t value) const noexcept; + bool maximum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + bool minimum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + + bool find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex, + QueryState* state) const; + + template + bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // This is the one installed into the m_finder slots. + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Optimized implementation for release mode + template + bool find_optimized(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Called for each search result + template + bool find_action(size_t index, value_type value, QueryState* state, Callback callback) const; + + template + bool find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const; + + // Wrappers for backwards compatibility and for simple use without + // setting up state initialization etc + template + size_t find_first(value_type value, size_t start = 0, size_t end = npos) const; + + void find_all(IntegerColumn* result, value_type value, size_t col_offset = 0, size_t begin = 0, + size_t end = npos) const; + + + size_t find_first(value_type value, size_t begin = 0, size_t end = npos) const; + + + // Overwrite Array::bptree_leaf_insert to correctly split nodes. + ref_type bptree_leaf_insert(size_t ndx, value_type value, TreeInsertBase& state); + + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + /// Construct a deep copy of the specified slice of this array using the + /// specified target allocator. Subarrays will be cloned. + MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +protected: + void avoid_null_collision(int64_t value); + +private: + template + bool minmax_helper(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + + int_fast64_t choose_random_null(int64_t incoming) const; + void replace_nulls_with(int64_t new_null); + bool can_use_as_null(int64_t value) const; +}; + + +// Implementation: + +inline ArrayInteger::ArrayInteger(Allocator& allocator) noexcept + : Array(allocator) +{ + m_is_inner_bptree_node = false; +} + +inline void ArrayInteger::add(int64_t value) +{ + Array::add(value); +} + +inline int64_t ArrayInteger::get(size_t ndx) const noexcept +{ + return Array::get(ndx); +} + +inline int64_t ArrayInteger::get(const char* header, size_t ndx) noexcept +{ + return Array::get(header, ndx); +} + +inline void ArrayInteger::set(size_t ndx, int64_t value) +{ + Array::set(ndx, value); +} + +inline void ArrayInteger::set_uint(size_t ndx, uint_fast64_t value) noexcept +{ + // When a value of a signed type is converted to an unsigned type, the C++ + // standard guarantees that negative values are converted from the native + // representation to 2's complement, but the effect of conversions in the + // opposite direction is left unspecified by the + // standard. `realm::util::from_twos_compl()` is used here to perform the + // correct opposite unsigned-to-signed conversion, which reduces to a no-op + // when 2's complement is the native representation of negative values. + set(ndx, util::from_twos_compl(value)); +} + +inline bool ArrayInteger::compare(const ArrayInteger& a) const noexcept +{ + if (a.size() != size()) + return false; + + for (size_t i = 0; i < size(); ++i) { + if (get(i) != a.get(i)) + return false; + } + + return true; +} + +inline int64_t ArrayInteger::front() const noexcept +{ + return Array::front(); +} + +inline int64_t ArrayInteger::back() const noexcept +{ + return Array::back(); +} + +inline void ArrayInteger::adjust(size_t ndx, int_fast64_t diff) +{ + Array::adjust(ndx, diff); +} + +inline void ArrayInteger::adjust(size_t begin, size_t end, int_fast64_t diff) +{ + Array::adjust(begin, end, diff); +} + +inline void ArrayInteger::adjust_ge(int_fast64_t limit, int_fast64_t diff) +{ + Array::adjust_ge(limit, diff); +} + +inline size_t ArrayInteger::lower_bound(int64_t value) const noexcept +{ + return lower_bound_int(value); +} + +inline size_t ArrayInteger::upper_bound(int64_t value) const noexcept +{ + return upper_bound_int(value); +} + + +inline ArrayIntNull::ArrayIntNull(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +inline ArrayIntNull::~ArrayIntNull() noexcept +{ +} + +inline void ArrayIntNull::create(Type type, bool context_flag) +{ + MemRef r = create_array(type, context_flag, 0, util::none, m_alloc); + init_from_mem(r); +} + + +inline size_t ArrayIntNull::size() const noexcept +{ + return Array::size() - 1; +} + +inline bool ArrayIntNull::is_empty() const noexcept +{ + return size() == 0; +} + +inline void ArrayIntNull::insert(size_t ndx, value_type value) +{ + if (value) { + avoid_null_collision(*value); + Array::insert(ndx + 1, *value); + } + else { + Array::insert(ndx + 1, null_value()); + } +} + +inline void ArrayIntNull::add(value_type value) +{ + if (value) { + avoid_null_collision(*value); + Array::add(*value); + } + else { + Array::add(null_value()); + } +} + +inline void ArrayIntNull::set(size_t ndx, value_type value) noexcept +{ + if (value) { + avoid_null_collision(*value); + Array::set(ndx + 1, *value); + } + else { + Array::set(ndx + 1, null_value()); + } +} + +inline void ArrayIntNull::set_null(size_t ndx) noexcept +{ + Array::set(ndx + 1, null_value()); +} + +inline ArrayIntNull::value_type ArrayIntNull::get(size_t ndx) const noexcept +{ + int64_t value = Array::get(ndx + 1); + if (value == null_value()) { + return util::none; + } + return util::some(value); +} + +inline ArrayIntNull::value_type ArrayIntNull::get(const char* header, size_t ndx) noexcept +{ + int64_t null_value = Array::get(header, 0); + int64_t value = Array::get(header, ndx + 1); + if (value == null_value) { + return util::none; + } + else { + return util::some(value); + } +} + +inline bool ArrayIntNull::is_null(size_t ndx) const noexcept +{ + return !get(ndx); +} + +inline int64_t ArrayIntNull::null_value() const noexcept +{ + return Array::get(0); +} + +inline ArrayIntNull::value_type ArrayIntNull::operator[](size_t ndx) const noexcept +{ + return get(ndx); +} + +inline ArrayIntNull::value_type ArrayIntNull::front() const noexcept +{ + return get(0); +} + +inline ArrayIntNull::value_type ArrayIntNull::back() const noexcept +{ + return Array::back(); +} + +inline void ArrayIntNull::erase(size_t ndx) +{ + Array::erase(ndx + 1); +} + +inline void ArrayIntNull::erase(size_t begin, size_t end) +{ + Array::erase(begin + 1, end + 1); +} + +inline void ArrayIntNull::truncate(size_t to_size) +{ + Array::truncate(to_size + 1); +} + +inline void ArrayIntNull::clear() +{ + truncate(0); +} + +inline void ArrayIntNull::set_all_to_zero() +{ + // FIXME: Array::set_all_to_zero does something else + for (size_t i = 0; i < size(); ++i) { + set(i, 0); + } +} + +inline void ArrayIntNull::move(size_t begin, size_t end, size_t dest_begin) +{ + Array::move(begin + 1, end + 1, dest_begin + 1); +} + +inline void ArrayIntNull::move_backward(size_t begin, size_t end, size_t dest_end) +{ + Array::move_backward(begin + 1, end + 1, dest_end + 1); +} + +inline size_t ArrayIntNull::lower_bound(int64_t value) const noexcept +{ + // FIXME: Consider this behaviour with NULLs. + // Array::lower_bound_int assumes an already sorted array, but + // this array could be sorted with nulls first or last. + return Array::lower_bound_int(value); +} + +inline size_t ArrayIntNull::upper_bound(int64_t value) const noexcept +{ + // FIXME: see lower_bound + return Array::upper_bound_int(value); +} + +inline int64_t ArrayIntNull::sum(size_t start, size_t end) const +{ + // FIXME: Optimize + int64_t sum_of_range = 0; + if (end == npos) + end = size(); + for (size_t i = start; i < end; ++i) { + value_type x = get(i); + if (x) { + sum_of_range += *x; + } + } + return sum_of_range; +} + +inline size_t ArrayIntNull::count(int64_t value) const noexcept +{ + size_t count_of_value = Array::count(value); + if (value == null_value()) { + --count_of_value; + } + return count_of_value; +} + +// FIXME: Optimize +template +inline bool ArrayIntNull::minmax_helper(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + size_t best_index = 1; + + if (end == npos) { + end = m_size; + } + + ++start; + + REALM_ASSERT(start < m_size && end <= m_size && start < end); + + if (m_size == 1) { + // empty array + return false; + } + + if (m_width == 0) { + if (return_ndx) + *return_ndx = best_index - 1; + result = 0; + return true; + } + + int64_t m = Array::get(start); + + const int64_t null_val = null_value(); + for (; start < end; ++start) { + const int64_t v = Array::get(start); + if (find_max ? v > m : v < m) { + if (v == null_val) { + continue; + } + m = v; + best_index = start; + } + } + + result = m; + if (return_ndx) { + *return_ndx = best_index - 1; + } + return true; +} + +inline bool ArrayIntNull::maximum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + return minmax_helper(result, start, end, return_ndx); +} + +inline bool ArrayIntNull::minimum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + return minmax_helper(result, start, end, return_ndx); +} + +inline bool ArrayIntNull::find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex, + QueryState* state) const +{ + if (value) { + return Array::find(cond, action, *value, start, end, baseindex, state, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(cond, action, 0 /* unused dummy*/, start, end, baseindex, state, + true /*treat as nullable array*/, true /*search for null, ignore value argument*/); + } +} + +template +bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + if (value) { + return Array::find(*value, start, end, baseindex, state, std::forward(callback), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(0 /*ignored*/, start, end, baseindex, state, + std::forward(callback), true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const +{ + return Array::find(value, start, end, baseindex, state, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); +} + + +template +bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + if (value) { + return Array::find(*value, start, end, baseindex, state, std::forward(callback), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(0 /*ignored*/, start, end, baseindex, state, + std::forward(callback), true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find_action(size_t index, value_type value, QueryState* state, Callback callback) const +{ + if (value) { + return Array::find_action(index, *value, state, callback, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find_action(index, 0 /* ignored */, state, callback, + true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find_action_pattern(size_t index, uint64_t pattern, QueryState* state, + Callback callback) const +{ + return Array::find_action_pattern(index, pattern, state, callback, + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); +} + + +template +size_t ArrayIntNull::find_first(value_type value, size_t start, size_t end) const +{ + QueryState state; + state.init(act_ReturnFirst, nullptr, 1); + if (value) { + Array::find(*value, start, end, 0, &state, Array::CallbackDummy(), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + Array::find(0 /*ignored*/, start, end, 0, &state, Array::CallbackDummy(), + true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } + + if (state.m_match_count > 0) + return to_size_t(state.m_state); + else + return not_found; +} + +inline size_t ArrayIntNull::find_first(value_type value, size_t begin, size_t end) const +{ + return find_first(value, begin, end); +} +} + +#endif // REALM_ARRAY_INTEGER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string.hpp new file mode 100644 index 0000000..bbf3203 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string.hpp @@ -0,0 +1,180 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_STRING_HPP +#define REALM_ARRAY_STRING_HPP + +#include + +namespace realm { + +/* +ArrayString stores strings as a concecutive list of fixed-length blocks of m_width bytes. The +longest string it can store is (m_width - 1) bytes before it needs to expand. + +An example of the format for m_width = 4 is following sequence of bytes, where x is payload: + +xxx0 xx01 x002 0003 0004 (strings "xxx",. "xx", "x", "", realm::null()) + +So each string is 0 terminated, and the last byte in a block tells how many 0s are present, except +for a realm::null() which has the byte set to m_width (4). The byte is used to compute the length of a string +in various functions. + +New: If m_witdh = 0, then all elements are realm::null(). So to add an empty string we must expand m_width +New: StringData is null() if-and-only-if StringData::data() == 0. +*/ + +class ArrayString : public Array { +public: + static const size_t max_width = 64; + + typedef StringData value_type; + // Constructor defaults to non-nullable because we use non-nullable ArrayString so many places internally in core + // (data which isn't user payload) where null isn't needed. + explicit ArrayString(Allocator&, bool nullable = false) noexcept; + ~ArrayString() noexcept override + { + } + + bool is_null(size_t ndx) const; + void set_null(size_t ndx); + StringData get(size_t ndx) const noexcept; + void add(); + void add(StringData value); + void set(size_t ndx, StringData value); + void insert(size_t ndx, StringData value); + void erase(size_t ndx); + + size_t count(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, StringData value, size_t add_offset = 0, size_t begin = 0, + size_t end = npos); + + /// Compare two string arrays for equality. + bool compare_string(const ArrayString&) const noexcept; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static StringData get(const char* header, size_t ndx, bool nullable) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, StringData, TreeInsertBase& state); + + /// Construct a string array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to the empty string. + static MemRef create_array(size_t size, Allocator&); + + /// Create a new empty string array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a copy of the specified slice of this string array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void string_stats() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t calc_byte_len(size_t num_items, size_t width) const override; + size_t calc_item_count(size_t bytes, size_t width) const noexcept override; + + bool m_nullable; +}; + + +// Implementation: + +// Creates new array (but invalid, call init_from_ref() to init) +inline ArrayString::ArrayString(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_nullable(nullable) +{ +} + +inline void ArrayString::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc()); // Throws + init_from_mem(mem); +} + +inline MemRef ArrayString::create_array(size_t init_size, Allocator& allocator) +{ + bool context_flag = false; + int_fast64_t value = 0; + return Array::create(type_Normal, context_flag, wtype_Multiply, init_size, value, allocator); // Throws +} + +inline StringData ArrayString::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_size); + if (m_width == 0) + return m_nullable ? realm::null() : StringData(""); + + const char* data = m_data + (ndx * m_width); + size_t array_size = (m_width - 1) - data[m_width - 1]; + + if (array_size == static_cast(-1)) + return m_nullable ? realm::null() : StringData(""); + + REALM_ASSERT_EX(data[array_size] == 0, data[array_size], + array_size); // Realm guarantees 0 terminated return strings + return StringData(data, array_size); +} + +inline void ArrayString::add(StringData value) +{ + REALM_ASSERT(!(!m_nullable && value.is_null())); + insert(m_size, value); // Throws +} + +inline void ArrayString::add() +{ + add(m_nullable ? realm::null() : StringData("")); // Throws +} + +inline StringData ArrayString::get(const char* header, size_t ndx, bool nullable) noexcept +{ + REALM_ASSERT(ndx < get_size_from_header(header)); + uint_least8_t width = get_width_from_header(header); + const char* data = get_data_from_header(header) + (ndx * width); + + if (width == 0) + return nullable ? realm::null() : StringData(""); + + size_t size = (width - 1) - data[width - 1]; + + if (size == static_cast(-1)) + return nullable ? realm::null() : StringData(""); + + return StringData(data, size); +} + + +} // namespace realm + +#endif // REALM_ARRAY_STRING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string_long.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string_long.hpp new file mode 100644 index 0000000..5f8fcb8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/array_string_long.hpp @@ -0,0 +1,228 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_STRING_LONG_HPP +#define REALM_ARRAY_STRING_LONG_HPP + +#include +#include + +namespace realm { + + +class ArrayStringLong : public Array { +public: + typedef StringData value_type; + + explicit ArrayStringLong(Allocator&, bool nullable) noexcept; + ~ArrayStringLong() noexcept override + { + } + + // Disable copying, this is not allowed. + ArrayStringLong& operator=(const ArrayStringLong&) = delete; + ArrayStringLong(const ArrayStringLong&) = delete; + + /// Create a new empty long string array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + //@{ + /// Overriding functions of Array + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + //@} + + bool is_empty() const noexcept; + size_t size() const noexcept; + + StringData get(size_t ndx) const noexcept; + + + void add(StringData value); + void set(size_t ndx, StringData value); + void insert(size_t ndx, StringData value); + void erase(size_t ndx); + void truncate(size_t size); + void clear(); + void destroy(); + + bool is_null(size_t ndx) const; + void set_null(size_t ndx); + + size_t count(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, StringData value, size_t add_offset = 0, size_t begin = 0, + size_t end = npos) const; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static StringData get(const char* header, size_t ndx, Allocator&, bool nullable) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, StringData, TreeInsertBase&); + + static size_t get_size_from_header(const char*, Allocator&) noexcept; + + /// Construct a long string array of the specified size and return + /// just the reference to the underlying memory. All elements will + /// be initialized to zero size blobs. + static MemRef create_array(size_t size, Allocator&, bool nullable); + + /// Construct a copy of the specified slice of this long string + /// array using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + + bool update_from_parent(size_t old_baseline) noexcept; + +private: + ArrayInteger m_offsets; + ArrayBlob m_blob; + Array m_nulls; + bool m_nullable; +}; + + +// Implementation: +inline ArrayStringLong::ArrayStringLong(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_offsets(allocator) + , m_blob(allocator) + , m_nulls(nullable ? allocator : Allocator::get_default()) + , m_nullable(nullable) +{ + m_offsets.set_parent(this, 0); + m_blob.set_parent(this, 1); + if (nullable) + m_nulls.set_parent(this, 2); +} + +inline void ArrayStringLong::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc(), m_nullable); // Throws + init_from_mem(mem); +} + +inline void ArrayStringLong::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT(ref); + char* header = get_alloc().translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); + m_nullable = (Array::size() == 3); +} + +inline void ArrayStringLong::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + +inline bool ArrayStringLong::is_empty() const noexcept +{ + return m_offsets.is_empty(); +} + +inline size_t ArrayStringLong::size() const noexcept +{ + return m_offsets.size(); +} + +inline StringData ArrayStringLong::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_offsets.size()); + + if (m_nullable && m_nulls.get(ndx) == 0) + return realm::null(); + + size_t begin, end; + if (0 < ndx) { + begin = to_size_t(m_offsets.get(ndx - 1)); + end = to_size_t(m_offsets.get(ndx)); + } + else { + begin = 0; + end = to_size_t(m_offsets.get(0)); + } + --end; // Discount the terminating zero + + return StringData(m_blob.get(begin), end - begin); +} + +inline void ArrayStringLong::truncate(size_t new_size) +{ + REALM_ASSERT_3(new_size, <, m_offsets.size()); + + size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + + m_offsets.truncate(new_size); + m_blob.truncate(blob_size); + if (m_nullable) + m_nulls.truncate(new_size); +} + +inline void ArrayStringLong::clear() +{ + m_blob.clear(); + m_offsets.clear(); + if (m_nullable) + m_nulls.clear(); +} + +inline void ArrayStringLong::destroy() +{ + m_blob.destroy(); + m_offsets.destroy(); + if (m_nullable) + m_nulls.destroy(); + Array::destroy(); +} + +inline bool ArrayStringLong::update_from_parent(size_t old_baseline) noexcept +{ + bool res = Array::update_from_parent(old_baseline); + if (res) { + m_blob.update_from_parent(old_baseline); + m_offsets.update_from_parent(old_baseline); + if (m_nullable) + m_nulls.update_from_parent(old_baseline); + } + return res; +} + +inline size_t ArrayStringLong::get_size_from_header(const char* header, Allocator& alloc) noexcept +{ + ref_type offsets_ref = to_ref(Array::get(header, 0)); + const char* offsets_header = alloc.translate(offsets_ref); + return Array::get_size_from_header(offsets_header); +} + + +} // namespace realm + +#endif // REALM_ARRAY_STRING_LONG_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/binary_data.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/binary_data.hpp new file mode 100644 index 0000000..a184f0c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/binary_data.hpp @@ -0,0 +1,239 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_BINARY_DATA_HPP +#define REALM_BINARY_DATA_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { + +/// A reference to a chunk of binary data. +/// +/// This class does not own the referenced memory, nor does it in any other way +/// attempt to manage the lifetime of it. +/// +/// \sa StringData +class BinaryData { +public: + BinaryData() noexcept + : m_data(nullptr) + , m_size(0) + { + } + BinaryData(const char* external_data, size_t data_size) noexcept + : m_data(external_data) + , m_size(data_size) + { + } + template + explicit BinaryData(const char (&external_data)[N]) + : m_data(external_data) + , m_size(N) + { + } + template + explicit BinaryData(const std::basic_string&); + + // BinaryData does not store data, callers must manage their own strings. + template + BinaryData(const std::basic_string&&) = delete; + + template + explicit operator std::basic_string() const; + + char operator[](size_t i) const noexcept + { + return m_data[i]; + } + + const char* data() const noexcept + { + return m_data; + } + size_t size() const noexcept + { + return m_size; + } + + /// Is this a null reference? + /// + /// An instance of BinaryData is a null reference when, and only when the + /// stored size is zero (size()) and the stored pointer is the null pointer + /// (data()). + /// + /// In the case of the empty byte sequence, the stored size is still zero, + /// but the stored pointer is **not** the null pointer. Note that the actual + /// value of the pointer is immaterial in this case (as long as it is not + /// zero), because when the size is zero, it is an error to dereference the + /// pointer. + /// + /// Conversion of a BinaryData object to `bool` yields the logical negation + /// of the result of calling this function. In other words, a BinaryData + /// object is converted to true if it is not the null reference, otherwise + /// it is converted to false. + /// + /// It is important to understand that all of the functions and operators in + /// this class, and most of the functions in the Realm API in general + /// makes no distinction between a null reference and a reference to the + /// empty byte sequence. These functions and operators never look at the + /// stored pointer if the stored size is zero. + bool is_null() const noexcept; + + friend bool operator==(const BinaryData&, const BinaryData&) noexcept; + friend bool operator!=(const BinaryData&, const BinaryData&) noexcept; + + //@{ + /// Trivial bytewise lexicographical comparison. + friend bool operator<(const BinaryData&, const BinaryData&) noexcept; + friend bool operator>(const BinaryData&, const BinaryData&) noexcept; + friend bool operator<=(const BinaryData&, const BinaryData&) noexcept; + friend bool operator>=(const BinaryData&, const BinaryData&) noexcept; + //@} + + bool begins_with(BinaryData) const noexcept; + bool ends_with(BinaryData) const noexcept; + bool contains(BinaryData) const noexcept; + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const BinaryData&); + + explicit operator bool() const noexcept; + +private: + const char* m_data; + size_t m_size; +}; + +/// A read-only chunk of binary data. +class OwnedBinaryData : public OwnedData { +public: + using OwnedData::OwnedData; + + OwnedBinaryData() = default; + OwnedBinaryData(const BinaryData& binary_data) + : OwnedData(binary_data.data(), binary_data.size()) + { + } + + BinaryData get() const + { + return {data(), size()}; + } +}; + + +// Implementation: + +template +inline BinaryData::BinaryData(const std::basic_string& s) + : m_data(s.data()) + , m_size(s.size()) +{ +} + +template +inline BinaryData::operator std::basic_string() const +{ + return std::basic_string(m_data, m_size); +} + +inline bool BinaryData::is_null() const noexcept +{ + return !m_data; +} + +inline bool operator==(const BinaryData& a, const BinaryData& b) noexcept +{ + return a.m_size == b.m_size && a.is_null() == b.is_null() && safe_equal(a.m_data, a.m_data + a.m_size, b.m_data); +} + +inline bool operator!=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(a == b); +} + +inline bool operator<(const BinaryData& a, const BinaryData& b) noexcept +{ + if (a.is_null() || b.is_null()) + return !a.is_null() < !b.is_null(); + + return std::lexicographical_compare(a.m_data, a.m_data + a.m_size, b.m_data, b.m_data + b.m_size); +} + +inline bool operator>(const BinaryData& a, const BinaryData& b) noexcept +{ + return b < a; +} + +inline bool operator<=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(b < a); +} + +inline bool operator>=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(a < b); +} + +inline bool BinaryData::begins_with(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size <= m_size && safe_equal(m_data, m_data + d.m_size, d.m_data); +} + +inline bool BinaryData::ends_with(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size <= m_size && safe_equal(m_data + m_size - d.m_size, m_data + m_size, d.m_data); +} + +inline bool BinaryData::contains(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size == 0 || std::search(m_data, m_data + m_size, d.m_data, d.m_data + d.m_size) != m_data + m_size; +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const BinaryData& d) +{ + out << "BinaryData(" << static_cast(d.m_data) << ", " << d.m_size << ")"; + return out; +} + +inline BinaryData::operator bool() const noexcept +{ + return !is_null(); +} + +} // namespace realm + +#endif // REALM_BINARY_DATA_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/bptree.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/bptree.hpp new file mode 100644 index 0000000..aae64de --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/bptree.hpp @@ -0,0 +1,1271 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_BPTREE_HPP +#define REALM_BPTREE_HPP + +#include // std::unique_ptr +#include +#include +#include +#include +#include + +namespace realm { + +/// Specialize BpTree to implement column types. +template +class BpTree; + +class ArrayInteger; +class ArrayIntNull; + +class BpTreeNode : public Array { +public: + using Array::Array; + /// Get the number of elements in the B+-tree rooted at this array + /// node. The root must not be a leaf. + /// + /// Please avoid using this function (consider it deprecated). It + /// will have to be removed if we choose to get rid of the last + /// element of the main array of an inner B+-tree node that stores + /// the total number of elements in the subtree. The motivation + /// for removing it, is that it will significantly improve the + /// efficiency when inserting after, and erasing the last element. + size_t get_bptree_size() const noexcept; + + /// The root must not be a leaf. + static size_t get_bptree_size_from_header(const char* root_header) noexcept; + + + /// Find the leaf node corresponding to the specified element + /// index index. The specified element index must refer to an + /// element that exists in the tree. This function must be called + /// on an inner B+-tree node, never a leaf. Note that according to + /// invar:bptree-nonempty-inner and invar:bptree-nonempty-leaf, an + /// inner B+-tree node can never be empty. + /// + /// This function is not obliged to instantiate intermediate array + /// accessors. For this reason, this function cannot be used for + /// operations that modify the tree, as that requires an unbroken + /// chain of parent array accessors between the root and the + /// leaf. Thus, despite the fact that the returned MemRef object + /// appears to allow modification of the referenced memory, the + /// caller must handle the memory reference as if it was + /// const-qualified. + /// + /// \return (`leaf_header`, `ndx_in_leaf`) where `leaf_header` + /// points to the the header of the located leaf, and + /// `ndx_in_leaf` is the local index within that leaf + /// corresponding to the specified element index. + std::pair get_bptree_leaf(size_t elem_ndx) const noexcept; + + + class NodeInfo; + class VisitHandler; + + /// Visit leaves of the B+-tree rooted at this inner node, + /// starting with the leaf that contains the element at the + /// specified element index start offset, and ending when the + /// handler returns false. The specified element index offset must + /// refer to an element that exists in the tree. This function + /// must be called on an inner B+-tree node, never a leaf. Note + /// that according to invar:bptree-nonempty-inner and + /// invar:bptree-nonempty-leaf, an inner B+-tree node can never be + /// empty. + /// + /// \param elem_ndx_offset The start position (must be valid). + /// + /// \param elems_in_tree The total number of elements in the tree. + /// + /// \param handler The callback which will get called for each leaf. + /// + /// \return True if, and only if the handler has returned true for + /// all visited leafs. + bool visit_bptree_leaves(size_t elem_ndx_offset, size_t elems_in_tree, VisitHandler& handler); + + + class UpdateHandler; + + /// Call the handler for every leaf. This function must be called + /// on an inner B+-tree node, never a leaf. + void update_bptree_leaves(UpdateHandler&); + + /// Call the handler for the leaf that contains the element at the + /// specified index. This function must be called on an inner + /// B+-tree node, never a leaf. + void update_bptree_elem(size_t elem_ndx, UpdateHandler&); + + + class EraseHandler; + + /// Erase the element at the specified index in the B+-tree with + /// the specified root. When erasing the last element, you must + /// pass npos in place of the index. This function must be called + /// with a root that is an inner B+-tree node, never a leaf. + /// + /// This function is guaranteed to succeed (not throw) if the + /// specified element was inserted during the current transaction, + /// and no other modifying operation has been carried out since + /// then (noexcept:bptree-erase-alt). + /// + /// FIXME: ExceptionSafety: The exception guarantee explained + /// above is not as powerfull as we would like it to be. Here is + /// what we would like: This function is guaranteed to succeed + /// (not throw) if the specified element was inserted during the + /// current transaction (noexcept:bptree-erase). This must be true + /// even if the element is modified after insertion, and/or if + /// other elements are inserted or erased around it. There are two + /// aspects of the current design that stand in the way of this + /// guarantee: (A) The fact that the node accessor, that is cached + /// in the column accessor, has to be reallocated/reinstantiated + /// when the root switches between being a leaf and an inner + /// node. This problem would go away if we always cached the last + /// used leaf accessor in the column accessor instead. (B) The + /// fact that replacing one child ref with another can fail, + /// because it may require reallocation of memory to expand the + /// bit-width. This can be fixed in two ways: Either have the + /// inner B+-tree nodes always have a bit-width of 64, or allow + /// the root node to be discarded and the column ref to be set to + /// zero in Table::m_columns. + static void erase_bptree_elem(BpTreeNode* root, size_t elem_ndx, EraseHandler&); + + template + struct TreeInsert : TreeInsertBase { + typename TreeTraits::value_type m_value; + bool m_nullable; + }; + + /// Same as bptree_insert() but insert after the last element. + template + ref_type bptree_append(TreeInsert& state); + + /// Insert an element into the B+-subtree rooted at this array + /// node. The element is inserted before the specified element + /// index. This function must be called on an inner B+-tree node, + /// never a leaf. If this inner node had to be split, this + /// function returns the `ref` of the new sibling. + template + ref_type bptree_insert(size_t elem_ndx, TreeInsert& state); + +protected: + /// Insert a new child after original. If the parent has to be + /// split, this function returns the `ref` of the new parent node. + ref_type insert_bptree_child(Array& offsets, size_t orig_child_ndx, ref_type new_sibling_ref, + TreeInsertBase& state); + + void ensure_bptree_offsets(Array& offsets); + void create_bptree_offsets(Array& offsets, int_fast64_t first_value); + + bool do_erase_bptree_elem(size_t elem_ndx, EraseHandler&); +}; + +class BpTreeBase { +public: + struct unattached_tag { + }; + + // Disable copying, this is not allowed. + BpTreeBase& operator=(const BpTreeBase&) = delete; + BpTreeBase(const BpTreeBase&) = delete; + + // Accessor concept: + Allocator& get_alloc() const noexcept; + void destroy() noexcept; + void detach(); + bool is_attached() const noexcept; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t ndx) noexcept; + void update_from_parent(size_t old_baseline) noexcept; + MemRef clone_deep(Allocator& alloc) const; + + // BpTree interface: + const Array& root() const noexcept; + Array& root() noexcept; + bool root_is_leaf() const noexcept; + BpTreeNode& root_as_node(); + const BpTreeNode& root_as_node() const; + void introduce_new_root(ref_type new_sibling_ref, TreeInsertBase& state, bool is_append); + void replace_root(std::unique_ptr leaf); + +protected: + explicit BpTreeBase(std::unique_ptr root); + explicit BpTreeBase(BpTreeBase&&) = default; + BpTreeBase& operator=(BpTreeBase&&) = default; + std::unique_ptr m_root; + + struct SliceHandler { + virtual MemRef slice_leaf(MemRef leaf_mem, size_t offset, size_t size, Allocator& target_alloc) = 0; + ~SliceHandler() noexcept + { + } + }; + static ref_type write_subtree(const BpTreeNode& root, size_t slice_offset, size_t slice_size, size_t table_size, + SliceHandler&, _impl::OutputStream&); + friend class ColumnBase; + friend class ColumnBaseSimple; + +private: + struct WriteSliceHandler; + + // FIXME: Move B+Tree functionality from Array to this class. +}; + + +// Default implementation of BpTree. This should work for all types that have monomorphic +// leaves (i.e. all leaves are of the same type). +template +class BpTree : public BpTreeBase { +public: + using value_type = T; + using LeafType = typename ColumnTypeTraits::leaf_type; + + /// LeafInfo is used by get_leaf() to provide access to a leaf + /// without instantiating unnecessary nodes along the way. + /// Upon return, out_leaf with hold a pointer to the leaf containing + /// the index given to get_leaf(). If the index happens to be + /// in the root node (i.e., the root is a leaf), it will point + /// to the root node. + /// If the index isn't in the root node, fallback will be initialized + /// to represent the leaf holding the node, and out_leaf will be set + /// to point to fallback. + struct LeafInfo { + const LeafType** out_leaf; + LeafType* fallback; + }; + + BpTree(); + explicit BpTree(BpTreeBase::unattached_tag); + explicit BpTree(Allocator& alloc); + REALM_DEPRECATED("Initialize with MemRef instead") + explicit BpTree(std::unique_ptr init_root) + : BpTreeBase(std::move(init_root)) + { + } + explicit BpTree(Allocator& alloc, MemRef mem) + : BpTreeBase(std::unique_ptr(new LeafType(alloc))) + { + init_from_mem(alloc, mem); + } + BpTree(BpTree&&) = default; + BpTree& operator=(BpTree&&) = default; + + // Disable copying, this is not allowed. + BpTree& operator=(const BpTree&) = delete; + BpTree(const BpTree&) = delete; + + void init_from_ref(Allocator& alloc, ref_type ref); + void init_from_mem(Allocator& alloc, MemRef mem); + void init_from_parent(); + + size_t size() const noexcept; + bool is_empty() const noexcept + { + return size() == 0; + } + + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept; + void set(size_t, T value); + void set_null(size_t); + void insert(size_t ndx, T value, size_t num_rows = 1); + void erase(size_t ndx, bool is_last = false); + void move_last_over(size_t ndx, size_t last_row_ndx); + void clear(); + T front() const noexcept; + T back() const noexcept; + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& out_indices, T value, size_t begin = 0, size_t end = npos) const; + + static MemRef create_leaf(Array::Type leaf_type, size_t size, T value, Allocator&); + + /// See LeafInfo for information about what to put in the inout_leaf + /// parameter. + /// + /// This function cannot be used for modifying operations as it + /// does not ensure the presence of an unbroken chain of parent + /// accessors. For this reason, the identified leaf should always + /// be accessed through the returned const-qualified reference, + /// and never directly through the specfied fallback accessor. + void get_leaf(size_t ndx, size_t& out_ndx_in_leaf, LeafInfo& inout_leaf) const noexcept; + + void update_each(BpTreeNode::UpdateHandler&); + void update_elem(size_t, BpTreeNode::UpdateHandler&); + + void adjust(size_t ndx, T diff); + void adjust(T diff); + void adjust_ge(T limit, T diff); + + ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const; + +#if defined(REALM_DEBUG) + void verify() const; + static size_t verify_leaf(MemRef mem, Allocator& alloc); +#endif + static void leaf_to_dot(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out, + Allocator& alloc); + +private: + LeafType& root_as_leaf(); + const LeafType& root_as_leaf() const; + + std::unique_ptr create_root_from_ref(Allocator& alloc, ref_type ref); + std::unique_ptr create_root_from_mem(Allocator& alloc, MemRef mem); + + struct EraseHandler; + struct UpdateHandler; + struct SetNullHandler; + struct SliceHandler; + struct AdjustHandler; + struct AdjustGEHandler; + + struct LeafValueInserter; + struct LeafNullInserter; + + template + void bptree_insert(size_t row_ndx, BpTreeNode::TreeInsert& state, size_t num_rows); +}; + + +class BpTreeNode::NodeInfo { +public: + MemRef m_mem; + Array* m_parent; + size_t m_ndx_in_parent; + size_t m_offset, m_size; +}; + +class BpTreeNode::VisitHandler { +public: + virtual bool visit(const NodeInfo& leaf_info) = 0; + virtual ~VisitHandler() noexcept + { + } +}; + + +class BpTreeNode::UpdateHandler { +public: + virtual void update(MemRef, ArrayParent*, size_t leaf_ndx_in_parent, size_t elem_ndx_in_leaf) = 0; + virtual ~UpdateHandler() noexcept + { + } +}; + + +class BpTreeNode::EraseHandler { +public: + /// If the specified leaf has more than one element, this function + /// must erase the specified element from the leaf and return + /// false. Otherwise, when the leaf has a single element, this + /// function must return true without modifying the leaf. If \a + /// elem_ndx_in_leaf is `npos`, it refers to the last element in + /// the leaf. The implementation of this function must be + /// exception safe. This function is guaranteed to be called at + /// most once during each execution of Array::erase_bptree_elem(), + /// and *exactly* once during each *successful* execution of + /// Array::erase_bptree_elem(). + virtual bool erase_leaf_elem(MemRef, ArrayParent*, size_t leaf_ndx_in_parent, size_t elem_ndx_in_leaf) = 0; + + virtual void destroy_leaf(MemRef leaf_mem) noexcept = 0; + + /// Must replace the current root with the specified leaf. The + /// implementation of this function must not destroy the + /// underlying root node, or any of its children, as that will be + /// done by Array::erase_bptree_elem(). The implementation of this + /// function must be exception safe. + virtual void replace_root_by_leaf(MemRef leaf_mem) = 0; + + /// Same as replace_root_by_leaf(), but must replace the root with + /// an empty leaf. Also, if this function is called during an + /// execution of Array::erase_bptree_elem(), it is guaranteed that + /// it will be preceeded by a call to erase_leaf_elem(). + virtual void replace_root_by_empty_leaf() = 0; + + virtual ~EraseHandler() noexcept + { + } +}; + + +/// Implementation: + +inline BpTreeBase::BpTreeBase(std::unique_ptr init_root) + : m_root(std::move(init_root)) +{ +} + +inline Allocator& BpTreeBase::get_alloc() const noexcept +{ + return m_root->get_alloc(); +} + +inline void BpTreeBase::destroy() noexcept +{ + if (m_root) + m_root->destroy_deep(); +} + +inline void BpTreeBase::detach() +{ + m_root->detach(); +} + +inline bool BpTreeBase::is_attached() const noexcept +{ + return m_root->is_attached(); +} + +inline bool BpTreeBase::root_is_leaf() const noexcept +{ + return !m_root->is_inner_bptree_node(); +} + +inline BpTreeNode& BpTreeBase::root_as_node() +{ + REALM_ASSERT_DEBUG(!root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(m_root.get()) != nullptr); + return static_cast(root()); +} + +inline const BpTreeNode& BpTreeBase::root_as_node() const +{ + Array* arr = m_root.get(); + REALM_ASSERT_DEBUG(!root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(arr) != nullptr); + return static_cast(*arr); +} + +inline void BpTreeBase::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_root->set_parent(parent, ndx_in_parent); +} + +inline size_t BpTreeBase::get_ndx_in_parent() const noexcept +{ + return m_root->get_ndx_in_parent(); +} + +inline void BpTreeBase::set_ndx_in_parent(size_t ndx) noexcept +{ + m_root->set_ndx_in_parent(ndx); +} + +inline void BpTreeBase::update_from_parent(size_t old_baseline) noexcept +{ + m_root->update_from_parent(old_baseline); +} + +inline MemRef BpTreeBase::clone_deep(Allocator& alloc) const +{ + return m_root->clone_deep(alloc); +} + +inline const Array& BpTreeBase::root() const noexcept +{ + return *m_root; +} + +inline Array& BpTreeBase::root() noexcept +{ + return *m_root; +} + +inline size_t BpTreeNode::get_bptree_size() const noexcept +{ + REALM_ASSERT_DEBUG(is_inner_bptree_node()); + int_fast64_t v = back(); + return size_t(v / 2); // v = 1 + 2*total_elems_in_tree +} + +inline size_t BpTreeNode::get_bptree_size_from_header(const char* root_header) noexcept +{ + REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(root_header)); + size_t root_size = get_size_from_header(root_header); + int_fast64_t v = get(root_header, root_size - 1); + return size_t(v / 2); // v = 1 + 2*total_elems_in_tree +} + +inline void BpTreeNode::ensure_bptree_offsets(Array& offsets) +{ + int_fast64_t first_value = get(0); + if (first_value % 2 == 0) { + offsets.init_from_ref(to_ref(first_value)); + } + else { + create_bptree_offsets(offsets, first_value); // Throws + } + offsets.set_parent(this, 0); +} + + +template +ref_type BpTreeNode::bptree_append(TreeInsert& state) +{ + // FIXME: Consider exception safety. Especially, how can the split + // be carried out in an exception safe manner? + // + // Can split be done as a separate preparation step, such that if + // the actual insert fails, the split will still have occured. + // + // Unfortunately, it requires a rather significant rearrangement + // of the insertion flow. Instead of returning the sibling ref + // from insert functions, the leaf-insert functions must instead + // call the special bptree_insert() function on the parent, which + // will then cascade the split towards the root as required. + // + // At each level where a split is required (starting at the leaf): + // + // 1. Create the new sibling. + // + // 2. Copy relevant entries over such that new sibling is in + // its final state. + // + // 3. Call Array::bptree_insert() on parent with sibling ref. + // + // 4. Rearrange entries in original sibling and truncate as + // required (must not throw). + // + // What about the 'offsets' array? It will always be + // present. Consider this carefully. + + REALM_ASSERT_DEBUG(size() >= 1 + 1 + 1); // At least one child + + ArrayParent& childs_parent = *this; + size_t child_ref_ndx = size() - 2; + ref_type child_ref = get_as_ref(child_ref_ndx), new_sibling_ref; + char* child_header = static_cast(m_alloc.translate(child_ref)); + + bool child_is_leaf = !get_is_inner_bptree_node_from_header(child_header); + if (child_is_leaf) { + size_t elem_ndx_in_child = npos; // Append + new_sibling_ref = TreeTraits::leaf_insert(MemRef(child_header, child_ref, m_alloc), childs_parent, + child_ref_ndx, m_alloc, elem_ndx_in_child, state); // Throws + } + else { + BpTreeNode child(m_alloc); + child.init_from_mem(MemRef(child_header, child_ref, m_alloc)); + child.set_parent(&childs_parent, child_ref_ndx); + new_sibling_ref = child.bptree_append(state); // Throws + } + + if (REALM_LIKELY(!new_sibling_ref)) { + // +2 because stored value is 1 + 2*total_elems_in_subtree + adjust(size() - 1, +2); // Throws + return 0; // Child was not split, so parent was not split either + } + + Array offsets(m_alloc); + int_fast64_t first_value = get(0); + if (first_value % 2 == 0) { + // Offsets array is present (general form) + offsets.init_from_ref(to_ref(first_value)); + offsets.set_parent(this, 0); + } + size_t child_ndx = child_ref_ndx - 1; + return insert_bptree_child(offsets, child_ndx, new_sibling_ref, state); // Throws +} + + +template +ref_type BpTreeNode::bptree_insert(size_t elem_ndx, TreeInsert& state) +{ + REALM_ASSERT_3(size(), >=, 1 + 1 + 1); // At least one child + + // Conversion to general form if in compact form. Since this + // conversion will occur from root to leaf, it will maintain + // invar:bptree-node-form. + Array offsets(m_alloc); + ensure_bptree_offsets(offsets); // Throws + + size_t child_ndx, elem_ndx_in_child; + if (elem_ndx == 0) { + // Optimization for prepend + child_ndx = 0; + elem_ndx_in_child = 0; + } + else { + // There is a choice to be made when the element is to be + // inserted between two subtrees. It can either be appended to + // the first subtree, or it can be prepended to the second + // one. We currently always append to the first subtree. It is + // essentially a matter of using the lower vs. the upper bound + // when searching through the offsets array. + child_ndx = offsets.lower_bound_int(elem_ndx); + REALM_ASSERT_3(child_ndx, <, size() - 2); + size_t elem_ndx_offset = child_ndx == 0 ? 0 : to_size_t(offsets.get(child_ndx - 1)); + elem_ndx_in_child = elem_ndx - elem_ndx_offset; + } + + ArrayParent& childs_parent = *this; + size_t child_ref_ndx = child_ndx + 1; + ref_type child_ref = get_as_ref(child_ref_ndx), new_sibling_ref; + char* child_header = static_cast(m_alloc.translate(child_ref)); + bool child_is_leaf = !get_is_inner_bptree_node_from_header(child_header); + if (child_is_leaf) { + REALM_ASSERT_3(elem_ndx_in_child, <=, REALM_MAX_BPNODE_SIZE); + new_sibling_ref = TreeTraits::leaf_insert(MemRef(child_header, child_ref, m_alloc), childs_parent, + child_ref_ndx, m_alloc, elem_ndx_in_child, state); // Throws + } + else { + BpTreeNode child(m_alloc); + child.init_from_mem(MemRef(child_header, child_ref, m_alloc)); + child.set_parent(&childs_parent, child_ref_ndx); + new_sibling_ref = child.bptree_insert(elem_ndx_in_child, state); // Throws + } + + if (REALM_LIKELY(!new_sibling_ref)) { + // +2 because stored value is 1 + 2*total_elems_in_subtree + adjust(size() - 1, +2); // Throws + offsets.adjust(child_ndx, offsets.size(), +1); + return 0; // Child was not split, so parent was not split either + } + + return insert_bptree_child(offsets, child_ndx, new_sibling_ref, state); // Throws +} + +template +BpTree::BpTree() + : BpTree(Allocator::get_default()) +{ +} + +template +BpTree::BpTree(Allocator& alloc) + : BpTreeBase(std::unique_ptr(new LeafType(alloc))) +{ +} + +template +BpTree::BpTree(BpTreeBase::unattached_tag) + : BpTreeBase(nullptr) +{ +} + +template +std::unique_ptr BpTree::create_root_from_mem(Allocator& alloc, MemRef mem) +{ + const char* header = mem.get_addr(); + std::unique_ptr new_root; + bool is_inner_bptree_node = Array::get_is_inner_bptree_node_from_header(header); + + bool can_reuse_root_accessor = + m_root && &m_root->get_alloc() == &alloc && m_root->is_inner_bptree_node() == is_inner_bptree_node; + if (can_reuse_root_accessor) { + if (is_inner_bptree_node) { + m_root->init_from_mem(mem); + } + else { + static_cast(*m_root).init_from_mem(mem); + } + return std::move(m_root); // Same root will be reinstalled. + } + + // Not reusing root note, allocating a new one. + if (is_inner_bptree_node) { + new_root.reset(new BpTreeNode{alloc}); + new_root->init_from_mem(mem); + } + else { + std::unique_ptr leaf{new LeafType{alloc}}; + leaf->init_from_mem(mem); + new_root = std::move(leaf); + } + return new_root; +} + +template +std::unique_ptr BpTree::create_root_from_ref(Allocator& alloc, ref_type ref) +{ + MemRef mem = MemRef{alloc.translate(ref), ref, alloc}; + return create_root_from_mem(alloc, mem); +} + +template +void BpTree::init_from_ref(Allocator& alloc, ref_type ref) +{ + auto new_root = create_root_from_ref(alloc, ref); + replace_root(std::move(new_root)); +} + +template +void BpTree::init_from_mem(Allocator& alloc, MemRef mem) +{ + auto new_root = create_root_from_mem(alloc, mem); + replace_root(std::move(new_root)); +} + +template +void BpTree::init_from_parent() +{ + ref_type ref = root().get_ref_from_parent(); + if (ref) { + ArrayParent* parent = m_root->get_parent(); + size_t ndx_in_parent = m_root->get_ndx_in_parent(); + auto new_root = create_root_from_ref(get_alloc(), ref); + new_root->set_parent(parent, ndx_in_parent); + m_root = std::move(new_root); + } + else { + m_root->detach(); + } +} + +template +typename BpTree::LeafType& BpTree::root_as_leaf() +{ + REALM_ASSERT_DEBUG(root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(m_root.get()) != nullptr); + return static_cast(root()); +} + +template +const typename BpTree::LeafType& BpTree::root_as_leaf() const +{ + REALM_ASSERT_DEBUG(root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(m_root.get()) != nullptr); + return static_cast(root()); +} + +template +size_t BpTree::size() const noexcept +{ + if (root_is_leaf()) { + return root_as_leaf().size(); + } + return root_as_node().get_bptree_size(); +} + +template +T BpTree::back() const noexcept +{ + // FIXME: slow + return get(size() - 1); +} + +namespace _impl { + +// NullableOrNothing encapsulates the behavior of nullable and +// non-nullable leaf types, so that non-nullable leaf types +// don't have to implement is_null/set_null but BpTree can still +// support the interface (and return false / assert when null +// is not supported). +template +struct NullableOrNothing { + static bool is_null(const Leaf& leaf, size_t ndx) + { + return leaf.is_null(ndx); + } + static void set_null(Leaf& leaf, size_t ndx) + { + leaf.set_null(ndx); + } +}; +template <> +struct NullableOrNothing { + static bool is_null(const ArrayInteger&, size_t) + { + return false; + } + static void set_null(ArrayInteger&, size_t) + { + REALM_ASSERT_RELEASE(false); + } +}; +} + +template +bool BpTree::is_null(size_t ndx) const noexcept +{ + if (root_is_leaf()) { + return _impl::NullableOrNothing::is_null(root_as_leaf(), ndx); + } + LeafType fallback(get_alloc()); + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &fallback}; + size_t ndx_in_leaf; + get_leaf(ndx, ndx_in_leaf, leaf_info); + return _impl::NullableOrNothing::is_null(*leaf, ndx_in_leaf); +} + +template +T BpTree::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG_EX(ndx < size(), ndx, size()); + if (root_is_leaf()) { + return root_as_leaf().get(ndx); + } + + // Use direct getter to avoid initializing leaf array: + std::pair p = root_as_node().get_bptree_leaf(ndx); + const char* leaf_header = p.first.get_addr(); + size_t ndx_in_leaf = p.second; + return LeafType::get(leaf_header, ndx_in_leaf); +} + +template +template +void BpTree::bptree_insert(size_t row_ndx, BpTreeNode::TreeInsert& state, size_t num_rows) +{ + ref_type new_sibling_ref; + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx == realm::npos ? realm::npos : row_ndx + i; + if (root_is_leaf()) { + REALM_ASSERT_DEBUG(row_ndx_2 == realm::npos || row_ndx_2 < REALM_MAX_BPNODE_SIZE); + new_sibling_ref = root_as_leaf().bptree_leaf_insert(row_ndx_2, state.m_value, state); + } + else { + if (row_ndx_2 == realm::npos) { + new_sibling_ref = root_as_node().bptree_append(state); // Throws + } + else { + new_sibling_ref = root_as_node().bptree_insert(row_ndx_2, state); // Throws + } + } + + if (REALM_UNLIKELY(new_sibling_ref)) { + bool is_append = row_ndx_2 == realm::npos; + introduce_new_root(new_sibling_ref, state, is_append); + } + } +} + +template +struct BpTree::LeafValueInserter { + using value_type = T; + T m_value; + LeafValueInserter(T value) + : m_value(std::move(value)) + { + } + + // TreeTraits concept: + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent& parent, size_t ndx_in_parent, Allocator& alloc, + size_t ndx_in_leaf, BpTreeNode::TreeInsert& state) + { + LeafType leaf{alloc}; + leaf.init_from_mem(leaf_mem); + leaf.set_parent(&parent, ndx_in_parent); + // Should not move out of m_value, because the same inserter may be used to perform + // multiple insertions (for example, if num_rows > 1). + return leaf.bptree_leaf_insert(ndx_in_leaf, state.m_value, state); + } +}; + +template +struct BpTree::LeafNullInserter { + using value_type = null; + // TreeTraits concept: + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent& parent, size_t ndx_in_parent, Allocator& alloc, + size_t ndx_in_leaf, BpTreeNode::TreeInsert& state) + { + LeafType leaf{alloc}; + leaf.init_from_mem(leaf_mem); + leaf.set_parent(&parent, ndx_in_parent); + return leaf.bptree_leaf_insert(ndx_in_leaf, null{}, state); + } +}; + +template +void BpTree::insert(size_t row_ndx, T value, size_t num_rows) +{ + REALM_ASSERT_DEBUG(row_ndx == npos || row_ndx < size()); + BpTreeNode::TreeInsert inserter; + inserter.m_value = std::move(value); + inserter.m_nullable = std::is_same>::value; // FIXME + bptree_insert(row_ndx, inserter, num_rows); // Throws +} + +template +struct BpTree::UpdateHandler : BpTreeNode::UpdateHandler { + LeafType m_leaf; + const T m_value; + UpdateHandler(BpTreeBase& tree, T value) noexcept + : m_leaf(tree.get_alloc()) + , m_value(std::move(value)) + { + } + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.set(elem_ndx_in_leaf, m_value); // Throws + } +}; + +template +struct BpTree::SetNullHandler : BpTreeNode::UpdateHandler { + LeafType m_leaf; + SetNullHandler(BpTreeBase& tree) noexcept + : m_leaf(tree.get_alloc()) + { + } + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + _impl::NullableOrNothing::set_null(m_leaf, elem_ndx_in_leaf); // Throws + } +}; + +template +void BpTree::set(size_t ndx, T value) +{ + if (root_is_leaf()) { + root_as_leaf().set(ndx, std::move(value)); + } + else { + UpdateHandler set_leaf_elem(*this, std::move(value)); + static_cast(m_root.get())->update_bptree_elem(ndx, set_leaf_elem); // Throws + } +} + +template +void BpTree::set_null(size_t ndx) +{ + if (root_is_leaf()) { + _impl::NullableOrNothing::set_null(root_as_leaf(), ndx); + } + else { + SetNullHandler set_leaf_elem(*this); + static_cast(m_root.get())->update_bptree_elem(ndx, set_leaf_elem); // Throws; + } +} + +template +struct BpTree::EraseHandler : BpTreeNode::EraseHandler { + BpTreeBase& m_tree; + LeafType m_leaf; + bool m_leaves_have_refs; // FIXME: Should be able to eliminate this. + EraseHandler(BpTreeBase& tree) noexcept + : m_tree(tree) + , m_leaf(tree.get_alloc()) + , m_leaves_have_refs(false) + { + } + bool erase_leaf_elem(MemRef leaf_mem, ArrayParent* parent, size_t leaf_ndx_in_parent, + size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(leaf_mem); + REALM_ASSERT_3(m_leaf.size(), >=, 1); + size_t last_ndx = m_leaf.size() - 1; + if (last_ndx == 0) { + m_leaves_have_refs = m_leaf.has_refs(); + return true; + } + m_leaf.set_parent(parent, leaf_ndx_in_parent); + size_t ndx = elem_ndx_in_leaf; + if (ndx == npos) + ndx = last_ndx; + m_leaf.erase(ndx); // Throws + return false; + } + void destroy_leaf(MemRef leaf_mem) noexcept override + { + // FIXME: Seems like this would cause file space leaks if + // m_leaves_have_refs is true, but consider carefully how + // m_leaves_have_refs get its value. + m_tree.get_alloc().free_(leaf_mem); + } + void replace_root_by_leaf(MemRef leaf_mem) override + { + std::unique_ptr leaf{new LeafType(m_tree.get_alloc())}; // Throws + leaf->init_from_mem(leaf_mem); + m_tree.replace_root(std::move(leaf)); // Throws + } + void replace_root_by_empty_leaf() override + { + std::unique_ptr leaf{new LeafType(m_tree.get_alloc())}; // Throws + leaf->create(m_leaves_have_refs ? Array::type_HasRefs : Array::type_Normal); // Throws + m_tree.replace_root(std::move(leaf)); // Throws + } +}; + +template +void BpTree::erase(size_t ndx, bool is_last) +{ + REALM_ASSERT_DEBUG_EX(ndx < size(), ndx, size()); + REALM_ASSERT_DEBUG(is_last == (ndx == size() - 1)); + if (root_is_leaf()) { + root_as_leaf().erase(ndx); + } + else { + size_t ndx_2 = is_last ? npos : ndx; + EraseHandler handler(*this); + BpTreeNode::erase_bptree_elem(&root_as_node(), ndx_2, handler); + } +} + +template +void BpTree::move_last_over(size_t row_ndx, size_t last_row_ndx) +{ + // Copy value from last row over + T value = get(last_row_ndx); + set(row_ndx, value); + erase(last_row_ndx, true); +} + +template +void BpTree::clear() +{ + if (root_is_leaf()) { + if (std::is_same::value && root().get_type() == Array::type_HasRefs) { + // FIXME: This is because some column types rely on integer columns + // to contain refs. + root().clear_and_destroy_children(); + } + else { + root_as_leaf().clear(); + } + } + else { + Allocator& alloc = get_alloc(); + root().destroy_deep(); + + std::unique_ptr new_root(new LeafType(alloc)); + new_root->create(); + replace_root(std::move(new_root)); + } +} + + +template +struct BpTree::AdjustHandler : BpTreeNode::UpdateHandler { + LeafType m_leaf; + const T m_diff; + AdjustHandler(BpTreeBase& tree, T diff) + : m_leaf(tree.get_alloc()) + , m_diff(diff) + { + } + + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t) final + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.adjust(0, m_leaf.size(), m_diff); + } +}; + +template +void BpTree::adjust(T diff) +{ + if (root_is_leaf()) { + root_as_leaf().adjust(0, m_root->size(), std::move(diff)); // Throws + } + else { + AdjustHandler adjust_leaf_elem(*this, std::move(diff)); + root_as_node().update_bptree_leaves(adjust_leaf_elem); // Throws + } +} + +template +void BpTree::adjust(size_t ndx, T diff) +{ + static_assert(std::is_arithmetic::value, "adjust is undefined for non-arithmetic trees"); + set(ndx, get(ndx) + diff); +} + +template +struct BpTree::AdjustGEHandler : BpTreeNode::UpdateHandler { + LeafType m_leaf; + const T m_limit, m_diff; + + AdjustGEHandler(BpTreeBase& tree, T limit, T diff) + : m_leaf(tree.get_alloc()) + , m_limit(limit) + , m_diff(diff) + { + } + + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t) final + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.adjust_ge(m_limit, m_diff); + } +}; + +template +void BpTree::adjust_ge(T limit, T diff) +{ + if (root_is_leaf()) { + root_as_leaf().adjust_ge(std::move(limit), std::move(diff)); // Throws + } + else { + AdjustGEHandler adjust_leaf_elem(*this, std::move(limit), std::move(diff)); + root_as_node().update_bptree_leaves(adjust_leaf_elem); // Throws + } +} + +template +struct BpTree::SliceHandler : public BpTreeBase::SliceHandler { +public: + SliceHandler(Allocator& alloc) + : m_leaf(alloc) + { + } + MemRef slice_leaf(MemRef leaf_mem, size_t offset, size_t size, Allocator& target_alloc) override + { + m_leaf.init_from_mem(leaf_mem); + return m_leaf.slice_and_clone_children(offset, size, target_alloc); // Throws + } + +private: + LeafType m_leaf; +}; + +template +ref_type BpTree::write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const +{ + ref_type ref; + if (root_is_leaf()) { + Allocator& alloc = Allocator::get_default(); + MemRef mem = root_as_leaf().slice_and_clone_children(slice_offset, slice_size, alloc); // Throws + Array slice(alloc); + _impl::DeepArrayDestroyGuard dg(&slice); + slice.init_from_mem(mem); + bool deep = true; + bool only_when_modified = false; + ref = slice.write(out, deep, only_when_modified); // Throws + } + else { + SliceHandler handler(get_alloc()); + ref = write_subtree(root_as_node(), slice_offset, slice_size, table_size, handler, out); // Throws + } + return ref; +} + +template +MemRef BpTree::create_leaf(Array::Type leaf_type, size_t size, T value, Allocator& alloc) +{ + bool context_flag = false; + MemRef mem = LeafType::create_array(leaf_type, context_flag, size, std::move(value), alloc); + return mem; +} + +template +void BpTree::get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf_info) const noexcept +{ + if (root_is_leaf()) { + ndx_in_leaf = ndx; + *inout_leaf_info.out_leaf = &root_as_leaf(); + return; + } + std::pair p = root_as_node().get_bptree_leaf(ndx); + inout_leaf_info.fallback->init_from_mem(p.first); + ndx_in_leaf = p.second; + *inout_leaf_info.out_leaf = inout_leaf_info.fallback; +} + +template +size_t BpTree::find_first(T value, size_t begin, size_t end) const +{ + if (root_is_leaf()) { + return root_as_leaf().find_first(value, begin, end); + } + + // FIXME: It would be better to always require that 'end' is + // specified explicitly, since Table has the size readily + // available, and Array::get_bptree_size() is deprecated. + if (end == npos) + end = size(); + + LeafType leaf_cache(get_alloc()); + size_t ndx_in_tree = begin; + while (ndx_in_tree < end) { + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &leaf_cache}; + size_t ndx_in_leaf; + get_leaf(ndx_in_tree, ndx_in_leaf, leaf_info); + size_t leaf_offset = ndx_in_tree - ndx_in_leaf; + size_t end_in_leaf = std::min(leaf->size(), end - leaf_offset); + size_t ndx = leaf->find_first(value, ndx_in_leaf, end_in_leaf); // Throws (maybe) + if (ndx != not_found) + return leaf_offset + ndx; + ndx_in_tree = leaf_offset + end_in_leaf; + } + + return not_found; +} + +template +void BpTree::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const +{ + if (root_is_leaf()) { + root_as_leaf().find_all(&result, value, 0, begin, end); // Throws + return; + } + + // FIXME: It would be better to always require that 'end' is + // specified explicitely, since Table has the size readily + // available, and Array::get_bptree_size() is deprecated. + if (end == npos) + end = size(); + + LeafType leaf_cache(get_alloc()); + size_t ndx_in_tree = begin; + while (ndx_in_tree < end) { + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &leaf_cache}; + size_t ndx_in_leaf; + get_leaf(ndx_in_tree, ndx_in_leaf, leaf_info); + size_t leaf_offset = ndx_in_tree - ndx_in_leaf; + size_t end_in_leaf = std::min(leaf->size(), end - leaf_offset); + leaf->find_all(&result, value, leaf_offset, ndx_in_leaf, end_in_leaf); // Throws + ndx_in_tree = leaf_offset + end_in_leaf; + } +} + +#if defined(REALM_DEBUG) +template +size_t BpTree::verify_leaf(MemRef mem, Allocator& alloc) +{ + LeafType leaf(alloc); + leaf.init_from_mem(mem); + leaf.verify(); + return leaf.size(); +} + +template +void BpTree::verify() const +{ + if (root_is_leaf()) { + root_as_leaf().verify(); + } + else { + root().verify_bptree(&verify_leaf); + } +} +#endif // REALM_DEBUG + +template +void BpTree::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out, + Allocator& alloc) +{ + LeafType leaf(alloc); + leaf.init_from_mem(leaf_mem); + leaf.set_parent(parent, ndx_in_parent); + leaf.to_dot(out); +} + +} // namespace realm + +#endif // REALM_BPTREE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column.hpp new file mode 100644 index 0000000..7e60766 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column.hpp @@ -0,0 +1,1852 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_HPP +#define REALM_COLUMN_HPP + +#include // unint8_t etc +#include // size_t +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +// Pre-definitions +struct CascadeState; +class StringIndex; + +template +struct ImplicitNull; + +template +struct ImplicitNull> { + static constexpr bool value = true; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = false; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = true; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = true; +}; + +// FIXME: Add specialization for ImplicitNull for float, double, StringData, BinaryData. + +template +R aggregate(const ColType& column, T target, size_t start, size_t end, size_t limit, size_t* return_ndx); + + +// Iterator with random access for Columns +template +class ColumnRandIterator : public std::iterator { +public: + ColumnRandIterator(const Column* src_col, size_t ndx = 0); + bool operator==(const ColumnRandIterator& rhs) const; + bool operator!=(const ColumnRandIterator& rhs) const; + bool operator<(const ColumnRandIterator& rhs) const; + bool operator>(const ColumnRandIterator& rhs) const; + bool operator<=(const ColumnRandIterator& rhs) const; + bool operator>=(const ColumnRandIterator& rhs) const; + ColumnRandIterator& operator+=(ptrdiff_t movement); + ColumnRandIterator& operator-=(ptrdiff_t movement); + ColumnRandIterator& operator++(); + ColumnRandIterator& operator--(); + ColumnRandIterator operator++(int); + ColumnRandIterator operator--(int); + ColumnRandIterator operator+(ptrdiff_t movement); + ColumnRandIterator operator-(ptrdiff_t movement); + ptrdiff_t operator-(const ColumnRandIterator& rawIterator); + const ColumnDataType operator*() const; + const ColumnDataType operator->() const; + const ColumnDataType operator[](ptrdiff_t offset) const; + size_t get_col_ndx() const; + +protected: + size_t m_col_ndx; + const Column* m_col; +}; + +/// Base class for all column types. +class ColumnBase { +public: + /// Get the number of entries in this column. This operation is relatively + /// slow. + virtual size_t size() const noexcept = 0; + + /// \throw LogicError Thrown if this column is not string valued. + virtual void set_string(size_t row_ndx, StringData value); + + /// Whether or not this column is nullable. + virtual bool is_nullable() const noexcept; + + /// Whether or not the value at \a row_ndx is NULL. If the column is not + /// nullable, always returns false. + virtual bool is_null(size_t row_ndx) const noexcept; + + /// Sets the value at \a row_ndx to be NULL. + /// \throw LogicError Thrown if this column is not nullable. + virtual void set_null(size_t row_ndx); + + /// Inserts the specified number of elements into this column + /// starting at the specified row index. The new elements will have the + /// default value for the column type. + /// + /// \param row_ndx The row to start insertion at. If the row_ndx is less + /// than prior_num_rows then previous rows from row_ndx onwards will be + /// moved ahead by num_rows_to_insert. + /// + /// \param num_rows_to_insert The number of rows to insert. There is no + /// restriction on this value. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param nullable Specifies whether or not this column is nullable. This + /// function may assert if nullable does not agree with \a is_nullable() + virtual void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) = 0; + + /// Removes the specified number of consecutive elements from + /// this column, starting at the specified row index. + /// + /// \param row_ndx The row to start removal at (inclusive). This must be + /// less than prior_num_rows. + /// + /// \param num_rows_to_erase The number of rows to erase. + /// The row_ndx + num_rows_to_erase must be less than prior_num_rows. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) = 0; + + /// Removes the element at the specified row index by + /// moving the element at the last row index over it. This reduces the + /// number of elements by one. + /// + /// \param row_ndx The row to erase. Must be less than prior_num_rows. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool broken_reciprocal_backlinks) = 0; + + /// Remove all elements from this column. + /// + /// \param num_rows The total number of rows in this column. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void clear(size_t num_rows, bool broken_reciprocal_backlinks) = 0; + + /// \brief Swap the elements at the specified indices. + /// + /// Behaviour is undefined if: + /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of + /// bounds) + /// - \a row_ndx_1 and \a row_ndx_2 point to the same value + virtual void swap_rows(size_t row_ndx_1, size_t row_ndx_2) = 0; + + virtual void destroy() noexcept = 0; + void move_assign(ColumnBase& col) noexcept; + + virtual ~ColumnBase() noexcept + { + } + + // Disable copying, this is not supported. + ColumnBase& operator=(const ColumnBase&) = delete; + ColumnBase(const ColumnBase&) = delete; + + // Getter function for index. For integer index, the caller must supply a + // buffer that we can store the extracted value in (it may be bitpacked, so + // we cannot return a pointer in to the Array as we do with String index). + virtual StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept = 0; + + // Search index + virtual bool supports_search_index() const noexcept; + virtual bool has_search_index() const noexcept; + virtual StringIndex* create_search_index(); + virtual void destroy_search_index() noexcept; + virtual const StringIndex* get_search_index() const noexcept; + virtual StringIndex* get_search_index() noexcept; + virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent); + + virtual Allocator& get_alloc() const noexcept = 0; + + /// Returns the 'ref' of the root array. + virtual ref_type get_ref() const noexcept = 0; + virtual MemRef get_mem() const noexcept = 0; + + virtual void replace_root_array(std::unique_ptr leaf) = 0; + virtual MemRef clone_deep(Allocator& alloc) const = 0; + virtual void detach(void) = 0; + virtual bool is_attached(void) const noexcept = 0; + + static size_t get_size_from_type_and_ref(ColumnType, ref_type, Allocator&, bool) noexcept; + + // These assume that the right column compile-time type has been + // figured out. + static size_t get_size_from_ref(ref_type root_ref, Allocator&); + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&); + + /// Write a slice of this column to the specified output stream. + virtual ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream&) const = 0; + + /// Get this column's logical index within the containing table, or npos + /// for free-standing or non-top-level columns. + size_t get_column_index() const noexcept + { + return m_column_ndx; + } + + virtual void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept = 0; + virtual size_t get_ndx_in_parent() const noexcept = 0; + virtual void set_ndx_in_parent(size_t ndx_in_parent) noexcept = 0; + + /// Called to update refs and memory pointers of this column accessor and + /// all its nested accessors, but only in cases where the logical contents + /// in strictly unchanged. Group::commit(), and + /// SharedGroup::commit_and_continue_as_read()() are examples of such + /// cases. In both those cases, the purpose is to keep user visible + /// accessors in a valid state across a commit. + virtual void update_from_parent(size_t old_baseline) noexcept = 0; + + //@{ + + /// cascade_break_backlinks_to() is called iteratively for each column by + /// Table::cascade_break_backlinks_to() with the same arguments as are + /// passed to Table::cascade_break_backlinks_to(). Link columns must + /// override it. The same is true for cascade_break_backlinks_to_all_rows(), + /// except that it is called from + /// Table::cascade_break_backlinks_to_all_rows(), and that it expects + /// Table::cascade_break_backlinks_to_all_rows() to pass the number of rows + /// in the table as \a num_rows. + + virtual void cascade_break_backlinks_to(size_t row_ndx, CascadeState&); + virtual void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&); + + //@} + + void discard_child_accessors() noexcept; + + /// For columns that are able to contain subtables, this function returns + /// the pointer to the subtable accessor at the specified row index if it + /// exists, otherwise it returns null. For other column types, this function + /// returns null. + virtual Table* get_subtable_accessor(size_t row_ndx) const noexcept; + + /// Detach and remove the subtable accessor at the specified row if it + /// exists. For column types that are unable to contain subtable, this + /// function does nothing. + virtual void discard_subtable_accessor(size_t row_ndx) noexcept; + + virtual void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + virtual void adj_acc_erase_row(size_t row_ndx) noexcept; + /// See Table::adj_acc_move_over() + virtual void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + virtual void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + virtual void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + virtual void adj_acc_clear_root_table() noexcept; + + enum { + mark_Recursive = 0x01, + mark_LinkTargets = 0x02, + mark_LinkOrigins = 0x04, + }; + + virtual void mark(int type) noexcept; + + virtual void bump_link_origin_table_version() noexcept; + + virtual int compare_values(size_t row1, size_t row2) const noexcept = 0; + + /// Refresh the dirty part of the accessor subtree rooted at this column + /// accessor. + /// + /// The following conditions are necessary and sufficient for the proper + /// operation of this function: + /// + /// - The parent table accessor (excluding its column accessors) is in a + /// valid state (already refreshed). + /// + /// - Every subtable accessor in the subtree is marked dirty if it needs to + /// be refreshed, or if it has a descendant accessor that needs to be + /// refreshed. + /// + /// - This column accessor, as well as all its descendant accessors, are in + /// structural correspondence with the underlying node hierarchy whose + /// root ref is stored in the parent (`Table::m_columns`) (see + /// AccessorConsistencyLevels). + /// + /// - The 'index in parent' property of the cached root array + /// (`root->m_ndx_in_parent`) is valid. + virtual void refresh_accessor_tree(size_t new_col_ndx, const Spec&); + + virtual void verify() const = 0; + virtual void verify(const Table&, size_t col_ndx) const; + virtual void to_dot(std::ostream&, StringData title = StringData()) const = 0; + virtual void do_dump_node_structure(std::ostream&, int level) const = 0; + +#ifdef REALM_DEBUG + void dump_node_structure() const; // To std::cerr (for GDB) + void bptree_to_dot(const Array* root, std::ostream& out) const; +#endif + +protected: + using SliceHandler = BpTreeBase::SliceHandler; + + ColumnBase(size_t column_ndx = npos) + : m_column_ndx(column_ndx) + { + } + ColumnBase(ColumnBase&&) = default; + + // Must not assume more than minimal consistency (see + // AccessorConsistencyLevels). + virtual void do_discard_child_accessors() noexcept + { + } + + //@{ + /// \tparam L Any type with an appropriate `value_type`, %size(), + /// and %get() members. + template + size_t lower_bound(const L& list, T value) const noexcept; + + template + size_t upper_bound(const L& list, T value) const noexcept; + //@} + + // Node functions + + class CreateHandler { + public: + virtual ref_type create_leaf(size_t size) = 0; + ~CreateHandler() noexcept + { + } + }; + + static ref_type create(Allocator&, size_t size, CreateHandler&); + + class LeafToDot; + virtual void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const = 0; + + template + static int compare_values(const Column* column, size_t row1, size_t row2) noexcept; + +private: + size_t m_column_ndx = npos; + + static ref_type build(size_t* rest_size_ptr, size_t fixed_height, Allocator&, CreateHandler&); +}; + + +// FIXME: Temporary class until all column types have been migrated to use BpTree interface +class ColumnBaseSimple : public ColumnBase { +public: + //@{ + /// Returns the array node at the root of this column, but note + /// that there is no guarantee that this node is an inner B+-tree + /// node or a leaf. This is the case for a MixedColumn in + /// particular. + Array* get_root_array() noexcept + { + return m_array.get(); + } + const Array* get_root_array() const noexcept + { + return m_array.get(); + } + //@} + + Allocator& get_alloc() const noexcept final + { + return m_array->get_alloc(); + } + void destroy() noexcept override + { + if (m_array) + m_array->destroy_deep(); + } + ref_type get_ref() const noexcept final + { + return m_array->get_ref(); + } + MemRef get_mem() const noexcept final + { + return m_array->get_mem(); + } + void detach() noexcept final + { + m_array->detach(); + } + bool is_attached() const noexcept final + { + return m_array->is_attached(); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept final + { + m_array->set_parent(parent, ndx_in_parent); + } + size_t get_ndx_in_parent() const noexcept final + { + return m_array->get_ndx_in_parent(); + } + void set_ndx_in_parent(size_t ndx_in_parent) noexcept override + { + m_array->set_ndx_in_parent(ndx_in_parent); + } + void update_from_parent(size_t old_baseline) noexcept override + { + m_array->update_from_parent(old_baseline); + } + MemRef clone_deep(Allocator& alloc) const override + { + return m_array->clone_deep(alloc); + } + +protected: + ColumnBaseSimple(size_t column_ndx) + : ColumnBase(column_ndx) + { + } + ColumnBaseSimple(Array* root) + : m_array(root) + { + } + std::unique_ptr m_array; + + void replace_root_array(std::unique_ptr new_root) final; + bool root_is_leaf() const noexcept + { + return !m_array->is_inner_bptree_node(); + } + + /// Introduce a new root node which increments the height of the + /// tree by one. + void introduce_new_root(ref_type new_sibling_ref, TreeInsertBase& state, bool is_append); + + static ref_type write(const Array* root, size_t slice_offset, size_t slice_size, size_t table_size, SliceHandler&, + _impl::OutputStream&); + +#if defined(REALM_DEBUG) + void tree_to_dot(std::ostream&) const; +#endif +}; + +class ColumnBaseWithIndex : public ColumnBase { +public: + ~ColumnBaseWithIndex() noexcept override + { + } + void set_ndx_in_parent(size_t ndx) noexcept override; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void move_assign(ColumnBaseWithIndex& col) noexcept; + void destroy() noexcept override; + + virtual bool supports_search_index() const noexcept override + { + return true; + } + bool has_search_index() const noexcept final + { + return bool(m_search_index); + } + StringIndex* get_search_index() noexcept final + { + return m_search_index.get(); + } + const StringIndex* get_search_index() const noexcept final + { + return m_search_index.get(); + } + void destroy_search_index() noexcept override; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent) final; + StringIndex* create_search_index() override = 0; + +protected: + using ColumnBase::ColumnBase; + ColumnBaseWithIndex(ColumnBaseWithIndex&&) = default; + std::unique_ptr m_search_index; +}; + + +/// A column (Column) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are arrays. +template +class Column : public ColumnBaseWithIndex { +public: + using value_type = T; + using LeafInfo = typename BpTree::LeafInfo; + using LeafType = typename BpTree::LeafType; + + static constexpr bool nullable = ImplicitNull::value; + + struct unattached_root_tag { + }; + + explicit Column() noexcept + : ColumnBaseWithIndex(npos) + , m_tree(Allocator::get_default()) + { + } + REALM_DEPRECATED("Initialize with ref instead") explicit Column(std::unique_ptr root) noexcept; + Column(Allocator&, ref_type, size_t column_ndx = npos); + Column(unattached_root_tag, Allocator&); + Column(Column&&) noexcept = default; + ~Column() noexcept override; + + void init_from_parent(); + void init_from_ref(Allocator&, ref_type); + void init_from_mem(Allocator&, MemRef); + // Accessor concept: + void destroy() noexcept override; + Allocator& get_alloc() const noexcept final; + ref_type get_ref() const noexcept final; + MemRef get_mem() const noexcept final; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override; + size_t get_ndx_in_parent() const noexcept final; + void set_ndx_in_parent(size_t ndx) noexcept final; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void detach() noexcept final; + bool is_attached() const noexcept final; + MemRef clone_deep(Allocator&) const override; + + void move_assign(Column&); + + static size_t get_size_from_ref(ref_type root_ref, Allocator& alloc) + { + return ColumnBase::get_size_from_ref(root_ref, alloc); + } + + size_t size() const noexcept override; + bool is_empty() const noexcept + { + return size() == 0; + } + bool is_nullable() const noexcept override; + + /// Provides access to the leaf that contains the element at the + /// specified index. Upon return \a ndx_in_leaf will be set to the + /// corresponding index relative to the beginning of the leaf. + /// + /// LeafInfo is a struct defined by the underlying BpTree + /// data structure, that provides a way for the caller to do + /// leaf caching without instantiating too many objects along + /// the way. + /// + /// This function cannot be used for modifying operations as it + /// does not ensure the presence of an unbroken chain of parent + /// accessors. For this reason, the identified leaf should always + /// be accessed through the returned const-qualified reference, + /// and never directly through the specfied fallback accessor. + void get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf) const noexcept; + + // Getting and setting values + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept override; + T back() const noexcept; + void set(size_t, T value); + void set_null(size_t) override; + void add(T value = T{}); + void insert(size_t ndx, T value = T{}, size_t num_rows = 1); + void erase(size_t row_ndx); + void erase(size_t row_ndx, bool is_last); + void move_last_over(size_t row_ndx, size_t last_row_ndx); + void clear(); + + // Index support + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override; + + // FIXME: Remove these + uint64_t get_uint(size_t ndx) const noexcept; + ref_type get_as_ref(size_t ndx) const noexcept; + void set_uint(size_t ndx, uint64_t value); + void set_as_ref(size_t ndx, ref_type value); + + template + void adjust(size_t ndx, U diff); + + template + void adjust(U diff); + + template + void adjust_ge(T limit, U diff); + + size_t count(T target) const; + + typename ColumnTypeTraits::sum_type sum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + typename ColumnTypeTraits::minmax_type maximum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + typename ColumnTypeTraits::minmax_type minimum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + double average(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const; + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(Column& out_indices, T value, size_t begin = 0, size_t end = npos) const; + + void populate_search_index(); + StringIndex* create_search_index() override; + inline bool supports_search_index() const noexcept override + { + if (realm::is_any::value) + return false; + else + return true; + } + + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to ordinary integer comparison. + size_t lower_bound(T value) const noexcept; + size_t upper_bound(T value) const noexcept; + //@} + + using const_iterator = ColumnRandIterator; + + const_iterator cbegin() const + { + return const_iterator(this, 0); // `this` is const in a const method + } + const_iterator cend() const + { + return const_iterator(this, size()); + } + + size_t find_gte(T target, size_t start) const; + + bool compare(const Column&) const noexcept; + int compare_values(size_t row1, size_t row2) const noexcept override; + + static ref_type create(Allocator&, Array::Type leaf_type = Array::type_Normal, size_t size = 0, T value = T{}); + + // Overriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + + /// \brief Swap the elements at the specified indices. + /// + /// If this \c Column has a search index defined, it will be updated to + /// reflect the changes induced by the swap. + /// + /// Behaviour is undefined if: + /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of + /// bounds) + /// - \a row_ndx_1 and \a row_ndx_2 point to the same value + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + + /// \param row_ndx Must be `realm::npos` if appending. + /// \param value The value to store at the specified row. + /// \param num_rows The number of rows to insert. + void insert_without_updating_index(size_t row_ndx, T value, size_t num_rows); + + void verify() const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; +#ifdef REALM_DEBUG + using ColumnBase::verify; + void tree_to_dot(std::ostream&) const; + MemStats stats() const; +#endif + + //@{ + /// Returns the array node at the root of this column, but note + /// that there is no guarantee that this node is an inner B+-tree + /// node or a leaf. This is the case for a MixedColumn in + /// particular. + Array* get_root_array() noexcept + { + return &m_tree.root(); + } + const Array* get_root_array() const noexcept + { + return &m_tree.root(); + } + //@} + +protected: + bool root_is_leaf() const noexcept + { + return m_tree.root_is_leaf(); + } + void replace_root_array(std::unique_ptr leaf) final + { + m_tree.replace_root(std::move(leaf)); + } + + void set_without_updating_index(size_t row_ndx, T value); + void erase_without_updating_index(size_t row_ndx, bool is_last); + void move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx); + void swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2); + + /// If any element points to an array node, this function recursively + /// destroys that array node. Note that the same is **not** true for + /// IntegerColumn::do_erase() and IntegerColumn::do_move_last_over(). + /// + /// FIXME: Be careful, clear_without_updating_index() currently forgets + /// if the leaf type is Array::type_HasRefs. + void clear_without_updating_index(); + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; +#ifdef REALM_DEBUG + static void dump_node_structure(const Array& root, std::ostream&, int level); +#endif + std::pair get_to_dot_parent(size_t ndx_in_parent) const; + +private: + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + friend class Array; + friend class ColumnBase; + friend class StringIndex; + + BpTree m_tree; + + void do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last); +}; + +// Implementation: + + +template <> +inline size_t IntNullColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) +{ + // FIXME: Speed improvement possible by not creating instance, but tricky! This slow method is OK so far + // because it's only invoked by Table::get_size_from_ref() which is only used for subtables which we + // currently 2016) do not expose publicly. + IntNullColumn inc(alloc, root_ref); + return inc.size(); +} + + +inline bool ColumnBase::supports_search_index() const noexcept +{ + REALM_ASSERT(!has_search_index()); + return false; +} + +inline bool ColumnBase::has_search_index() const noexcept +{ + return get_search_index() != nullptr; +} + +inline StringIndex* ColumnBase::create_search_index() +{ + return nullptr; +} + +inline void ColumnBase::destroy_search_index() noexcept +{ +} + +inline const StringIndex* ColumnBase::get_search_index() const noexcept +{ + return nullptr; +} + +inline StringIndex* ColumnBase::get_search_index() noexcept +{ + return nullptr; +} + +inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t) +{ +} + +inline void ColumnBase::discard_child_accessors() noexcept +{ + do_discard_child_accessors(); +} + +inline Table* ColumnBase::get_subtable_accessor(size_t) const noexcept +{ + return 0; +} + +inline void ColumnBase::discard_subtable_accessor(size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_insert_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_erase_row(size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_move_over(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_swap_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_merge_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_clear_root_table() noexcept +{ + // Noop +} + +inline void ColumnBase::mark(int) noexcept +{ + // Noop +} + +inline void ColumnBase::bump_link_origin_table_version() noexcept +{ + // Noop +} + +template +int ColumnBase::compare_values(const Column* column, size_t row1, size_t row2) noexcept +{ + // we negate nullability such that the two ternary statements in this method can look identical to reduce + // risk of bugs + bool v1 = !column->is_null(row1); + bool v2 = !column->is_null(row2); + + if (!v1 || !v2) + return v1 == v2 ? 0 : v1 < v2 ? 1 : -1; + + auto a = column->get(row1); + auto b = column->get(row2); + return a == b ? 0 : a < b ? 1 : -1; +} + +template +void Column::set_without_updating_index(size_t ndx, T value) +{ + m_tree.set(ndx, std::move(value)); +} + +template +void Column::set(size_t ndx, T value) +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (has_search_index()) { + m_search_index->set(ndx, value); + } + set_without_updating_index(ndx, std::move(value)); +} + +template +void Column::set_null(size_t ndx) +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (!is_nullable()) { + throw LogicError{LogicError::column_not_nullable}; + } + if (has_search_index()) { + m_search_index->set(ndx, null{}); + } + m_tree.set_null(ndx); +} + +// When a value of a signed type is converted to an unsigned type, the C++ standard guarantees that negative values +// are converted from the native representation to 2's complement, but the opposite conversion is left as undefined. +// realm::util::from_twos_compl() is used here to perform the correct opposite unsigned-to-signed conversion, +// which reduces to a no-op when 2's complement is the native representation of negative values. +template +void Column::set_uint(size_t ndx, uint64_t value) +{ + set(ndx, util::from_twos_compl(value)); +} + +template +void Column::set_as_ref(size_t ndx, ref_type ref) +{ + set(ndx, from_ref(ref)); +} + +template +template +void Column::adjust(size_t ndx, U diff) +{ + REALM_ASSERT_3(ndx, <, size()); + m_tree.adjust(ndx, diff); +} + +template +template +void Column::adjust(U diff) +{ + m_tree.adjust(diff); +} + +template +template +void Column::adjust_ge(T limit, U diff) +{ + m_tree.adjust_ge(limit, diff); +} + +template +size_t Column::count(T target) const +{ + if (has_search_index()) { + return m_search_index->count(target); + } + return to_size_t(aggregate(*this, target, 0, size(), npos, nullptr)); +} + +template +typename ColumnTypeTraits::sum_type Column::sum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using sum_type = typename ColumnTypeTraits::sum_type; + if (nullable) + return aggregate(*this, 0, start, end, limit, return_ndx); + else + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +double Column::average(size_t start, size_t end, size_t limit, size_t* return_ndx) const +{ + if (end == size_t(-1)) + end = size(); + + auto s = sum(start, end, limit); + size_t cnt = to_size_t(aggregate(*this, 0, start, end, limit, nullptr)); + if (return_ndx) + *return_ndx = cnt; + double avg = double(s) / (cnt == 0 ? 1 : cnt); + return avg; +} + +template +typename ColumnTypeTraits::minmax_type Column::minimum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using R = typename ColumnTypeTraits::minmax_type; + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +typename ColumnTypeTraits::minmax_type Column::maximum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using R = typename ColumnTypeTraits::minmax_type; + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +void Column::get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf_info) const noexcept +{ + m_tree.get_leaf(ndx, ndx_in_leaf, inout_leaf_info); +} + +template +StringData Column::get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept +{ + T x = get(ndx); + return to_str(x, buffer); +} + +template +void Column::populate_search_index() +{ + REALM_ASSERT(has_search_index()); + // Populate the index + size_t num_rows = size(); + for (size_t row_ndx = 0; row_ndx != num_rows; ++row_ndx) { + bool is_append = true; + if (is_null(row_ndx)) { + m_search_index->insert(row_ndx, null{}, 1, is_append); // Throws + } + else { + T value = get(row_ndx); + m_search_index->insert(row_ndx, value, 1, is_append); // Throws + } + } +} + +template +StringIndex* Column::create_search_index() +{ + if (realm::is_any::value) + return nullptr; + + REALM_ASSERT(!has_search_index()); + REALM_ASSERT(supports_search_index()); + m_search_index.reset(new StringIndex(this, get_alloc())); // Throws + populate_search_index(); + return m_search_index.get(); +} + +template +size_t Column::find_first(T value, size_t begin, size_t end) const +{ + REALM_ASSERT_3(begin, <=, size()); + REALM_ASSERT(end == npos || (begin <= end && end <= size())); + + if (m_search_index && begin == 0 && end == npos) + return m_search_index->find_first(value); + return m_tree.find_first(value, begin, end); +} + +template +void Column::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const +{ + REALM_ASSERT_3(begin, <=, size()); + REALM_ASSERT(end == npos || (begin <= end && end <= size())); + + if (m_search_index && begin == 0 && end == npos) + return m_search_index->find_all(result, value); + return m_tree.find_all(result, value, begin, end); +} + +inline size_t ColumnBase::get_size_from_ref(ref_type root_ref, Allocator& alloc) +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) + return Array::get_size_from_header(root_header); + return BpTreeNode::get_bptree_size_from_header(root_header); +} + +template +size_t ColumnBase::lower_bound(const L& list, T value) const noexcept +{ + size_t i = 0; + size_t list_size = list.size(); + while (0 < list_size) { + size_t half = list_size / 2; + size_t mid = i + half; + typename L::value_type probe = list.get(mid); + if (probe < value) { + i = mid + 1; + list_size -= half + 1; + } + else { + list_size = half; + } + } + return i; +} + +template +size_t ColumnBase::upper_bound(const L& list, T value) const noexcept +{ + size_t i = 0; + size_t list_size = list.size(); + while (0 < list_size) { + size_t half = list_size / 2; + size_t mid = i + half; + typename L::value_type probe = list.get(mid); + if (!(value < probe)) { + i = mid + 1; + list_size -= half + 1; + } + else { + list_size = half; + } + } + return i; +} + + +inline ref_type ColumnBase::create(Allocator& alloc, size_t column_size, CreateHandler& handler) +{ + size_t rest_size = column_size; + size_t fixed_height = 0; // Not fixed + return build(&rest_size, fixed_height, alloc, handler); +} + +template +Column::Column(Allocator& alloc, ref_type ref, size_t column_ndx) + : ColumnBaseWithIndex(column_ndx) + , m_tree(BpTreeBase::unattached_tag{}) +{ + // fixme, must m_search_index be copied here? + m_tree.init_from_ref(alloc, ref); +} + +template +Column::Column(unattached_root_tag, Allocator& alloc) + : ColumnBaseWithIndex(npos) + , m_tree(alloc) +{ +} + +template +Column::Column(std::unique_ptr root) noexcept + : m_tree(std::move(root)) +{ +} + +template +Column::~Column() noexcept +{ +} + +template +void Column::init_from_parent() +{ + m_tree.init_from_parent(); +} + +template +void Column::init_from_ref(Allocator& alloc, ref_type ref) +{ + m_tree.init_from_ref(alloc, ref); +} + +template +void Column::init_from_mem(Allocator& alloc, MemRef mem) +{ + m_tree.init_from_mem(alloc, mem); +} + +template +void Column::destroy() noexcept +{ + ColumnBaseWithIndex::destroy(); + m_tree.destroy(); +} + +template +void Column::move_assign(Column& col) +{ + ColumnBaseWithIndex::move_assign(col); + m_tree = std::move(col.m_tree); +} + +template +Allocator& Column::get_alloc() const noexcept +{ + return m_tree.get_alloc(); +} + +template +void Column::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_tree.set_parent(parent, ndx_in_parent); +} + +template +size_t Column::get_ndx_in_parent() const noexcept +{ + return m_tree.get_ndx_in_parent(); +} + +template +void Column::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + ColumnBaseWithIndex::set_ndx_in_parent(ndx_in_parent); + m_tree.set_ndx_in_parent(ndx_in_parent); +} + +template +void Column::detach() noexcept +{ + m_tree.detach(); +} + +template +bool Column::is_attached() const noexcept +{ + return m_tree.is_attached(); +} + +template +ref_type Column::get_ref() const noexcept +{ + return get_root_array()->get_ref(); +} + +template +MemRef Column::get_mem() const noexcept +{ + return get_root_array()->get_mem(); +} + +template +void Column::update_from_parent(size_t old_baseline) noexcept +{ + ColumnBaseWithIndex::update_from_parent(old_baseline); + m_tree.update_from_parent(old_baseline); +} + +template +MemRef Column::clone_deep(Allocator& alloc) const +{ + return m_tree.clone_deep(alloc); +} + +template +size_t Column::size() const noexcept +{ + return m_tree.size(); +} + +template +bool Column::is_nullable() const noexcept +{ + return nullable; +} + +template +T Column::get(size_t ndx) const noexcept +{ + return m_tree.get(ndx); +} + +template +bool Column::is_null(size_t ndx) const noexcept +{ + return nullable && m_tree.is_null(ndx); +} + +template +T Column::back() const noexcept +{ + return m_tree.back(); +} + +template +ref_type Column::get_as_ref(size_t ndx) const noexcept +{ + return to_ref(get(ndx)); +} + +template +uint64_t Column::get_uint(size_t ndx) const noexcept +{ + static_assert(std::is_convertible::value, "T is not convertible to uint."); + return static_cast(get(ndx)); +} + +template +void Column::add(T value) +{ + insert(npos, std::move(value)); +} + +template +void Column::insert_without_updating_index(size_t row_ndx, T value, size_t num_rows) +{ + size_t column_size = this->size(); // Slow + bool is_append = row_ndx == column_size || row_ndx == npos; + size_t ndx_or_npos_if_append = is_append ? npos : row_ndx; + + m_tree.insert(ndx_or_npos_if_append, std::move(value), num_rows); // Throws +} + +template +void Column::insert(size_t row_ndx, T value, size_t num_rows) +{ + size_t column_size = this->size(); // Slow + bool is_append = row_ndx == column_size || row_ndx == npos; + size_t ndx_or_npos_if_append = is_append ? npos : row_ndx; + + m_tree.insert(ndx_or_npos_if_append, value, num_rows); // Throws + + if (has_search_index()) { + row_ndx = is_append ? column_size : row_ndx; + m_search_index->insert(row_ndx, value, num_rows, is_append); // Throws + } +} + +template +void Column::erase_without_updating_index(size_t row_ndx, bool is_last) +{ + m_tree.erase(row_ndx, is_last); +} + +template +void Column::erase(size_t row_ndx) +{ + REALM_ASSERT(size() >= 1); + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = (row_ndx == last_row_ndx); + erase(row_ndx, is_last); // Throws +} + +template +void Column::erase(size_t row_ndx, bool is_last) +{ + size_t num_rows_to_erase = 1; + do_erase(row_ndx, num_rows_to_erase, is_last); // Throws +} + +template +void Column::move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx) +{ + m_tree.move_last_over(row_ndx, last_row_ndx); +} + +template +void Column::move_last_over(size_t row_ndx, size_t last_row_ndx) +{ + REALM_ASSERT_3(row_ndx, <=, last_row_ndx); + REALM_ASSERT_DEBUG(last_row_ndx + 1 == size()); + + if (has_search_index()) { + // remove the value to be overwritten from index + bool is_last = true; // This tells StringIndex::erase() to not adjust subsequent indexes + m_search_index->erase(row_ndx, is_last); // Throws + + // update index to point to new location + if (row_ndx != last_row_ndx) { + T moved_value = get(last_row_ndx); + m_search_index->update_ref(moved_value, last_row_ndx, row_ndx); // Throws + } + } + + move_last_over_without_updating_index(row_ndx, last_row_ndx); +} + +template +void Column::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + REALM_ASSERT_3(row_ndx_1, <, size()); + REALM_ASSERT_3(row_ndx_2, <, size()); + REALM_ASSERT_DEBUG(row_ndx_1 != row_ndx_2); + + if (has_search_index()) { + T value_1 = get(row_ndx_1); + T value_2 = get(row_ndx_2); + size_t column_size = this->size(); + bool row_ndx_1_is_last = row_ndx_1 == column_size - 1; + bool row_ndx_2_is_last = row_ndx_2 == column_size - 1; + m_search_index->erase(row_ndx_1, row_ndx_1_is_last); + m_search_index->insert(row_ndx_1, value_2, 1, row_ndx_1_is_last); + + m_search_index->erase(row_ndx_2, row_ndx_2_is_last); + m_search_index->insert(row_ndx_2, value_1, 1, row_ndx_2_is_last); + } + + swap_rows_without_updating_index(row_ndx_1, row_ndx_2); +} + +template +void Column::swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2) +{ + // FIXME: This can be optimized with direct getters and setters. + T value_1 = get(row_ndx_1); + T value_2 = get(row_ndx_2); + m_tree.set(row_ndx_1, value_2); + m_tree.set(row_ndx_2, value_1); +} + +template +void Column::clear_without_updating_index() +{ + m_tree.clear(); // Throws +} + +template +void Column::clear() +{ + if (has_search_index()) { + m_search_index->clear(); + } + clear_without_updating_index(); +} + +template +struct NullOrDefaultValue; +template +struct NullOrDefaultValue::value>::type> { + static T null_or_default_value(bool is_null) + { + if (is_null) { + return null::get_null_float(); + } + else { + return T{}; + } + } +}; +template +struct NullOrDefaultValue, void> { + static util::Optional null_or_default_value(bool is_null) + { + if (is_null) { + return util::none; + } + else { + return util::some(T{}); + } + } +}; +template +struct NullOrDefaultValue::value>::type> { + static T null_or_default_value(bool is_null) + { + REALM_ASSERT(!is_null); + return T{}; + } +}; + +// Implementing pure virtual method of ColumnBase. +template +void Column::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + T value = NullOrDefaultValue::null_or_default_value(insert_nulls); + insert(row_ndx_2, value, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + do_erase(row_ndx, num_rows_to_erase, is_last); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::clear(size_t, bool) +{ + clear(); // Throws +} + + +template +size_t Column::lower_bound(T value) const noexcept +{ + if (root_is_leaf()) { + auto root = static_cast(get_root_array()); + return root->lower_bound(value); + } + return ColumnBase::lower_bound(*this, value); +} + +template +size_t Column::upper_bound(T value) const noexcept +{ + if (root_is_leaf()) { + auto root = static_cast(get_root_array()); + return root->upper_bound(value); + } + return ColumnBase::upper_bound(*this, value); +} + +// For a *sorted* Column, return first element E for which E >= target or return -1 if none +template +size_t Column::find_gte(T target, size_t start) const +{ + // fixme: slow reference implementation. See Array::find_gte for faster version + size_t ref = 0; + size_t idx; + for (idx = start; idx < size(); ++idx) { + if (get(idx) >= target) { + ref = idx; + break; + } + } + if (idx == size()) + ref = not_found; + + return ref; +} + + +template +bool Column::compare(const Column& c) const noexcept +{ + size_t n = size(); + if (c.size() != n) + return false; + for (size_t i = 0; i < n; ++i) { + bool left_is_null = is_null(i); + bool right_is_null = c.is_null(i); + if (left_is_null != right_is_null) { + return false; + } + if (!left_is_null) { + if (get(i) != c.get(i)) + return false; + } + } + return true; +} + +template +int Column::compare_values(size_t row1, size_t row2) const noexcept +{ + return ColumnBase::compare_values(this, row1, row2); +} + +template +class Column::CreateHandler : public ColumnBase::CreateHandler { +public: + CreateHandler(Array::Type leaf_type, T value, Allocator& alloc) + : m_value(value) + , m_alloc(alloc) + , m_leaf_type(leaf_type) + { + } + ref_type create_leaf(size_t size) override + { + MemRef mem = BpTree::create_leaf(m_leaf_type, size, m_value, m_alloc); // Throws + return mem.get_ref(); + } + +private: + const T m_value; + Allocator& m_alloc; + Array::Type m_leaf_type; +}; + +template +ref_type Column::create(Allocator& alloc, Array::Type leaf_type, size_t size, T value) +{ + CreateHandler handler(leaf_type, std::move(value), alloc); + return ColumnBase::create(alloc, size, handler); +} + +template +ref_type Column::write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const +{ + return m_tree.write(slice_offset, slice_size, table_size, out); +} + +template +void Column::refresh_accessor_tree(size_t new_col_ndx, const Spec& spec) +{ + m_tree.init_from_parent(); + ColumnBaseWithIndex::refresh_accessor_tree(new_col_ndx, spec); +} + +template +void Column::do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last) +{ + if (has_search_index()) { + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + m_search_index->erase(row_ndx_2, is_last); // Throws + } + } + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + erase_without_updating_index(row_ndx_2, is_last); // Throws + } +} + +template +void Column::verify() const +{ +#ifdef REALM_DEBUG + m_tree.verify(); +#endif +} + +// LCOV_EXCL_START + +template +void Column::to_dot(std::ostream& out, StringData title) const +{ +#ifdef REALM_DEBUG + ref_type ref = get_root_array()->get_ref(); + out << "subgraph cluster_integer_column" << ref << " {" << std::endl; + out << " label = \"Integer column"; + if (title.size() != 0) + out << "\\n'" << title << "'"; + out << "\";" << std::endl; + tree_to_dot(out); + out << "}" << std::endl; +#else + static_cast(out); + static_cast(title); +#endif +} + +template +void Column::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out) const +{ +#ifdef REALM_DEBUG + BpTree::leaf_to_dot(leaf_mem, parent, ndx_in_parent, out, get_alloc()); +#else + static_cast(leaf_mem); + static_cast(parent); + static_cast(ndx_in_parent); + static_cast(out); +#endif +} + +template +void Column::do_dump_node_structure(std::ostream& out, int level) const +{ +#ifdef REALM_DEBUG + dump_node_structure(*get_root_array(), out, level); +#else + static_cast(out); + static_cast(level); +#endif +} + +#ifdef REALM_DEBUG + +template +void Column::tree_to_dot(std::ostream& out) const +{ + ColumnBase::bptree_to_dot(get_root_array(), out); +} + + +template +MemStats Column::stats() const +{ + MemStats mem_stats; + get_root_array()->stats(mem_stats); + return mem_stats; +} + +namespace _impl { +void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level); +} + +template +void Column::dump_node_structure(const Array& root, std::ostream& out, int level) +{ + root.dump_bptree_structure(out, level, &_impl::leaf_dumper); +} + +#endif + +template +std::pair Column::get_to_dot_parent(size_t ndx_in_parent) const +{ + auto root = get_root_array(); + if (root->is_inner_bptree_node()) { + std::pair p = static_cast(root)->get_bptree_leaf(ndx_in_parent); + return std::make_pair(p.first.get_ref(), p.second); + } + else { + return std::make_pair(root->get_ref(), ndx_in_parent); + } +} + +// LCOV_EXCL_STOP ignore debug functions + + +template +ColumnRandIterator::ColumnRandIterator(const Column* src_col, size_t ndx) + : m_col_ndx(ndx) + , m_col(src_col) +{ +} + +template +bool ColumnRandIterator::operator==(const ColumnRandIterator& rhs) const +{ + return (m_col_ndx == rhs.m_col_ndx); +} + +template +bool ColumnRandIterator::operator!=(const ColumnRandIterator& rhs) const +{ + return !(*this == rhs); +} + +template +bool ColumnRandIterator::operator<(const ColumnRandIterator& rhs) const +{ + return m_col_ndx < rhs.m_col_ndx; +} + +template +bool ColumnRandIterator::operator>(const ColumnRandIterator& rhs) const +{ + return rhs < *this; +} + +template +bool ColumnRandIterator::operator<=(const ColumnRandIterator& rhs) const +{ + return !(rhs < *this); +} + +template +bool ColumnRandIterator::operator>=(const ColumnRandIterator& rhs) const +{ + return !(*this < rhs); +} + +template +ColumnRandIterator& ColumnRandIterator::operator+=(ptrdiff_t movement) +{ + m_col_ndx += movement; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator-=(ptrdiff_t movement) +{ + m_col_ndx -= movement; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator++() +{ + ++m_col_ndx; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator--() +{ + --m_col_ndx; + return (*this); +} + +template +ColumnRandIterator ColumnRandIterator::operator++(int) +{ + auto temp(*this); + ++m_col_ndx; + return temp; +} + +template +ColumnRandIterator ColumnRandIterator::operator--(int) +{ + auto temp(*this); + --m_col_ndx; + return temp; +} + +template +ColumnRandIterator ColumnRandIterator::operator+(ptrdiff_t movement) +{ + return ColumnRandIterator(m_col, m_col_ndx + movement); +} + +template +ColumnRandIterator ColumnRandIterator::operator-(ptrdiff_t movement) +{ + return ColumnRandIterator(m_col, m_col_ndx - movement); +} + +template +ptrdiff_t ColumnRandIterator::operator-(const ColumnRandIterator& other) +{ + return m_col_ndx - other.m_col_ndx; +} + +template +const ColumnDataType ColumnRandIterator::operator*() const +{ + return m_col->get(m_col_ndx); +} + +template +const ColumnDataType ColumnRandIterator::operator->() const +{ + return m_col->get(m_col_ndx); +} + +template +const ColumnDataType ColumnRandIterator::operator[](ptrdiff_t offset) const +{ + return m_col->get(m_col_ndx + offset); +} + +template +size_t ColumnRandIterator::get_col_ndx() const +{ + return m_col_ndx; +} + +template +std::ostream& operator<<(std::ostream& out, const ColumnRandIterator& it) +{ + out << "ColumnRandIterator at index: " << it.get_col_ndx(); + return out; +} + +} // namespace realm + +#endif // REALM_COLUMN_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_backlink.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_backlink.hpp new file mode 100644 index 0000000..efa3cb4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_backlink.hpp @@ -0,0 +1,237 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_BACKLINK_HPP +#define REALM_COLUMN_BACKLINK_HPP + +#include + +#include +#include +#include + +namespace realm { + +/// A column of backlinks (BacklinkColumn) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are single arrays of +/// type Array with the hasRefs bit set. +/// +/// The individual values in the column are either refs to Columns containing +/// the row indexes in the origin table that links to it, or in the case where +/// there is a single link, a tagged ref encoding the origin row position. +class BacklinkColumn : public IntegerColumn, public ArrayParent { +public: + BacklinkColumn(Allocator&, ref_type, size_t col_ndx = npos); + ~BacklinkColumn() noexcept override + { + } + + static ref_type create(Allocator&, size_t size = 0); + + bool has_backlinks(size_t row_ndx) const noexcept; + size_t get_backlink_count(size_t row_ndx) const noexcept; + size_t get_backlink(size_t row_ndx, size_t backlink_ndx) const noexcept; + + void add_backlink(size_t row_ndx, size_t origin_row_ndx); + void remove_one_backlink(size_t row_ndx, size_t origin_row_ndx); + void remove_all_backlinks(size_t num_rows); + void update_backlink(size_t row_ndx, size_t old_origin_row_ndx, size_t new_origin_row_ndx); + void swap_backlinks(size_t row_ndx, size_t origin_row_ndx_1, size_t origin_row_ndx_2); + + void add_row(); + + // Link origination info + Table& get_origin_table() const noexcept; + void set_origin_table(Table&) noexcept; + LinkColumnBase& get_origin_column() const noexcept; + size_t get_origin_column_index() const noexcept; + void set_origin_column(LinkColumnBase& column) noexcept; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_merge_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + + void bump_link_origin_table_version() noexcept override; + + void cascade_break_backlinks_to(size_t row_ndx, CascadeState& state) override; + void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&) override; + + int compare_values(size_t, size_t) const noexcept override; + + void verify() const override; + void verify(const Table&, size_t) const override; +#ifdef REALM_DEBUG + struct VerifyPair { + size_t origin_row_ndx, target_row_ndx; + bool operator<(const VerifyPair&) const noexcept; + }; + void get_backlinks(std::vector&); // Sorts +#endif + +protected: + // ArrayParent overrides + void update_child_ref(size_t child_ndx, ref_type new_ref) override; + ref_type get_child_ref(size_t child_ndx) const noexcept override; + + std::pair get_to_dot_parent(size_t) const override; + +private: + TableRef m_origin_table; + LinkColumnBase* m_origin_column = nullptr; + + template + size_t for_each_link(size_t row_ndx, bool do_destroy, Func&& f); +}; + + +// Implementation + +inline BacklinkColumn::BacklinkColumn(Allocator& alloc, ref_type ref, size_t col_ndx) + : IntegerColumn(alloc, ref, col_ndx) // Throws +{ +} + +inline ref_type BacklinkColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline bool BacklinkColumn::has_backlinks(size_t ndx) const noexcept +{ + return IntegerColumn::get(ndx) != 0; +} + +inline Table& BacklinkColumn::get_origin_table() const noexcept +{ + return *m_origin_table; +} + +inline void BacklinkColumn::set_origin_table(Table& table) noexcept +{ + REALM_ASSERT(!m_origin_table); + m_origin_table = table.get_table_ref(); +} + +inline LinkColumnBase& BacklinkColumn::get_origin_column() const noexcept +{ + return *m_origin_column; +} + +inline size_t BacklinkColumn::get_origin_column_index() const noexcept +{ + return m_origin_column ? m_origin_column->get_column_index() : npos; +} + +inline void BacklinkColumn::set_origin_column(LinkColumnBase& column) noexcept +{ + m_origin_column = &column; +} + +inline void BacklinkColumn::add_row() +{ + IntegerColumn::add(0); +} + +inline void BacklinkColumn::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + IntegerColumn::adj_acc_insert_rows(row_ndx, num_rows); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_erase_row(size_t row_ndx) noexcept +{ + IntegerColumn::adj_acc_erase_row(row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + IntegerColumn::adj_acc_move_over(from_row_ndx, to_row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + Column::adj_acc_swap_rows(row_ndx_1, row_ndx_2); + + using tf = _impl::TableFriend; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept +{ + Column::adj_acc_merge_rows(old_row_ndx, new_row_ndx); + + using tf = _impl::TableFriend; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_clear_root_table() noexcept +{ + IntegerColumn::adj_acc_clear_root_table(); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::mark(int type) noexcept +{ + if (type & mark_LinkOrigins) { + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); + } +} + +inline void BacklinkColumn::bump_link_origin_table_version() noexcept +{ + // It is important to mark connected tables as modified. + // Also see LinkColumnBase::bump_link_origin_table_version(). + typedef _impl::TableFriend tf; + if (m_origin_table) { + bool bump_global = false; + tf::bump_version(*m_origin_table, bump_global); + } +} + +#ifdef REALM_DEBUG + +inline bool BacklinkColumn::VerifyPair::operator<(const VerifyPair& p) const noexcept +{ + return origin_row_ndx < p.origin_row_ndx; +} + +#endif // REALM_DEBUG + +} // namespace realm + +#endif // REALM_COLUMN_BACKLINK_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_binary.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_binary.hpp new file mode 100644 index 0000000..c891297 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_binary.hpp @@ -0,0 +1,429 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_BINARY_HPP +#define REALM_COLUMN_BINARY_HPP + +#include +#include +#include + +namespace realm { + + +/// A binary column (BinaryColumn) is a single B+-tree, and the root +/// of the column is the root of the B+-tree. Leaf nodes are either of +/// type ArrayBinary (array of small blobs) or ArrayBigBlobs (array of +/// big blobs). +class BinaryColumn : public ColumnBaseSimple { +public: + typedef BinaryData value_type; + + BinaryColumn(Allocator&, ref_type, bool nullable = false, size_t column_ndx = npos); + + size_t size() const noexcept final; + bool is_empty() const noexcept + { + return size() == 0; + } + bool is_nullable() const noexcept override; + + BinaryData get(size_t ndx) const noexcept; + + /// Return data from position 'pos' and onwards. If the blob is distributed + /// across multiple arrays (if bigger than ~ 16M), you will only get data + /// from one array. 'pos' will be updated to be an index to next available + /// data. It will be 0 if no more data. + BinaryData get_at(size_t ndx, size_t& pos) const noexcept; + + bool is_null(size_t ndx) const noexcept override; + StringData get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept final; + + void add(BinaryData value); + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void set_null(size_t ndx) override; + void insert(size_t ndx, BinaryData value); + void erase(size_t row_ndx); + void erase(size_t row_ndx, bool is_last); + void move_last_over(size_t row_ndx); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void clear(); + size_t find_first(BinaryData value) const; + + // Requires that the specified entry was inserted as StringData. + StringData get_string(size_t ndx) const noexcept; + + void add_string(StringData value); + void set_string(size_t ndx, StringData value) override; + void insert_string(size_t ndx, StringData value); + + /// Compare two binary columns for equality. + bool compare_binary(const BinaryColumn&) const; + + int compare_values(size_t row1, size_t row2) const noexcept override; + + static ref_type create(Allocator&, size_t size, bool nullable); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overrriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + /// In contrast to update_from_parent(), this function is able to handle + /// cases where the accessed payload data has changed. In particular, it + /// handles cases where the B+-tree switches from having one level (root is + /// a leaf node), to having multiple levels (root is an inner node). Note + /// that this is at the expense of loosing the `noexcept` guarantee. + void update_from_ref(ref_type ref); + + void verify() const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, BinaryData value, bool add_zero_term, size_t num_rows); + + // Called by Array::bptree_insert(). + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent&, size_t ndx_in_parent, Allocator&, size_t insert_ndx, + BpTreeNode::TreeInsert& state); + + struct InsertState : BpTreeNode::TreeInsert { + bool m_add_zero_term; + }; + + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_clear(); + + /// Root must be a leaf. Upgrades the root leaf if + /// necessary. Returns true if, and only if the root is a 'big + /// blobs' leaf upon return. + bool upgrade_root_leaf(size_t value_size); + + bool m_nullable = false; + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + friend class BpTreeNode; + friend class ColumnBase; +}; + +class BinaryIterator { +public: + BinaryIterator() + { + } + // TODO: When WriteLogCollector is removed, there is no need for this + BinaryIterator(BinaryData binary) + : m_binary(binary) + { + } + + BinaryIterator(BinaryColumn* col, size_t ndx) + : m_binary_col(col) + , m_ndx(ndx) + { + } + + BinaryData get_next() noexcept + { + if (!end_of_data) { + if (m_binary_col) { + BinaryData ret = m_binary_col->get_at(m_ndx, m_pos); + end_of_data = (m_pos == 0); + return ret; + } + else if (!m_binary.is_null()) { + end_of_data = true; + return m_binary; + } + } + return {}; + } + +private: + bool end_of_data = false; + BinaryColumn* m_binary_col = nullptr; + size_t m_ndx = 0; + size_t m_pos = 0; + BinaryData m_binary; +}; + + +// Implementation + +// LCOV_EXCL_START +inline StringData BinaryColumn::get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept +{ + REALM_ASSERT(false && "Index not implemented for BinaryColumn."); + REALM_UNREACHABLE(); +} +// LCOV_EXCL_STOP + +inline size_t BinaryColumn::size() const noexcept +{ + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Small blobs root leaf + ArrayBinary* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Big blobs root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Non-leaf root + return static_cast(m_array.get())->get_bptree_size(); +} + +inline bool BinaryColumn::is_nullable() const noexcept +{ + return m_nullable; +} + +inline void BinaryColumn::update_from_parent(size_t old_baseline) noexcept +{ + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Small blobs root leaf + REALM_ASSERT(dynamic_cast(m_array.get())); + ArrayBinary* leaf = static_cast(m_array.get()); + leaf->update_from_parent(old_baseline); + return; + } + // Big blobs root leaf + REALM_ASSERT(dynamic_cast(m_array.get())); + ArrayBigBlobs* leaf = static_cast(m_array.get()); + leaf->update_from_parent(old_baseline); + return; + } + // Non-leaf root + m_array->update_from_parent(old_baseline); +} + +inline BinaryData BinaryColumn::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + BinaryData ret; + if (!is_big) { + // Small blobs root leaf + ArrayBinary* leaf = static_cast(m_array.get()); + ret = leaf->get(ndx); + } + else { + // Big blobs root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + ret = leaf->get(ndx); + } + if (!m_nullable && ret.is_null()) + return BinaryData("", 0); // return empty string (non-null) + return ret; + } + + // Non-leaf root + std::pair p = static_cast(m_array.get())->get_bptree_leaf(ndx); + const char* leaf_header = p.first.get_addr(); + size_t ndx_in_leaf = p.second; + Allocator& alloc = m_array->get_alloc(); + bool is_big = Array::get_context_flag_from_header(leaf_header); + if (!is_big) { + // Small blobs + return ArrayBinary::get(leaf_header, ndx_in_leaf, alloc); + } + // Big blobs + return ArrayBigBlobs::get(leaf_header, ndx_in_leaf, alloc); +} + +inline bool BinaryColumn::is_null(size_t ndx) const noexcept +{ + return m_nullable && get(ndx).is_null(); +} + +inline StringData BinaryColumn::get_string(size_t ndx) const noexcept +{ + BinaryData bin = get(ndx); + REALM_ASSERT_3(0, <, bin.size()); + return StringData(bin.data(), bin.size() - 1); +} + +inline void BinaryColumn::set_string(size_t ndx, StringData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + set(ndx, bin, add_zero_term); +} + +inline void BinaryColumn::add(BinaryData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + size_t row_ndx = realm::npos; + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx, value, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::insert(size_t row_ndx, BinaryData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + size_t column_size = this->size(); // Slow + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t row_ndx_2 = row_ndx == column_size ? realm::npos : row_ndx; + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx_2, value, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::set_null(size_t row_ndx) +{ + set(row_ndx, BinaryData{}); +} + +inline size_t BinaryColumn::find_first(BinaryData value) const +{ + for (size_t t = 0; t < size(); t++) + if (get(t) == value) + return t; + + return not_found; +} + + +inline void BinaryColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + erase(row_ndx, is_last); // Throws +} + +inline void BinaryColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void BinaryColumn::clear() +{ + do_clear(); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + BinaryData value = m_nullable ? BinaryData() : BinaryData("", 0); + bool add_zero_term = false; + do_insert(row_ndx_2, value, add_zero_term, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + erase(row_ndx_2, is_last); // Throws + } +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +inline void BinaryColumn::add_string(StringData value) +{ + size_t row_ndx = realm::npos; + BinaryData value_2(value.data(), value.size()); + bool add_zero_term = true; + size_t num_rows = 1; + do_insert(row_ndx, value_2, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::insert_string(size_t row_ndx, StringData value) +{ + size_t column_size = this->size(); // Slow + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t row_ndx_2 = row_ndx == column_size ? realm::npos : row_ndx; + BinaryData value_2(value.data(), value.size()); + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx_2, value_2, add_zero_term, num_rows); // Throws +} + +inline size_t BinaryColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) { + bool is_big = Array::get_context_flag_from_header(root_header); + if (!is_big) { + // Small blobs leaf + return ArrayBinary::get_size_from_header(root_header, alloc); + } + // Big blobs leaf + return ArrayBigBlobs::get_size_from_header(root_header); + } + return BpTreeNode::get_bptree_size_from_header(root_header); +} + + +} // namespace realm + +#endif // REALM_COLUMN_BINARY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_fwd.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_fwd.hpp new file mode 100644 index 0000000..1eecf01 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_fwd.hpp @@ -0,0 +1,57 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_FWD_HPP +#define REALM_COLUMN_FWD_HPP + +#include + +namespace realm { + +// Regular classes +class ColumnBase; +class StringColumn; +class StringEnumColumn; +class BinaryColumn; +class SubtableColumn; +class MixedColumn; +class LinkColumn; +class LinkListColumn; + +// Templated classes +template +class Column; +template +class BasicColumn; +template +class ColumnRandIterator; + +namespace util { +template +class Optional; +} + +// Shortcuts, aka typedefs. +using IntegerColumn = Column; +using IntNullColumn = Column>; +using DoubleColumn = Column; +using FloatColumn = Column; +using IntegerColumnIterator = ColumnRandIterator; +} // namespace realm + +#endif // REALM_COLUMN_FWD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_link.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_link.hpp new file mode 100644 index 0000000..18471bb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_link.hpp @@ -0,0 +1,179 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINK_HPP +#define REALM_COLUMN_LINK_HPP + +#include +#include +#include + +namespace realm { + +/// A link column is an extension of an integer column (Column) and maintains +/// its node structure. +/// +/// The individual values in a link column are indexes of rows in the target +/// table (offset with one to allow zero to indicate null links.) The target +/// table is specified by the table descriptor. +class LinkColumn : public LinkColumnBase { +public: + using LinkColumnBase::LinkColumnBase; + ~LinkColumn() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + bool is_nullable() const noexcept override; + + //@{ + + /// is_null_link() is shorthand for `get_link() == realm::npos`, + /// nullify_link() is shorthand foe `set_link(realm::npos)`, and + /// insert_null_link() is shorthand for + /// `insert_link(realm::npos)`. set_link() returns the original link, with + /// `realm::npos` indicating that it was null. + + size_t get_link(size_t row_ndx) const noexcept; + bool is_null(size_t row_ndx) const noexcept override; + bool is_null_link(size_t row_ndx) const noexcept; + size_t set_link(size_t row_ndx, size_t target_row_ndx); + void set_null(size_t row_ndx) override; + void nullify_link(size_t row_ndx); + void insert_link(size_t row_ndx, size_t target_row_ndx); + void insert_null_link(size_t row_ndx); + + //@} + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void cascade_break_backlinks_to(size_t, CascadeState&) override; + void cascade_break_backlinks_to_all_rows(size_t, CascadeState&) override; + + void verify(const Table&, size_t) const override; + +protected: + friend class BacklinkColumn; + void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) override; + void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) override; + void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) override; + +private: + void remove_backlinks(size_t row_ndx); +}; + + +// Implementation + +inline LinkColumn::~LinkColumn() noexcept +{ +} + +inline bool LinkColumn::is_nullable() const noexcept +{ + return true; +} + +inline ref_type LinkColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_Normal, size); // Throws +} + +inline bool LinkColumn::is_null(size_t row_ndx) const noexcept +{ + // Null is represented by zero + return LinkColumnBase::get(row_ndx) == 0; +} + +inline size_t LinkColumn::get_link(size_t row_ndx) const noexcept +{ + // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index. + return to_size_t(LinkColumnBase::get(row_ndx)) - size_t(1); +} + +inline bool LinkColumn::is_null_link(size_t row_ndx) const noexcept +{ + return is_null(row_ndx); +} + +inline size_t LinkColumn::set_link(size_t row_ndx, size_t target_row_ndx) +{ + int_fast64_t old_value = LinkColumnBase::get(row_ndx); + size_t old_target_row_ndx = to_size_t(old_value) - size_t(1); + if (old_value != 0) + m_backlink_column->remove_one_backlink(old_target_row_ndx, row_ndx); // Throws + + int_fast64_t new_value = int_fast64_t(size_t(1) + target_row_ndx); + LinkColumnBase::set(row_ndx, new_value); // Throws + + if (target_row_ndx != realm::npos) + m_backlink_column->add_backlink(target_row_ndx, row_ndx); // Throws + + return old_target_row_ndx; +} + +inline void LinkColumn::set_null(size_t row_ndx) +{ + set_link(row_ndx, realm::npos); // Throws +} + +inline void LinkColumn::nullify_link(size_t row_ndx) +{ + set_null(row_ndx); // Throws +} + +inline void LinkColumn::insert_link(size_t row_ndx, size_t target_row_ndx) +{ + int_fast64_t value = int_fast64_t(size_t(1) + target_row_ndx); + LinkColumnBase::insert(row_ndx, value); // Throws + + if (target_row_ndx != realm::npos) + m_backlink_column->add_backlink(target_row_ndx, row_ndx); // Throws +} + +inline void LinkColumn::insert_null_link(size_t row_ndx) +{ + insert_link(row_ndx, realm::npos); // Throws +} + +inline void LinkColumn::do_update_link(size_t row_ndx, size_t, size_t new_target_row_ndx) +{ + // Row pos is offset by one, to allow null refs + LinkColumnBase::set(row_ndx, new_target_row_ndx + 1); +} + +inline void LinkColumn::do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) +{ + // Row pos is offset by one, to allow null refs + ++target_row_ndx_1; + ++target_row_ndx_2; + + uint64_t value = LinkColumnBase::get_uint(row_ndx); + if (value == target_row_ndx_1) { + LinkColumnBase::set_uint(row_ndx, target_row_ndx_2); + } + else if (value == target_row_ndx_2) { + LinkColumnBase::set_uint(row_ndx, target_row_ndx_1); + } +} + +} // namespace realm + +#endif // REALM_COLUMN_LINK_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linkbase.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linkbase.hpp new file mode 100644 index 0000000..cff46b2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linkbase.hpp @@ -0,0 +1,197 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINKBASE_HPP +#define REALM_COLUMN_LINKBASE_HPP + +#include + +namespace realm { + +class BacklinkColumn; +class Table; + +// Abstract base class for columns containing links +class LinkColumnBase : public IntegerColumn { +public: + // Create unattached root array aaccessor. + LinkColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~LinkColumnBase() noexcept override; + + bool is_nullable() const noexcept override = 0; + void set_null(size_t) override = 0; + bool is_null(size_t) const noexcept override = 0; + + bool supports_search_index() const noexcept final + { + return false; + } + StringIndex* create_search_index() override; + + bool get_weak_links() const noexcept; + void set_weak_links(bool) noexcept; + + Table& get_target_table() const noexcept; + void set_target_table(Table&) noexcept; + BacklinkColumn& get_backlink_column() const noexcept; + void set_backlink_column(BacklinkColumn&) noexcept; + + void swap_rows(size_t, size_t) override = 0; + + virtual void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) = 0; + virtual void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) = 0; + virtual void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) = 0; + + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void bump_link_origin_table_version() noexcept override; + + void verify(const Table&, size_t) const override; + using IntegerColumn::verify; + +protected: + // A pointer to the table that this column is part of. + Table* const m_table; + + TableRef m_target_table; + BacklinkColumn* m_backlink_column = nullptr; + bool m_weak_links = false; // True if these links are weak (not strong) + + /// Call Table::cascade_break_backlinks_to() for the specified target row if + /// it is not already in \a state.rows, and the number of strong links to it + /// has dropped to zero. + void check_cascade_break_backlinks_to(size_t target_table_ndx, size_t target_row_ndx, CascadeState& state); +}; + + +// Implementation + +inline LinkColumnBase::LinkColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : IntegerColumn(alloc, ref, column_ndx) // Throws + , m_table(table) +{ +} + +inline LinkColumnBase::~LinkColumnBase() noexcept +{ +} + +inline StringIndex* LinkColumnBase::create_search_index() +{ + return nullptr; +} + +inline bool LinkColumnBase::get_weak_links() const noexcept +{ + return m_weak_links; +} + +inline void LinkColumnBase::set_weak_links(bool value) noexcept +{ + m_weak_links = value; +} + +inline Table& LinkColumnBase::get_target_table() const noexcept +{ + return *m_target_table; +} + +inline void LinkColumnBase::set_target_table(Table& table) noexcept +{ + REALM_ASSERT(!m_target_table); + m_target_table = table.get_table_ref(); +} + +inline BacklinkColumn& LinkColumnBase::get_backlink_column() const noexcept +{ + return *m_backlink_column; +} + +inline void LinkColumnBase::set_backlink_column(BacklinkColumn& column) noexcept +{ + m_backlink_column = &column; +} + +inline void LinkColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + IntegerColumn::adj_acc_insert_rows(row_ndx, num_rows); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept +{ + IntegerColumn::adj_acc_erase_row(row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + IntegerColumn::adj_acc_move_over(from_row_ndx, to_row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + IntegerColumn::adj_acc_swap_rows(row_ndx_1, row_ndx_2); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_clear_root_table() noexcept +{ + IntegerColumn::adj_acc_clear_root_table(); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::mark(int type) noexcept +{ + if (type & mark_LinkTargets) { + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); + } +} + +inline void LinkColumnBase::bump_link_origin_table_version() noexcept +{ + // It is important to mark connected tables as modified. + // Also see BacklinkColumn::bump_link_origin_table_version(). + typedef _impl::TableFriend tf; + if (m_target_table) { + bool bump_global = false; + tf::bump_version(*m_target_table, bump_global); + } +} + + +} // namespace realm + +#endif // REALM_COLUMN_LINKBASE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linklist.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linklist.hpp new file mode 100644 index 0000000..5683114 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_linklist.hpp @@ -0,0 +1,244 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINKLIST_HPP +#define REALM_COLUMN_LINKLIST_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace realm { + +namespace _impl { +class TransactLogConvenientEncoder; +} + + +/// A column of link lists (LinkListColumn) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are single arrays of +/// type Array with the hasRefs bit set. +/// +/// The individual values in the column are either refs to Columns containing the +/// row positions in the target table, or in the case where they are empty, a zero +/// ref. +class LinkListColumn : public LinkColumnBase, public ArrayParent { +public: + using LinkColumnBase::LinkColumnBase; + using value_type = ConstLinkViewRef; + LinkListColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~LinkListColumn() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + bool is_nullable() const noexcept final; + + bool has_links(size_t row_ndx) const noexcept; + size_t get_link_count(size_t row_ndx) const noexcept; + + ConstLinkViewRef get(size_t row_ndx) const; + LinkViewRef get(size_t row_ndx); + + bool is_null(size_t row_ndx) const noexcept final; + void set_null(size_t row_ndx) final; + + /// Compare two columns for equality. + bool compare_link_list(const LinkListColumn&) const; + + void to_json_row(size_t row_ndx, std::ostream& out) const; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void cascade_break_backlinks_to(size_t, CascadeState&) override; + void cascade_break_backlinks_to_all_rows(size_t, CascadeState&) override; + void update_from_parent(size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_merge_rows(size_t, size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + +protected: + void do_discard_child_accessors() noexcept override; + +private: + struct list_entry { + size_t m_row_ndx; + std::weak_ptr m_list; + bool operator<(const list_entry& other) const + { + return m_row_ndx < other.m_row_ndx; + } + }; + + // The accessors stored in `m_list_accessors` are sorted by their row index. + // When a LinkList accessor is destroyed because the last shared_ptr pointing + // to it dies, its entry is implicitly replaced by a tombstone (an entry with + // an empty `m_list`). These tombstones are pruned at a later time by + // `prune_list_accessor_tombstones`. This is done to amortize the O(n) cost + // of `std::vector::erase` that would otherwise be incurred each time an + // accessor is removed. + mutable std::vector m_list_accessors; + mutable std::atomic m_list_accessors_contains_tombstones; + + std::shared_ptr get_ptr(size_t row_ndx) const; + + void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) override; + void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) override; + void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) override; + + void unregister_linkview(); + ref_type get_row_ref(size_t row_ndx) const noexcept; + void set_row_ref(size_t row_ndx, ref_type new_ref); + void add_backlink(size_t target_row, size_t source_row); + void remove_backlink(size_t target_row, size_t source_row); + + // ArrayParent overrides + void update_child_ref(size_t child_ndx, ref_type new_ref) override; + ref_type get_child_ref(size_t child_ndx) const noexcept override; + + // These helpers are needed because of the way the B+-tree of links is + // traversed in cascade_break_backlinks_to() and + // cascade_break_backlinks_to_all_rows(). + void cascade_break_backlinks_to__leaf(size_t row_ndx, const Array& link_list_leaf, CascadeState&); + void cascade_break_backlinks_to_all_rows__leaf(const Array& link_list_leaf, CascadeState&); + + void discard_child_accessors() noexcept; + + template + void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept; + + template + void adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept; + + template + void adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + template + void adj_swap(size_t row_ndx_1, size_t row_ndx_2) noexcept; + + void prune_list_accessor_tombstones() noexcept; + void validate_list_accessors() const noexcept; + + std::pair get_to_dot_parent(size_t) const override; + + friend class BacklinkColumn; + friend class LinkView; + friend class _impl::TransactLogConvenientEncoder; +}; + + +// Implementation + +inline LinkListColumn::LinkListColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : LinkColumnBase(alloc, ref, table, column_ndx) +{ + m_list_accessors_contains_tombstones.store(false); +} + +inline LinkListColumn::~LinkListColumn() noexcept +{ + discard_child_accessors(); +} + +inline ref_type LinkListColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline bool LinkListColumn::is_nullable() const noexcept +{ + return false; +} + +inline bool LinkListColumn::has_links(size_t row_ndx) const noexcept +{ + ref_type ref = LinkColumnBase::get_as_ref(row_ndx); + return (ref != 0); +} + +inline size_t LinkListColumn::get_link_count(size_t row_ndx) const noexcept +{ + ref_type ref = LinkColumnBase::get_as_ref(row_ndx); + if (ref == 0) + return 0; + return ColumnBase::get_size_from_ref(ref, get_alloc()); +} + +inline ConstLinkViewRef LinkListColumn::get(size_t row_ndx) const +{ + return get_ptr(row_ndx); +} + +inline LinkViewRef LinkListColumn::get(size_t row_ndx) +{ + return get_ptr(row_ndx); +} + +inline bool LinkListColumn::is_null(size_t) const noexcept +{ + return false; +} + +inline void LinkListColumn::set_null(size_t) +{ + throw LogicError{LogicError::column_not_nullable}; +} + +inline void LinkListColumn::do_discard_child_accessors() noexcept +{ + discard_child_accessors(); +} + +inline ref_type LinkListColumn::get_row_ref(size_t row_ndx) const noexcept +{ + return LinkColumnBase::get_as_ref(row_ndx); +} + +inline void LinkListColumn::set_row_ref(size_t row_ndx, ref_type new_ref) +{ + LinkColumnBase::set(row_ndx, new_ref); // Throws +} + +inline void LinkListColumn::add_backlink(size_t target_row, size_t source_row) +{ + m_backlink_column->add_backlink(target_row, source_row); // Throws +} + +inline void LinkListColumn::remove_backlink(size_t target_row, size_t source_row) +{ + m_backlink_column->remove_one_backlink(target_row, source_row); // Throws +} + + +} // namespace realm + +#endif // REALM_COLUMN_LINKLIST_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed.hpp new file mode 100644 index 0000000..b3d111e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed.hpp @@ -0,0 +1,267 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_MIXED_HPP +#define REALM_COLUMN_MIXED_HPP + +#include + +#include +#include +#include +#include +#include + + +namespace realm { + + +// Pre-declarations +class BinaryColumn; + + +/// A mixed column (MixedColumn) is composed of three subcolumns. The first +/// subcolumn is an integer column (Column) and stores value types. The second +/// one stores values and is a subtable parent column (SubtableColumnBase), +/// which is a subclass of an integer column (Column). The last one is a binary +/// column (BinaryColumn) and stores additional data for values of type string +/// or binary data. The last subcolumn is optional. The root of a mixed column +/// is an array node of type Array that stores the root refs of the subcolumns. +class MixedColumn : public ColumnBaseSimple { +public: + /// Create a mixed column wrapper and attach it to a preexisting + /// underlying structure of arrays. + /// + /// \param alloc The memory allocator to change the underlying + /// structure in memory. + /// + /// \param ref The memory reference of the MixedColumn for which + /// this accessor should be creator for. + /// + /// \param table If this column is used as part of a table you + /// must pass a pointer to that table. Otherwise you must pass + /// null + /// + /// \param column_ndx If this column is used as part of a table + /// you must pass the logical index of the column within that + /// table. Otherwise you should pass zero. + MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + + ~MixedColumn() noexcept override; + + DataType get_type(size_t ndx) const noexcept; + size_t size() const noexcept final + { + return m_types->size(); + } + bool is_empty() const noexcept + { + return size() == 0; + } + + int64_t get_int(size_t ndx) const noexcept; + bool get_bool(size_t ndx) const noexcept; + OldDateTime get_olddatetime(size_t ndx) const noexcept; + Timestamp get_timestamp(size_t ndx) const noexcept; + float get_float(size_t ndx) const noexcept; + double get_double(size_t ndx) const noexcept; + StringData get_string(size_t ndx) const noexcept; + BinaryData get_binary(size_t ndx) const noexcept; + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override; + + /// The returned array ref is zero if the specified row does not + /// contain a subtable. + ref_type get_subtable_ref(size_t row_ndx) const noexcept; + + /// The returned size is zero if the specified row does not + /// contain a subtable. + size_t get_subtable_size(size_t row_ndx) const noexcept; + + Table* get_subtable_accessor(size_t row_ndx) const noexcept override; + + void discard_subtable_accessor(size_t row_ndx) noexcept override; + + /// If the value at the specified index is a subtable, return a + /// pointer to that accessor for that subtable. Otherwise return + /// null. The accessor will be created if it does not already + /// exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t row_ndx); + + const Table* get_subtable_ptr(size_t subtable_ndx) const; + + void set_int(size_t ndx, int64_t value); + void set_bool(size_t ndx, bool value); + void set_olddatetime(size_t ndx, OldDateTime value); + void set_timestamp(size_t ndx, Timestamp value); + void set_float(size_t ndx, float value); + void set_double(size_t ndx, double value); + void set_string(size_t ndx, StringData value) override; + void set_binary(size_t ndx, BinaryData value); + void set_subtable(size_t ndx, const Table* value); + + void insert_int(size_t ndx, int_fast64_t value); + void insert_bool(size_t ndx, bool value); + void insert_olddatetime(size_t ndx, OldDateTime value); + void insert_timestamp(size_t ndx, Timestamp value); + void insert_float(size_t ndx, float value); + void insert_double(size_t ndx, double value); + void insert_string(size_t ndx, StringData value); + void insert_binary(size_t ndx, BinaryData value); + void insert_subtable(size_t ndx, const Table* value); + + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void clear(); + + /// Compare two mixed columns for equality. + bool compare_mixed(const MixedColumn&) const; + + int compare_values(size_t row1, size_t row2) const noexcept override; + + void discard_child_accessors() noexcept; + + static ref_type create(Allocator&, size_t size = 0); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + enum MixedColType { + // NOTE: below numbers must be kept in sync with ColumnType + // Column types used in Mixed + mixcol_Int = 0, + mixcol_Bool = 1, + mixcol_String = 2, + // 3, used for STRING_ENUM in ColumnType + mixcol_Binary = 4, + mixcol_Table = 5, + mixcol_Mixed = 6, + mixcol_OldDateTime = 7, + mixcol_Timestamp = 8, + mixcol_Float = 9, + mixcol_Double = 10, // Positive Double + mixcol_DoubleNeg = 11, // Negative Double + mixcol_IntNeg = 12 // Negative Integers + }; + + class RefsColumn; + + /// Stores the MixedColType of each value at the given index. For + /// values that uses all 64 bits, the type also encodes the sign + /// bit by having distinct types for positive negative values. + std::unique_ptr m_types; + + /// Stores the data for each entry. For a subtable, the stored + /// value is the ref of the subtable. For string, binary data, + /// the stored value is an index within `m_binary_data`. Likewise, + /// for timestamp, an index into `m_timestamp` is stored. For other + /// types the stored value is itself. Since we only have 63 bits + /// available for a non-ref value, the sign of numeric values is + /// encoded as part of the type in `m_types`. + std::unique_ptr m_data; + + /// For string and binary data types, the bytes are stored here. + std::unique_ptr m_binary_data; + + /// Timestamps are stored here. + std::unique_ptr m_timestamp_data; + + void do_erase(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows); + void do_move_last_over(size_t row_ndx, size_t prior_num_rows); + void do_swap_rows(size_t, size_t); + void do_clear(size_t num_rows); + + void create(Allocator&, ref_type, Table*, size_t column_ndx); + void ensure_binary_data_column(); + void ensure_timestamp_column(); + + MixedColType clear_value(size_t ndx, MixedColType new_type); // Returns old type + void clear_value_and_discard_subtab_acc(size_t ndx, MixedColType new_type); + + // Get/set/insert 64-bit values in m_data/m_types + int64_t get_value(size_t ndx) const noexcept; + void set_value(size_t ndx, int64_t value, MixedColType); + void set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type); + + void insert_value(size_t row_ndx, int_fast64_t types_value, int_fast64_t data_value); + void insert_int(size_t ndx, int_fast64_t value, MixedColType type); + void insert_pos_neg(size_t ndx, int_fast64_t value, MixedColType pos_type, MixedColType neg_type); + + void do_discard_child_accessors() noexcept override; + +#ifdef REALM_DEBUG + void do_verify(const Table*, size_t col_ndx) const; +#endif + void leaf_to_dot(MemRef, ArrayParent*, size_t, std::ostream&) const override; +}; + +// LCOV_EXCL_START +inline StringData MixedColumn::get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept +{ + REALM_ASSERT(false && "Index not supported for MixedColumn yet."); + REALM_UNREACHABLE(); + return {}; +} +// LCOV_EXCL_STOP + + +class MixedColumn::RefsColumn : public SubtableColumnBase { +public: + RefsColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~RefsColumn() noexcept override; + + using SubtableColumnBase::get_subtable_ptr; + + void refresh_accessor_tree(size_t, const Spec&) override; + + friend class MixedColumn; +}; + + +} // namespace realm + + +// Implementation +#include + + +#endif // REALM_COLUMN_MIXED_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp new file mode 100644 index 0000000..39d1f08 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp @@ -0,0 +1,510 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#include +#include + +namespace realm { + +inline MixedColumn::MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : ColumnBaseSimple(column_ndx) +{ + create(alloc, ref, table, column_ndx); +} + +inline void MixedColumn::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + m_data->adj_acc_insert_rows(row_ndx, num_rows); +} + +inline void MixedColumn::adj_acc_erase_row(size_t row_ndx) noexcept +{ + m_data->adj_acc_erase_row(row_ndx); +} + +inline void MixedColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + m_data->adj_acc_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void MixedColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + m_data->adj_acc_move_over(from_row_ndx, to_row_ndx); +} + +inline void MixedColumn::adj_acc_clear_root_table() noexcept +{ + m_data->adj_acc_clear_root_table(); +} + +inline ref_type MixedColumn::get_subtable_ref(size_t row_ndx) const noexcept +{ + REALM_ASSERT_3(row_ndx, <, m_types->size()); + if (m_types->get(row_ndx) != type_Table) + return 0; + return m_data->get_as_ref(row_ndx); +} + +inline size_t MixedColumn::get_subtable_size(size_t row_ndx) const noexcept +{ + ref_type top_ref = get_subtable_ref(row_ndx); + if (top_ref == 0) + return 0; + return _impl::TableFriend::get_size_from_ref(top_ref, m_data->get_alloc()); +} + +inline Table* MixedColumn::get_subtable_accessor(size_t row_ndx) const noexcept +{ + return m_data->get_subtable_accessor(row_ndx); +} + +inline void MixedColumn::discard_subtable_accessor(size_t row_ndx) noexcept +{ + m_data->discard_subtable_accessor(row_ndx); +} + +inline Table* MixedColumn::get_subtable_ptr(size_t row_ndx) +{ + REALM_ASSERT_3(row_ndx, <, m_types->size()); + if (m_types->get(row_ndx) != type_Table) + return 0; + return m_data->get_subtable_ptr(row_ndx); // Throws +} + +inline const Table* MixedColumn::get_subtable_ptr(size_t subtable_ndx) const +{ + return const_cast(this)->get_subtable_ptr(subtable_ndx); +} + +inline void MixedColumn::discard_child_accessors() noexcept +{ + m_data->discard_child_accessors(); +} + + +// +// Getters +// + +#define REALM_BIT63 0x8000000000000000ULL + +inline int64_t MixedColumn::get_value(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // Shift the unsigned value right - ensuring 0 gets in from left. + // Shifting signed integers right doesn't ensure 0's. + uint64_t value = uint64_t(m_data->get(ndx)) >> 1; + return int64_t(value); +} + +inline int64_t MixedColumn::get_int(size_t ndx) const noexcept +{ + // Get first 63 bits of the integer value + int64_t value = get_value(ndx); + + // restore 'sign'-bit from the column-type + MixedColType col_type = MixedColType(m_types->get(ndx)); + if (col_type == mixcol_IntNeg) { + // FIXME: Bad cast of result of '|' from unsigned to signed + value |= REALM_BIT63; // set sign bit (63) + } + else { + REALM_ASSERT_3(col_type, ==, mixcol_Int); + } + return value; +} + +inline bool MixedColumn::get_bool(size_t ndx) const noexcept +{ + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Bool); + + return (get_value(ndx) != 0); +} + +inline OldDateTime MixedColumn::get_olddatetime(size_t ndx) const noexcept +{ + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_OldDateTime); + + return OldDateTime(get_value(ndx)); +} + +inline float MixedColumn::get_float(size_t ndx) const noexcept +{ + static_assert(std::numeric_limits::is_iec559, "'float' is not IEEE"); + static_assert((sizeof(float) * CHAR_BIT == 32), "Assume 32 bit float."); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Float); + + return type_punning(get_value(ndx)); +} + +inline double MixedColumn::get_double(size_t ndx) const noexcept +{ + static_assert(std::numeric_limits::is_iec559, "'double' is not IEEE"); + static_assert((sizeof(double) * CHAR_BIT == 64), "Assume 64 bit double."); + + int64_t int_val = get_value(ndx); + + // restore 'sign'-bit from the column-type + MixedColType col_type = MixedColType(m_types->get(ndx)); + if (col_type == mixcol_DoubleNeg) + int_val |= REALM_BIT63; // set sign bit (63) + else { + REALM_ASSERT_3(col_type, ==, mixcol_Double); + } + return type_punning(int_val); +} + +inline StringData MixedColumn::get_string(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_String); + REALM_ASSERT(m_binary_data); + + size_t data_ndx = size_t(int64_t(m_data->get(ndx)) >> 1); + return m_binary_data->get_string(data_ndx); +} + +inline BinaryData MixedColumn::get_binary(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Binary); + REALM_ASSERT(m_binary_data); + + size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1); + return m_binary_data->get(data_ndx); +} + +inline Timestamp MixedColumn::get_timestamp(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Timestamp); + REALM_ASSERT(m_timestamp_data); + size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1); + return m_timestamp_data->get(data_ndx); +} + +// +// Setters +// + +// Set a int64 value. +// Store 63 bit of the value in m_data. Store sign bit in m_types. + +inline void MixedColumn::set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // If sign-bit is set in value, 'store' it in the column-type + MixedColType coltype = ((value & REALM_BIT63) == 0) ? pos_type : neg_type; + + // Remove refs or binary data (sets type to double) + clear_value_and_discard_subtab_acc(ndx, coltype); // Throws + + // Shift value one bit and set lowest bit to indicate that this is not a ref + value = (value << 1) + 1; + m_data->set(ndx, value); +} + +inline void MixedColumn::set_int(size_t ndx, int64_t value) +{ + set_int64(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws +} + +inline void MixedColumn::set_double(size_t ndx, double value) +{ + int64_t val64 = type_punning(value); + set_int64(ndx, val64, mixcol_Double, mixcol_DoubleNeg); // Throws +} + +inline void MixedColumn::set_value(size_t ndx, int64_t value, MixedColType coltype) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // Remove refs or binary data (sets type to float) + clear_value_and_discard_subtab_acc(ndx, coltype); // Throws + + // Shift value one bit and set lowest bit to indicate that this is not a ref + int64_t v = (value << 1) + 1; + m_data->set(ndx, v); // Throws +} + +inline void MixedColumn::set_float(size_t ndx, float value) +{ + int64_t val64 = type_punning(value); + set_value(ndx, val64, mixcol_Float); // Throws +} + +inline void MixedColumn::set_bool(size_t ndx, bool value) +{ + set_value(ndx, (value ? 1 : 0), mixcol_Bool); // Throws +} + +inline void MixedColumn::set_olddatetime(size_t ndx, OldDateTime value) +{ + set_value(ndx, int64_t(value.get_olddatetime()), mixcol_OldDateTime); // Throws +} + +inline void MixedColumn::set_subtable(size_t ndx, const Table* t) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + typedef _impl::TableFriend tf; + ref_type ref; + if (t) { + ref = tf::clone(*t, get_alloc()); // Throws + } + else { + ref = tf::create_empty_table(get_alloc()); // Throws + } + // Remove any previous refs or binary data + clear_value_and_discard_subtab_acc(ndx, mixcol_Table); // Throws + m_data->set(ndx, ref); // Throws +} + +// +// Inserts +// + +inline void MixedColumn::insert_value(size_t row_ndx, int_fast64_t types_value, int_fast64_t data_value) +{ + size_t types_size = m_types->size(); // Slow + bool is_append = row_ndx == types_size; + size_t row_ndx_2 = is_append ? realm::npos : row_ndx; + size_t num_rows = 1; + m_types->insert_without_updating_index(row_ndx_2, types_value, num_rows); // Throws + m_data->do_insert(row_ndx_2, data_value, num_rows); // Throws +} + +// Insert a int64 value. +// Store 63 bit of the value in m_data. Store sign bit in m_types. + +inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value, MixedColType type) +{ + int_fast64_t types_value = type; + // Shift value one bit and set lowest bit to indicate that this is not a ref + int_fast64_t data_value = 1 + (value << 1); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::insert_pos_neg(size_t ndx, int_fast64_t value, MixedColType pos_type, MixedColType neg_type) +{ + // 'store' the sign-bit in the integer-type + MixedColType type = (value & REALM_BIT63) == 0 ? pos_type : neg_type; + int_fast64_t types_value = type; + // Shift value one bit and set lowest bit to indicate that this is not a ref + int_fast64_t data_value = 1 + (value << 1); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value) +{ + insert_pos_neg(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws +} + +inline void MixedColumn::insert_double(size_t ndx, double value) +{ + int_fast64_t value_2 = type_punning(value); + insert_pos_neg(ndx, value_2, mixcol_Double, mixcol_DoubleNeg); // Throws +} + +inline void MixedColumn::insert_float(size_t ndx, float value) +{ + int_fast64_t value_2 = type_punning(value); + insert_int(ndx, value_2, mixcol_Float); // Throws +} + +inline void MixedColumn::insert_bool(size_t ndx, bool value) +{ + int_fast64_t value_2 = int_fast64_t(value); + insert_int(ndx, value_2, mixcol_Bool); // Throws +} + +inline void MixedColumn::insert_olddatetime(size_t ndx, OldDateTime value) +{ + int_fast64_t value_2 = int_fast64_t(value.get_olddatetime()); + insert_int(ndx, value_2, mixcol_OldDateTime); // Throws +} + +inline void MixedColumn::insert_timestamp(size_t ndx, Timestamp value) +{ + ensure_timestamp_column(); + size_t data_ndx = m_timestamp_data->size(); + m_timestamp_data->add(value); // Throws + insert_int(ndx, int_fast64_t(data_ndx), mixcol_Timestamp); +} + +inline void MixedColumn::insert_string(size_t ndx, StringData value) +{ + ensure_binary_data_column(); + size_t blob_ndx = m_binary_data->size(); + m_binary_data->add_string(value); // Throws + + int_fast64_t value_2 = int_fast64_t(blob_ndx); + insert_int(ndx, value_2, mixcol_String); // Throws +} + +inline void MixedColumn::insert_binary(size_t ndx, BinaryData value) +{ + ensure_binary_data_column(); + size_t blob_ndx = m_binary_data->size(); + m_binary_data->add(value); // Throws + + int_fast64_t value_2 = int_fast64_t(blob_ndx); + insert_int(ndx, value_2, mixcol_Binary); // Throws +} + +inline void MixedColumn::insert_subtable(size_t ndx, const Table* t) +{ + typedef _impl::TableFriend tf; + ref_type ref; + if (t) { + ref = tf::clone(*t, get_alloc()); // Throws + } + else { + ref = tf::create_empty_table(get_alloc()); // Throws + } + int_fast64_t types_value = mixcol_Table; + int_fast64_t data_value = int_fast64_t(ref); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::erase(size_t row_ndx) +{ + size_t num_rows_to_erase = 1; + size_t prior_num_rows = size(); // Note that size() is slow + do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws +} + +inline void MixedColumn::move_last_over(size_t row_ndx) +{ + size_t prior_num_rows = size(); // Note that size() is slow + do_move_last_over(row_ndx, prior_num_rows); // Throws +} + +inline void MixedColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + do_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void MixedColumn::clear() +{ + size_t num_rows = size(); // Note that size() is slow + do_clear(num_rows); // Throws +} + +inline size_t MixedColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + ref_type types_ref = to_ref(Array::get(root_header, 0)); + return IntegerColumn::get_size_from_ref(types_ref, alloc); +} + +inline void MixedColumn::clear_value_and_discard_subtab_acc(size_t row_ndx, MixedColType new_type) +{ + MixedColType old_type = clear_value(row_ndx, new_type); + if (old_type == mixcol_Table) + m_data->discard_subtable_accessor(row_ndx); +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + + int_fast64_t type_value = mixcol_Int; + m_types->insert_without_updating_index(row_ndx_2, type_value, num_rows_to_insert); // Throws + + // The least significant bit indicates that the rest of the bits form an + // integer value, so 1 is actually zero. + int_fast64_t data_value = 1; + m_data->do_insert(row_ndx_2, data_value, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + do_move_last_over(row_ndx, prior_num_rows); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::clear(size_t num_rows, bool) +{ + do_clear(num_rows); // Throws +} + +inline void MixedColumn::mark(int type) noexcept +{ + m_data->mark(type); +} + +inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + ColumnBaseSimple::refresh_accessor_tree(col_ndx, spec); + + get_root_array()->init_from_parent(); + m_types->refresh_accessor_tree(col_ndx, spec); // Throws + m_data->refresh_accessor_tree(col_ndx, spec); // Throws + + m_binary_data.reset(); + // See if m_binary_data needs to be created. + if (get_root_array()->size() >= 3) { + ref_type ref = get_root_array()->get_as_ref(2); + m_binary_data.reset(new BinaryColumn(get_alloc(), ref)); // Throws + m_binary_data->set_parent(get_root_array(), 2); + } + + m_timestamp_data.reset(); + // See if m_timestamp_data needs to be created. + if (get_root_array()->size() >= 4) { + ref_type ref = get_root_array()->get_as_ref(3); + // When adding/creating a Mixed column the user cannot specify nullability, so the "true" below + // makes it implicitly nullable, which may not be wanted. But it's OK since Mixed columns are not + // publicly supported + m_timestamp_data.reset(new TimestampColumn(true /*fixme*/, get_alloc(), ref)); // Throws + m_timestamp_data->set_parent(get_root_array(), 3); + } + + if (m_binary_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 3); + m_binary_data->refresh_accessor_tree(col_ndx, spec); // Throws + } + if (m_timestamp_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 4); + m_timestamp_data->refresh_accessor_tree(col_ndx, spec); // Throws + } +} + +inline void MixedColumn::RefsColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws + size_t spec_ndx_in_parent = 0; // Ignored because these are root tables + m_subtable_map.refresh_accessor_tree(spec_ndx_in_parent); // Throws +} + +} // namespace realm diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string.hpp new file mode 100644 index 0000000..e7693ee --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string.hpp @@ -0,0 +1,377 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_STRING_HPP +#define REALM_COLUMN_STRING_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { + +// Pre-declarations +class StringIndex; + + +/// A string column (StringColumn) is a single B+-tree, and +/// the root of the column is the root of the B+-tree. Leaf nodes are +/// either of type ArrayString (array of small strings), +/// ArrayStringLong (array of medium strings), or ArrayBigBlobs (array +/// of big strings). +/// +/// A string column can optionally be equipped with a search index. If +/// it is, then the root ref of the index is stored in +/// Table::m_columns immediately after the root ref of the string +/// column. +class StringColumn : public ColumnBaseSimple { +public: + typedef StringData value_type; + + StringColumn(Allocator&, ref_type, bool nullable = false, size_t column_ndx = npos); + ~StringColumn() noexcept override; + + void destroy() noexcept override; + + size_t size() const noexcept final; + bool is_empty() const noexcept + { + return size() == 0; + } + + bool is_null(size_t ndx) const noexcept final; + void set_null(size_t ndx) final; + StringData get(size_t ndx) const noexcept; + void set(size_t ndx, StringData); + void add(); + void add(StringData value); + void insert(size_t ndx); + void insert(size_t ndx, StringData value); + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void clear(); + + size_t count(StringData value) const; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& result, StringData value, size_t begin = 0, size_t end = npos) const; + FindRes find_all_no_copy(StringData value, InternalFindResult& result) const; + + int compare_values(size_t, size_t) const noexcept override; + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to StringData::operator<(). + size_t lower_bound_string(StringData value) const noexcept; + size_t upper_bound_string(StringData value) const noexcept; + //@} + + void set_string(size_t, StringData) override; + + bool is_nullable() const noexcept final; + + // Search index + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; + bool has_search_index() const noexcept override; + void set_search_index_ref(ref_type, ArrayParent*, size_t) override; + StringIndex* get_search_index() noexcept override; + const StringIndex* get_search_index() const noexcept override; + std::unique_ptr release_search_index() noexcept; + bool supports_search_index() const noexcept final + { + return true; + } + StringIndex* create_search_index() override; + + // Simply inserts all column values in the index in a loop + void populate_search_index(); + void destroy_search_index() noexcept override; + + // Optimizing data layout. enforce == true will enforce enumeration; + // enforce == false will auto-evaluate if it should be enumerated or not + bool auto_enumerate(ref_type& keys, ref_type& values, bool enforce = false) const; + + /// Compare two string columns for equality. + bool compare_string(const StringColumn&) const; + + enum LeafType { + leaf_type_Small, ///< ArrayString + leaf_type_Medium, ///< ArrayStringLong + leaf_type_Big ///< ArrayBigBlobs + }; + + std::unique_ptr get_leaf(size_t ndx, size_t& out_ndx_in_parent, LeafType& out_leaf_type) const; + + static ref_type create(Allocator&, size_t size = 0); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overrriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void set_ndx_in_parent(size_t ndx_in_parent) noexcept override; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + std::unique_ptr m_search_index; + bool m_nullable; + + LeafType get_block(size_t ndx, ArrayParent**, size_t& off, bool use_retval = false) const; + + /// If you are appending and have the size of the column readily available, + /// call the 4 argument version instead. If you are not appending, either + /// one is fine. + /// + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, StringData value, size_t num_rows); + + /// If you are appending and you do not have the size of the column readily + /// available, call the 3 argument version instead. If you are not + /// appending, either one is fine. + /// + /// \param is_append Must be true if, and only if `row_ndx` is equal to the + /// size of the column (before insertion). + void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append); + + /// \param row_ndx Must be `realm::npos` if appending. + void bptree_insert(size_t row_ndx, StringData value, size_t num_rows); + + // Called by Array::bptree_insert(). + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent&, size_t ndx_in_parent, Allocator&, size_t insert_ndx, + BpTreeNode::TreeInsert& state); + + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + void do_erase(size_t row_ndx, bool is_last); + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void do_clear(); + + /// Root must be a leaf. Upgrades the root leaf as + /// necessary. Returns the type of the root leaf as it is upon + /// return. + LeafType upgrade_root_leaf(size_t value_size); + + void refresh_root_accessor(); + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + friend class BpTreeNode; + friend class ColumnBase; +}; + + +// Implementation: + +inline size_t StringColumn::size() const noexcept +{ + if (root_is_leaf()) { + bool long_strings = m_array->has_refs(); + if (!long_strings) { + // Small strings root leaf + ArrayString* leaf = static_cast(m_array.get()); + return leaf->size(); + } + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Medium strings root leaf + ArrayStringLong* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Big strings root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Non-leaf root + BpTreeNode* node = static_cast(m_array.get()); + return node->get_bptree_size(); +} + +inline void StringColumn::add(StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + size_t row_ndx = realm::npos; + size_t num_rows = 1; + do_insert(row_ndx, value, num_rows); // Throws +} + +inline void StringColumn::add() +{ + add(m_nullable ? realm::null() : StringData("")); +} + +inline void StringColumn::insert(size_t row_ndx, StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + size_t column_size = this->size(); + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t num_rows = 1; + bool is_append = row_ndx == column_size; + do_insert(row_ndx, value, num_rows, is_append); // Throws +} + +inline void StringColumn::insert(size_t row_ndx) +{ + insert(row_ndx, m_nullable ? realm::null() : StringData("")); +} + +inline void StringColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + do_erase(row_ndx, is_last); // Throws +} + +inline void StringColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void StringColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + do_swap_rows(row_ndx_1, row_ndx_2); // Throws +} + +inline void StringColumn::clear() +{ + do_clear(); // Throws +} + +inline int StringColumn::compare_values(size_t row1, size_t row2) const noexcept +{ + StringData a = get(row1); + StringData b = get(row2); + + if (a.is_null() && !b.is_null()) + return 1; + else if (b.is_null() && !a.is_null()) + return -1; + else if (a.is_null() && b.is_null()) + return 0; + + if (a == b) + return 0; + return utf8_compare(a, b) ? 1 : -1; +} + +inline void StringColumn::set_string(size_t row_ndx, StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + set(row_ndx, value); // Throws +} + +inline bool StringColumn::has_search_index() const noexcept +{ + return m_search_index != 0; +} + +inline StringIndex* StringColumn::get_search_index() noexcept +{ + return m_search_index.get(); +} + +inline const StringIndex* StringColumn::get_search_index() const noexcept +{ + return m_search_index.get(); +} + +inline size_t StringColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) { + bool long_strings = Array::get_hasrefs_from_header(root_header); + if (!long_strings) { + // Small strings leaf + return ArrayString::get_size_from_header(root_header); + } + bool is_big = Array::get_context_flag_from_header(root_header); + if (!is_big) { + // Medium strings leaf + return ArrayStringLong::get_size_from_header(root_header, alloc); + } + // Big strings leaf + return ArrayBigBlobs::get_size_from_header(root_header); + } + + return BpTreeNode::get_bptree_size_from_header(root_header); +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + StringData value = m_nullable ? realm::null() : StringData(""); + bool is_append = (row_ndx == prior_num_rows); + do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + do_erase(row_ndx_2, is_last); // Throws + } +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +} // namespace realm + +#endif // REALM_COLUMN_STRING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string_enum.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string_enum.hpp new file mode 100644 index 0000000..9656e9b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_string_enum.hpp @@ -0,0 +1,310 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_STRING_ENUM_HPP +#define REALM_COLUMN_STRING_ENUM_HPP + +#include + +namespace realm { + +// Pre-declarations +class StringIndex; + + +/// From the point of view of the application, an enumerated strings column +/// (StringEnumColumn) is like a string column (StringColumn), yet it manages +/// its strings in such a way that each unique string is stored only once. In +/// fact, an enumerated strings column is a combination of two subcolumns; a +/// regular string column (StringColumn) that stores the unique strings, and an +/// integer column that stores one unique string index for each entry in the +/// enumerated strings column. +/// +/// In terms of the underlying node structure, the subcolumn containing the +/// unique strings is not a true part of the enumerated strings column. Instead +/// it is a part of the spec structure that describes the table of which the +/// enumerated strings column is a part. This way, the unique strings can be +/// shared across enumerated strings columns of multiple subtables. This also +/// means that the root of an enumerated strings column coincides with the root +/// of the integer subcolumn, and in some sense, an enumerated strings column is +/// just the integer subcolumn. +/// +/// An enumerated strings column can optionally be equipped with a +/// search index. If it is, then the root ref of the index is stored +/// in Table::m_columns immediately after the root ref of the +/// enumerated strings column. +class StringEnumColumn : public IntegerColumn { +public: + typedef StringData value_type; + + StringEnumColumn(Allocator&, ref_type ref, ref_type keys_ref, bool nullable, size_t column_ndx = npos); + ~StringEnumColumn() noexcept override; + void destroy() noexcept override; + MemRef clone_deep(Allocator& alloc) const override; + + int compare_values(size_t row1, size_t row2) const noexcept override + { + StringData a = get(row1); + StringData b = get(row2); + + if (a.is_null() && !b.is_null()) + return 1; + else if (b.is_null() && !a.is_null()) + return -1; + else if (a.is_null() && b.is_null()) + return 0; + + if (a == b) + return 0; + + return utf8_compare(a, b) ? 1 : -1; + } + + StringData get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept final; + void set(size_t ndx, StringData value); + void set_null(size_t ndx) override; + void add(); + void add(StringData value); + void insert(size_t ndx); + void insert(size_t ndx, StringData value); + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void clear(); + bool is_nullable() const noexcept final; + + size_t count(StringData value) const; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& res, StringData value, size_t begin = 0, size_t end = npos) const; + FindRes find_all_no_copy(StringData value, InternalFindResult& result) const; + + size_t count(size_t key_index) const; + size_t find_first(size_t key_index, size_t begin = 0, size_t end = -1) const; + void find_all(IntegerColumn& res, size_t key_index, size_t begin = 0, size_t end = -1) const; + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to StringData::operator<(). + size_t lower_bound_string(StringData value) const noexcept; + size_t upper_bound_string(StringData value) const noexcept; + //@} + + void set_string(size_t, StringData) override; + + void adjust_keys_ndx_in_parent(int diff) noexcept; + + // Search index + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; + bool supports_search_index() const noexcept final + { + return true; + } + StringIndex* create_search_index() override; + void install_search_index(std::unique_ptr) noexcept; + void destroy_search_index() noexcept override; + + // Compare two string columns for equality + bool compare_string(const StringColumn&) const; + bool compare_string(const StringEnumColumn&) const; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + size_t get_key_ndx(StringData value) const; + size_t get_key_ndx_or_add(StringData value); + + StringColumn& get_keys(); + const StringColumn& get_keys() const; + +#ifdef REALM_DEBUG + void verify() const override; + void verify(const Table&, size_t) const override; + void do_dump_node_structure(std::ostream&, int) const override; + void to_dot(std::ostream&, StringData title) const override; +#endif + +private: + // Member variables + StringColumn m_keys; + bool m_nullable; + + /// If you are appending and have the size of the column readily available, + /// call the 4 argument version instead. If you are not appending, either + /// one is fine. + /// + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, StringData value, size_t num_rows); + + /// If you are appending and you do not have the size of the column readily + /// available, call the 3 argument version instead. If you are not + /// appending, either one is fine. + /// + /// \param is_append Must be true if, and only if `row_ndx` is equal to the + /// size of the column (before insertion). + void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append); + + void do_erase(size_t row_ndx, bool is_last); + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_clear(); +}; + + +// Implementation: + +inline StringData StringEnumColumn::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, IntegerColumn::size()); + size_t key_ndx = to_size_t(IntegerColumn::get(ndx)); + StringData sd = m_keys.get(key_ndx); + REALM_ASSERT_DEBUG(!(!m_nullable && sd.is_null())); + return sd; +} + +inline bool StringEnumColumn::is_null(size_t ndx) const noexcept +{ + return is_nullable() && get(ndx).is_null(); +} + +inline void StringEnumColumn::add() +{ + add(m_nullable ? realm::null() : StringData("")); +} + +inline void StringEnumColumn::add(StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + size_t row_ndx = realm::npos; + size_t num_rows = 1; + do_insert(row_ndx, value, num_rows); // Throws +} + +inline void StringEnumColumn::insert(size_t row_ndx) +{ + insert(row_ndx, m_nullable ? realm::null() : StringData("")); +} + +inline void StringEnumColumn::insert(size_t row_ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + size_t column_size = this->size(); + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t num_rows = 1; + bool is_append = row_ndx == column_size; + do_insert(row_ndx, value, num_rows, is_append); // Throws +} + +inline void StringEnumColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + do_erase(row_ndx, is_last); // Throws +} + +inline void StringEnumColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void StringEnumColumn::clear() +{ + do_clear(); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + StringData value = m_nullable ? realm::null() : StringData(""); + bool is_append = (row_ndx == prior_num_rows); + do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + do_erase(row_ndx_2, is_last); // Throws + } +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +inline size_t StringEnumColumn::lower_bound_string(StringData value) const noexcept +{ + return ColumnBase::lower_bound(*this, value); +} + +inline size_t StringEnumColumn::upper_bound_string(StringData value) const noexcept +{ + return ColumnBase::upper_bound(*this, value); +} + +inline void StringEnumColumn::set_string(size_t row_ndx, StringData value) +{ + set(row_ndx, value); // Throws +} + +inline void StringEnumColumn::set_null(size_t row_ndx) +{ + set(row_ndx, realm::null{}); +} + +inline StringColumn& StringEnumColumn::get_keys() +{ + return m_keys; +} + +inline const StringColumn& StringEnumColumn::get_keys() const +{ + return m_keys; +} + + +} // namespace realm + +#endif // REALM_COLUMN_STRING_ENUM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_table.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_table.hpp new file mode 100644 index 0000000..47768b8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_table.hpp @@ -0,0 +1,607 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TABLE_HPP +#define REALM_COLUMN_TABLE_HPP + +#include + +#include +#include +#include +#include + +namespace realm { + + +/// Base class for any type of column that can contain subtables. +// FIXME: Don't derive from IntegerColumn, but define a BpTree specialization. +class SubtableColumnBase : public IntegerColumn, public Table::Parent { +public: + void discard_child_accessors() noexcept; + + ~SubtableColumnBase() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + Table* get_subtable_accessor(size_t) const noexcept override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void swap_rows(size_t, size_t) override; + void discard_subtable_accessor(size_t) noexcept override; + void update_from_parent(size_t) noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void mark(int) noexcept override; + bool supports_search_index() const noexcept override + { + return false; + } + StringIndex* create_search_index() override + { + return nullptr; + } + + void verify() const override; + void verify(const Table&, size_t) const override; + +protected: + /// A pointer to the table that this column is part of. For a free-standing + /// column, this pointer is null. + Table* const m_table; + + struct SubtableMap { + ~SubtableMap() noexcept + { + } + bool empty() const noexcept + { + return m_entries.empty(); + } + Table* find(size_t subtable_ndx) const noexcept; + void add(size_t subtable_ndx, Table*); + // Returns true if, and only if at least one entry was detached and + // removed from the map. + bool detach_and_remove_all() noexcept; + // Returns true if, and only if the entry was found and removed, and it + // was the last entry in the map. + bool detach_and_remove(size_t subtable_ndx) noexcept; + // Returns true if, and only if the entry was found and removed, and it + // was the last entry in the map. + bool remove(Table*) noexcept; + void update_from_parent(size_t old_baseline) const noexcept; + template + void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept; + // Returns true if, and only if an entry was found and removed, and it + // was the last entry in the map. + template + bool adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept; + // Returns true if, and only if an entry was found and removed, and it + // was the last entry in the map. + template + bool adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + template + void adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + + void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater&); + void recursive_mark() noexcept; + void refresh_accessor_tree(size_t spec_ndx_in_parent); + + private: + struct SubtableEntry { + size_t m_subtable_ndx; + Table* m_table; + }; + typedef std::vector entries; + entries m_entries; + }; + + /// Contains all existing accessors that are attached to a subtable in this + /// column. It can map a row index into a pointer to the corresponding + /// accessor when it exists. + /// + /// There is an invariant in force: Either `m_table` is null, or there is an + /// additional referece count on `*m_table` when, and only when the map is + /// non-empty. + mutable SubtableMap m_subtable_map; + + SubtableColumnBase(Allocator&, ref_type, Table*, size_t column_ndx); + + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + /// + /// NOTE: This method must be used only for subtables with + /// independent specs, i.e. for elements of a MixedColumn. + Table* get_subtable_ptr(size_t subtable_ndx); + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + // Overriding method in Table::Parent + Table* get_parent_table(size_t*) noexcept override; + + // Overriding method in Table::Parent + void child_accessor_destroyed(Table*) noexcept override; + + /// Assumes that the two tables have the same spec. + static bool compare_subtable_rows(const Table&, const Table&); + + /// Construct a copy of the columns array of the specified table + /// and return just the ref to that array. + /// + /// In the clone, no string column will be of the enumeration + /// type. + ref_type clone_table_columns(const Table*); + + size_t* record_subtable_path(size_t* begin, size_t* end) noexcept override; + + void update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater&); + + /// \param row_ndx Must be `realm::npos` if appending. + /// \param value The value to place in any newly created rows. + /// \param num_rows The number of rows to insert. + void do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows); + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + + friend class Table; +}; + + +class SubtableColumn : public SubtableColumnBase { +public: + using value_type = ConstTableRef; + /// Create a subtable column accessor and attach it to a + /// preexisting underlying structure of arrays. + /// + /// \param alloc The allocator to provide new memory. + /// + /// \param ref The memory reference of the underlying subtable that + /// we are creating an accessor for. + /// + /// \param table If this column is used as part of a table you must + /// pass a pointer to that table. Otherwise you must pass null. + /// + /// \param column_ndx If this column is used as part of a table + /// you must pass the logical index of the column within that + /// table. Otherwise you should pass zero. + SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + + ~SubtableColumn() noexcept override + { + } + + size_t get_subtable_size(size_t ndx) const noexcept; + + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t subtable_ndx); + + ConstTableRef get(size_t subtable_ndx) const + { + return ConstTableRef(get_subtable_ptr(subtable_ndx)); + } + + const Table* get_subtable_ptr(size_t subtable_ndx) const; + + // When passing a table to add() or insert() it is assumed that + // the table spec is compatible with this column. The number of + // columns must be the same, and the corresponding columns must + // have the same data type (as returned by + // Table::get_column_type()). + + void add(const Table* value = nullptr); + void insert(size_t ndx, const Table* value = nullptr); + void set(size_t ndx, const Table*); + void clear_table(size_t ndx); + + using SubtableColumnBase::insert; + + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + + /// Compare two subtable columns for equality. + bool compare_table(const SubtableColumn&) const; + + void refresh_accessor_tree(size_t, const Spec&) override; + +#ifdef REALM_DEBUG + void verify(const Table&, size_t) const override; + void do_dump_node_structure(std::ostream&, int) const override; + void to_dot(std::ostream&, StringData title) const override; +#endif + +private: + mutable size_t m_subspec_ndx; // Unknown if equal to `npos` + + size_t get_subspec_ndx() const noexcept; + + void destroy_subtable(size_t ndx) noexcept; + + void do_discard_child_accessors() noexcept override; +}; + + +// Implementation + +// Overriding virtual method of Column. +inline void SubtableColumnBase::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + int_fast64_t value = 0; + do_insert(row_ndx_2, value, num_rows_to_insert); // Throws +} + +// Overriding virtual method of Column. +inline void SubtableColumnBase::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) +{ + IntegerColumn::erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks); // Throws + + const bool fix_ndx_in_parent = true; + bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_to_erase); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +// Overriding virtual method of Column. +inline void SubtableColumnBase::move_last_row_over(size_t row_ndx, size_t prior_num_rows, + bool broken_reciprocal_backlinks) +{ + IntegerColumn::move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks); // Throws + + const bool fix_ndx_in_parent = true; + size_t last_row_ndx = prior_num_rows - 1; + bool last_entry_removed = m_subtable_map.adj_move_over(last_row_ndx, row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::clear(size_t, bool) +{ + discard_child_accessors(); + clear_without_updating_index(); // Throws + // FIXME: This one is needed because + // IntegerColumn::clear_without_updating_index() forgets about the + // leaf type. A better solution should probably be sought after. + get_root_array()->set_type(Array::type_HasRefs); +} + +inline void SubtableColumnBase::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + IntegerColumn::swap_rows(row_ndx_1, row_ndx_2); // Throws + + const bool fix_ndx_in_parent = true; + m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void SubtableColumnBase::mark(int type) noexcept +{ + if (type & mark_Recursive) + m_subtable_map.recursive_mark(); +} + +inline void SubtableColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + m_subtable_map.adj_insert_rows(row_ndx, num_rows); +} + +inline void SubtableColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + size_t num_rows_erased = 1; + bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_erased); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + bool last_entry_removed = m_subtable_map.adj_move_over(from_row_ndx, to_row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::adj_acc_clear_root_table() noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + IntegerColumn::adj_acc_clear_root_table(); + discard_child_accessors(); +} + +inline void SubtableColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + const bool fix_ndx_in_parent = false; + m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); +} + +inline Table* SubtableColumnBase::get_subtable_accessor(size_t row_ndx) const noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + Table* subtable = m_subtable_map.find(row_ndx); + return subtable; +} + +inline void SubtableColumnBase::discard_subtable_accessor(size_t row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + bool last_entry_removed = m_subtable_map.detach_and_remove(row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::SubtableMap::add(size_t subtable_ndx, Table* table) +{ + SubtableEntry e; + e.m_subtable_ndx = subtable_ndx; + e.m_table = table; + m_entries.push_back(e); +} + +template +void SubtableColumnBase::SubtableMap::adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept +{ + for (auto& entry : m_entries) { + if (entry.m_subtable_ndx >= row_ndx) { + entry.m_subtable_ndx += num_rows_inserted; + typedef _impl::TableFriend tf; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } +} + +template +bool SubtableColumnBase::SubtableMap::adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept +{ + if (m_entries.empty()) + return false; + typedef _impl::TableFriend tf; + auto end = m_entries.end(); + auto i = m_entries.begin(); + do { + if (i->m_subtable_ndx >= row_ndx + num_rows_erased) { + i->m_subtable_ndx -= num_rows_erased; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(i->m_table), i->m_subtable_ndx); + } + else if (i->m_subtable_ndx >= row_ndx) { + // Must hold a counted reference while detaching + TableRef table(i->m_table); + tf::detach(*table); + // Move last over + *i = *--end; + continue; + } + ++i; + } while (i != end); + m_entries.erase(end, m_entries.end()); + return m_entries.empty(); +} + + +template +bool SubtableColumnBase::SubtableMap::adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + typedef _impl::TableFriend tf; + + size_t i = 0, n = m_entries.size(); + // We return true if, and only if we remove the last entry in the map. We + // need special handling for the case, where the set of entries are already + // empty, otherwise the final return statement would return true in this + // case, even though we didn't actually remove an entry. + if (n == 0) + return false; + + while (i < n) { + SubtableEntry& e = m_entries[i]; + if (REALM_UNLIKELY(e.m_subtable_ndx == to_row_ndx)) { + // Must hold a counted reference while detaching + TableRef table(e.m_table); + tf::detach(*table); + // Delete entry by moving last over (faster and avoids invalidating + // iterators) + e = m_entries[--n]; + m_entries.pop_back(); + } + else { + if (REALM_UNLIKELY(e.m_subtable_ndx == from_row_ndx)) { + e.m_subtable_ndx = to_row_ndx; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(e.m_table), e.m_subtable_ndx); + } + ++i; + } + } + return m_entries.empty(); +} + +template +void SubtableColumnBase::SubtableMap::adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + using tf = _impl::TableFriend; + for (auto& entry : m_entries) { + if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_1)) { + entry.m_subtable_ndx = row_ndx_2; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + else if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_2)) { + entry.m_subtable_ndx = row_ndx_1; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } +} + +inline SubtableColumnBase::SubtableColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : IntegerColumn(alloc, ref, column_ndx) // Throws + , m_table(table) +{ +} + +inline void SubtableColumnBase::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + set(child_ndx, new_ref); +} + +inline ref_type SubtableColumnBase::get_child_ref(size_t child_ndx) const noexcept +{ + return get_as_ref(child_ndx); +} + +inline void SubtableColumnBase::discard_child_accessors() noexcept +{ + bool last_entry_removed = m_subtable_map.detach_and_remove_all(); + if (last_entry_removed && m_table) + _impl::TableFriend::unbind_ptr(*m_table); +} + +inline SubtableColumnBase::~SubtableColumnBase() noexcept +{ + discard_child_accessors(); +} + +inline bool SubtableColumnBase::compare_subtable_rows(const Table& a, const Table& b) +{ + return _impl::TableFriend::compare_rows(a, b); +} + +inline ref_type SubtableColumnBase::clone_table_columns(const Table* t) +{ + return _impl::TableFriend::clone_columns(*t, get_root_array()->get_alloc()); +} + +inline ref_type SubtableColumnBase::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline size_t* SubtableColumnBase::record_subtable_path(size_t* begin, size_t* end) noexcept +{ + if (end == begin) + return 0; // Error, not enough space in buffer + *begin++ = get_column_index(); + if (end == begin) + return 0; // Error, not enough space in buffer + return _impl::TableFriend::record_subtable_path(*m_table, begin, end); +} + +inline void SubtableColumnBase::update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater& updater) +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + m_subtable_map.update_accessors(col_path_begin, col_path_end, updater); // Throws +} + +inline void SubtableColumnBase::do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows) +{ + IntegerColumn::insert_without_updating_index(row_ndx, value, num_rows); // Throws + bool is_append = row_ndx == realm::npos; + if (!is_append) { + const bool fix_ndx_in_parent = true; + m_subtable_map.adj_insert_rows(row_ndx, num_rows); + } +} + + +inline SubtableColumn::SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : SubtableColumnBase(alloc, ref, table, column_ndx) + , m_subspec_ndx(realm::npos) +{ +} + +inline const Table* SubtableColumn::get_subtable_ptr(size_t subtable_ndx) const +{ + return const_cast(this)->get_subtable_ptr(subtable_ndx); +} + +inline void SubtableColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws + m_subspec_ndx = spec.get_subspec_ndx(col_ndx); + m_subtable_map.refresh_accessor_tree(m_subspec_ndx); // Throws +} + +inline size_t SubtableColumn::get_subspec_ndx() const noexcept +{ + if (REALM_UNLIKELY(m_subspec_ndx == realm::npos)) { + typedef _impl::TableFriend tf; + const Spec& spec = tf::get_spec(*m_table); + m_subspec_ndx = spec.get_subspec_ndx(get_column_index()); + } + return m_subspec_ndx; +} + + +} // namespace realm + +#endif // REALM_COLUMN_TABLE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_timestamp.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_timestamp.hpp new file mode 100644 index 0000000..d5c4859 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_timestamp.hpp @@ -0,0 +1,155 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TIMESTAMP_HPP +#define REALM_COLUMN_TIMESTAMP_HPP + +#include +#include + +namespace realm { + +// Inherits from ColumnTemplate to get a compare_values() that can be called without knowing the +// column type +class TimestampColumn : public ColumnBaseSimple { +public: + TimestampColumn(bool nullable, Allocator& alloc, ref_type ref, size_t col_ndx = npos); + + static ref_type create(Allocator& alloc, size_t size, bool nullable); + static size_t get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept; + + /// Get the number of entries in this column. This operation is relatively + /// slow. + size_t size() const noexcept override; + /// Whether or not this column is nullable. + bool is_nullable() const noexcept override; + /// Whether or not the value at \a row_ndx is NULL. If the column is not + /// nullable, always returns false. + bool is_null(size_t row_ndx) const noexcept override; + /// Sets the value at \a row_ndx to be NULL. + /// \throw LogicError Thrown if this column is not nullable. + void set_null(size_t row_ndx) override; + void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) override; + void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) override; + void move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool broken_reciprocal_backlinks) override; + void clear(size_t num_rows, bool broken_reciprocal_backlinks) override; + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void destroy() noexcept override; + + bool has_search_index() const noexcept final + { + return bool(m_search_index); + } + StringIndex* get_search_index() noexcept final + { + return m_search_index.get(); + } + StringIndex* get_search_index() const noexcept final + { + return m_search_index.get(); + } + void destroy_search_index() noexcept override; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent) final; + void populate_search_index(); + StringIndex* create_search_index() override; + bool supports_search_index() const noexcept final + { + return true; + } + + StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept override; + ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream&) const override; + void update_from_parent(size_t old_baseline) noexcept override; + void set_ndx_in_parent(size_t ndx) noexcept override; + void refresh_accessor_tree(size_t new_col_ndx, const Spec&) override; + + void verify() const override; + void to_dot(std::ostream&, StringData title = StringData()) const override; + void do_dump_node_structure(std::ostream&, int level) const override; + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + void add(const Timestamp& ts = Timestamp{}); + Timestamp get(size_t row_ndx) const noexcept; + void set(size_t row_ndx, const Timestamp& ts); + bool compare(const TimestampColumn& c) const noexcept; + int compare_values(size_t row1, size_t row2) const noexcept override; + + Timestamp maximum(size_t* result_index) const; + Timestamp minimum(size_t* result_index) const; + size_t count(Timestamp) const; + void erase(size_t row_ndx, bool is_last); + + template + size_t find(Timestamp value, size_t begin, size_t end) const noexcept + { + // FIXME: Here we can do all sorts of clever optimizations. Use bithack-search on seconds, then for each match + // check nanoseconds, etc. Lots of possibilities. Below code is naive and slow but works. + + Condition cond; + for (size_t t = begin; t < end; t++) { + Timestamp ts = get(t); + if (cond(ts, value, ts.is_null(), value.is_null())) + return t; + } + return npos; + } + + typedef Timestamp value_type; + +private: + std::unique_ptr>> m_seconds; + std::unique_ptr> m_nanoseconds; + + std::unique_ptr m_search_index; + bool m_nullable; + + template + class CreateHandler; + + template + Timestamp minmax(size_t* result_index) const noexcept + { + // Condition is realm::Greater for maximum and realm::Less for minimum. Any non-null value is both larger + // and smaller than a null value. + if (size() == 0) { + if (result_index) + *result_index = npos; + return Timestamp{}; + } + + Timestamp best = get(0); + size_t best_index = best.is_null() ? npos : 0; + + for (size_t i = 1; i < size(); ++i) { + Timestamp candidate = get(i); + // Condition() will return false if any of the two values are null. + if ((best.is_null() && !candidate.is_null()) || Condition()(candidate, best, candidate.is_null(), best.is_null())) { + best = candidate; + best_index = i; + } + } + if (result_index) + *result_index = best_index; + return best; + } +}; + +} // namespace realm + +#endif // REALM_COLUMN_TIMESTAMP_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_tpl.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_tpl.hpp new file mode 100644 index 0000000..2411007 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_tpl.hpp @@ -0,0 +1,143 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TPL_HPP +#define REALM_COLUMN_TPL_HPP + +#include + +#include +#include +#include + +namespace realm { + +template +class FloatDoubleNode; +template +class IntegerNode; +template +class SequentialGetter; + +template +struct ColumnTypeTraits2; + +template +struct ColumnTypeTraits2 { + typedef IntegerColumn column_type; + typedef ArrayInteger array_type; +}; +template +struct ColumnTypeTraits2 { + typedef IntegerColumn column_type; + typedef ArrayInteger array_type; +}; +template +struct ColumnTypeTraits2 { + typedef FloatColumn column_type; + typedef ArrayFloat array_type; +}; +template +struct ColumnTypeTraits2 { + typedef DoubleColumn column_type; + typedef ArrayDouble array_type; +}; + + +namespace _impl { + +template +struct FindInLeaf { + using LeafType = typename ColType::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + Condition cond; + bool cont = true; + // todo, make an additional loop with hard coded `false` instead of is_null(v) for non-nullable columns + bool null_target = null::is_null_float(target); + for (size_t local_index = local_start; cont && local_index < local_end; local_index++) { + auto v = leaf.get(local_index); + if (cond(v, target, null::is_null_float(v), null_target)) { + cont = state.template match(leaf_start + local_index, 0, static_cast(v)); + } + } + return cont; + } +}; + +template <> +struct FindInLeaf { + using LeafType = IntegerColumn::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + const int c = Condition::condition; + return leaf.find(c, action, target, local_start, local_end, leaf_start, &state); + } +}; + +template <> +struct FindInLeaf { + using LeafType = IntNullColumn::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + constexpr int cond = Condition::condition; + return leaf.find(cond, action, target, local_start, local_end, leaf_start, &state); + } +}; + +} // namespace _impl + +template +R aggregate(const ColType& column, T target, size_t start, size_t end, size_t limit, size_t* return_ndx) +{ + if (end == npos) + end = column.size(); + + QueryState state; + state.init(action, nullptr, limit); + SequentialGetter sg{&column}; + + bool cont = true; + for (size_t s = start; cont && s < end;) { + sg.cache_next(s); + size_t start2 = s - sg.m_leaf_start; + size_t end2 = sg.local_end(end); + cont = _impl::FindInLeaf::template find(*sg.m_leaf_ptr, target, start2, end2, + sg.m_leaf_start, state); + s = sg.m_leaf_start + end2; + } + + if (return_ndx) + *return_ndx = action == act_Sum ? state.m_match_count : state.m_minmax_index; + + return state.m_state; +} + + +} // namespace realm + +#endif // REALM_COLUMN_TPL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type.hpp new file mode 100644 index 0000000..5a6e21c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type.hpp @@ -0,0 +1,70 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TYPE_HPP +#define REALM_COLUMN_TYPE_HPP + +namespace realm { + + +// Note: Enumeration value assignments must be kept in sync with +// . +enum ColumnType { + // Column types + col_type_Int = 0, + col_type_Bool = 1, + col_type_String = 2, + col_type_StringEnum = 3, // double refs + col_type_Binary = 4, + col_type_Table = 5, + col_type_Mixed = 6, + col_type_OldDateTime = 7, + col_type_Timestamp = 8, + col_type_Float = 9, + col_type_Double = 10, + col_type_Reserved4 = 11, // Decimal + col_type_Link = 12, + col_type_LinkList = 13, + col_type_BackLink = 14 +}; + + +// Column attributes can be combined using bitwise or. +enum ColumnAttr { + col_attr_None = 0, + col_attr_Indexed = 1, + + /// Specifies that this column forms a unique constraint. It requires + /// `col_attr_Indexed`. + col_attr_Unique = 2, + + /// Reserved for future use. + col_attr_Reserved = 4, + + /// Specifies that the links of this column are strong, not weak. Applies + /// only to link columns (`type_Link` and `type_LinkList`). + col_attr_StrongLinks = 8, + + /// Specifies that elements in the column can be null. + col_attr_Nullable = 16 +}; + + +} // namespace realm + +#endif // REALM_COLUMN_TYPE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type_traits.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type_traits.hpp new file mode 100644 index 0000000..3f5da78 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/column_type_traits.hpp @@ -0,0 +1,164 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TYPE_TRAITS_HPP +#define REALM_COLUMN_TYPE_TRAITS_HPP + +#include +#include +#include +#include + +namespace realm { + +class OldDateTime; +class ArrayBinary; +class ArrayInteger; +class ArrayIntNull; +template +class BasicArray; + +template +struct ColumnTypeTraits; + +template <> +struct ColumnTypeTraits { + using column_type = Column; + using leaf_type = ArrayInteger; + using sum_type = int64_t; + using minmax_type = int64_t; + static const DataType id = type_Int; + static const ColumnType column_id = col_type_Int; + static const ColumnType real_column_type = col_type_Int; +}; + +template <> +struct ColumnTypeTraits> { + using column_type = Column>; + using leaf_type = ArrayIntNull; + using sum_type = int64_t; + using minmax_type = int64_t; + static const DataType id = type_Int; + static const ColumnType column_id = col_type_Int; + static const ColumnType real_column_type = col_type_Int; +}; + +template <> +struct ColumnTypeTraits : ColumnTypeTraits { + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits> : ColumnTypeTraits> { + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits { + using column_type = FloatColumn; + using leaf_type = BasicArray; + using sum_type = double; + using minmax_type = float; + static const DataType id = type_Float; + static const ColumnType column_id = col_type_Float; + static const ColumnType real_column_type = col_type_Float; +}; + +template <> +struct ColumnTypeTraits { + using column_type = DoubleColumn; + using leaf_type = BasicArray; + using sum_type = double; + using minmax_type = double; + static const DataType id = type_Double; + static const ColumnType column_id = col_type_Double; + static const ColumnType real_column_type = col_type_Double; +}; + +template <> +struct ColumnTypeTraits : ColumnTypeTraits { + static const DataType id = type_OldDateTime; + static const ColumnType column_id = col_type_OldDateTime; +}; + +template <> +struct ColumnTypeTraits> : ColumnTypeTraits> { + static const DataType id = type_OldDateTime; + static const ColumnType column_id = col_type_OldDateTime; +}; + +template <> +struct ColumnTypeTraits { + using column_type = StringEnumColumn; + using leaf_type = ArrayInteger; + using sum_type = int64_t; + static const DataType id = type_String; + static const ColumnType column_id = col_type_String; + static const ColumnType real_column_type = col_type_String; +}; + +template <> +struct ColumnTypeTraits { + using column_type = BinaryColumn; + using leaf_type = ArrayBinary; + static const DataType id = type_Binary; + static const ColumnType column_id = col_type_Binary; + static const ColumnType real_column_type = col_type_Binary; +}; + +template +struct GetColumnType; +template <> +struct GetColumnType { + using type = IntegerColumn; +}; +template <> +struct GetColumnType { + using type = IntNullColumn; +}; +template +struct GetColumnType { + // FIXME: Null definition + using type = FloatColumn; +}; +template +struct GetColumnType { + // FIXME: Null definition + using type = DoubleColumn; +}; + +// Only purpose is to return 'double' if and only if source column (T) is float and you're doing a sum (A) +template +struct ColumnTypeTraitsSum { + typedef T sum_type; +}; + +template <> +struct ColumnTypeTraitsSum { + typedef double sum_type; +}; + +template +struct ColumnTypeTraitsSum, A> { + using sum_type = int64_t; +}; +} + +#endif // REALM_COLUMN_TYPE_TRAITS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/data_type.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/data_type.hpp new file mode 100644 index 0000000..0ba78c8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/data_type.hpp @@ -0,0 +1,63 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_DATA_TYPE_HPP +#define REALM_DATA_TYPE_HPP + +namespace realm { + +class StringData; +class BinaryData; + +typedef int64_t Int; +typedef bool Bool; +typedef float Float; +typedef double Double; +typedef realm::StringData String; +typedef realm::BinaryData Binary; + + +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with "com/realm/ColumnType.java" +// Note: Any change to this enum is a file-format breaking change. +enum DataType { + type_Int = 0, + type_Bool = 1, + type_Float = 9, + type_Double = 10, + type_String = 2, + type_Binary = 4, + type_OldDateTime = 7, + type_Timestamp = 8, + type_Table = 5, + type_Mixed = 6, + type_Link = 12, + type_LinkList = 13 +}; + +/// See Descriptor::set_link_type(). +enum LinkType { + link_Strong, + link_Weak, +}; + +} // namespace realm + +#endif // REALM_DATA_TYPE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor.hpp new file mode 100644 index 0000000..10433eb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor.hpp @@ -0,0 +1,820 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_DESCRIPTOR_HPP +#define REALM_DESCRIPTOR_HPP + +#include + +#include +#include +#include + + +namespace realm { + +namespace _impl { +class DescriptorFriend; +} + + +/// Accessor for table type descriptors. +/// +/// A table type descriptor is an entity that specifies the dynamic +/// type of a Realm table. Objects of this class are accessors +/// through which the descriptor can be inspected and +/// changed. Accessors can become detached, see is_attached() for more +/// on this. The descriptor itself is stored inside the database file, +/// or elsewhere in case of a free-standing table or a table in a +/// free-standing group. +/// +/// The dynamic type consists first, and foremost of an ordered list +/// of column descriptors. Each column descriptor specifies the name +/// and type of the column. +/// +/// When a table has a subtable column, every cell in than column +/// contains a subtable. All those subtables have the same dynamic +/// type, and therefore have a shared descriptor. See is_root() for +/// more on this. +/// +/// The Table class contains convenience methods, such as +/// Table::get_column_count() and Table::add_column(), that allow you +/// to inspect and change the dynamic type of simple tables without +/// resorting to use of descriptors. For example, the following two +/// statements have the same effect: +/// +/// table->add_column(type, name); +/// table->get_descriptor()->add_column(type, name); +/// +/// Note, however, that this equivalence holds only as long as no +/// shared subtable descriptors are involved. +/// +/// \sa Table::get_descriptor() +class Descriptor : public std::enable_shared_from_this { +public: + /// Get the number of columns in the associated tables. + size_t get_column_count() const noexcept; + + /// Get the type of the column at the specified index. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + DataType get_column_type(size_t column_ndx) const noexcept; + + /// Get the name of the column at the specified index. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + StringData get_column_name(size_t column_ndx) const noexcept; + + /// Search for a column with the specified name. + /// + /// This function finds the first column with the specified name, + /// and returns its index. If there are no such columns, it + /// returns `not_found`. + size_t get_column_index(StringData name) const noexcept; + + /// Get the index of the column to which links in the column at the + /// specified index refer. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + /// + /// The consequences of specifying a column index that does not refer + /// to a link column, are undefined. + size_t get_column_link_target(size_t column_ndx) const noexcept; + + /// Get whether or not the specified column is nullable. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + bool is_nullable(size_t column_ndx) const noexcept; + + /// \defgroup descriptor_column_accessors Accessing Columns Via A Descriptor + /// + /// add_column() and add_column_link() are a shorthands for calling + /// insert_column() and insert_column_link(), respectively, with a column + /// index equal to the original number of columns. The returned value is + /// that column index. + /// + /// insert_column() inserts a new column into all the tables associated with + /// this descriptor. If any of the tables are not empty, the new column will + /// be filled with the default value associated with the specified data + /// type. This function cannot be used to insert link-type columns. For + /// that, you have to use insert_column_link() instead. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. It does this by inserting a new column + /// with the specified name and type into the descriptor at the + /// specified index, and into each of the tables that share this + /// descriptor. + /// + /// insert_column_link() is like insert_column(), but inserts a link-type + /// column to a group-level table. It is not possible to add link-type + /// columns to tables that are not group-level tables. This functions must + /// be used in place of insert_column() when the column type is `type_Link` + /// or `type_LinkList`. A link-type column is associated with a particular + /// target table. All links in a link-type column refer to rows in the + /// target table of that column. The target table must also be a group-level + /// table, and it must belong to the same group as the origin table. + /// + /// \param name Name of new column. All strings are valid column names as + /// long as they are valid UTF-8 encodings and the number of bytes does not + /// exceed `max_column_name_length`. An attempt to add a column with a name + /// that is longer than `max_column_name_length` will cause an exception to + /// be thrown. + /// + /// \param subdesc If a non-null pointer is passed, and the + /// specified type is `type_Table`, then this function + /// automatically retrieves the descriptor associated with the new + /// subtable column, and stores a reference to its accessor in + /// `*subdesc`. + /// + /// \param col_ndx Insert the new column at this index. Preexisting columns + /// at indexes equal to, or greater than `col_ndx` will be shifted to the + /// next higher index. It is an error to specify an index that is greater + /// than the number of columns prior to the insertion. + /// + /// \param link_type See set_link_type(). + /// + /// \sa Table::add_column() + /// \sa Table::insert_column() + /// \sa Table::add_column_link() + /// \sa Table::insert_column_link() + /// \sa is_root() + //@{ + + static const size_t max_column_name_length = 63; + + size_t add_column(DataType type, StringData name, DescriptorRef* subdesc = nullptr, bool nullable = false); + + void insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc = nullptr, + bool nullable = false); + + size_t add_column_link(DataType type, StringData name, Table& target, LinkType = link_Weak); + void insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target, LinkType = link_Weak); + //@} + + /// Remove the specified column from each of the associated + /// tables. If the removed column is the only column in the + /// descriptor, then the table size will drop to zero for all + /// tables that were not already empty. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. It does this by removing the column at + /// the specified index from the descriptor, and from each of the + /// tables that share this descriptor. The consequences of + /// specifying a column index that is out of range, are undefined. + /// + /// If the removed column was a subtable column, then the + /// associated descriptor accessor will be detached, if it + /// exists. This function will also detach all accessors of + /// subtables of the root table. Only the accessor of the root + /// table will remain attached. The root table is the table + /// associated with the root descriptor. + /// + /// \param col_ndx The index of the column to be removed. It is an error to + /// specify an index that is greater than, or equal to the number of + /// columns. + /// + /// \sa is_root() + /// \sa Table::remove_column() + void remove_column(size_t col_ndx); + + /// Rename the specified column. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. The consequences of specifying a column + /// index that is out of range, are undefined. + /// + /// This function will detach all accessors of subtables of the + /// root table. Only the accessor of the root table will remain + /// attached. The root table is the table associated with the root + /// descriptor. + /// + /// \param col_ndx The index of the column to be renamed. It is an error to + /// specify an index that is greater than, or equal to the number of + /// columns. + /// + /// \param new_name The new name of the column. + /// + /// \sa is_root() + /// \sa Table::rename_column() + void rename_column(size_t col_ndx, StringData new_name); + + /// There are two kinds of links, 'weak' and 'strong'. A strong link is one + /// that implies ownership, i.e., that the origin row (parent) owns the + /// target row (child). Simply stated, this means that when the origin row + /// (parent) is removed, so is the target row (child). If there are multiple + /// strong links to a target row, the origin rows share ownership, and the + /// target row is removed when the last owner disappears. Weak links do not + /// imply ownership, and will be nullified or removed when the target row + /// disappears. + /// + /// To put this in precise terms; when a strong link is broken, and the + /// target row has no other strong links to it, the target row is removed. A + /// row that is implicitly removed in this way, is said to be + /// *cascade-removed*. When a weak link is broken, nothing is + /// cascade-removed. + /// + /// A link is considered broken if + /// + /// - the link is nullified, removed, or replaced by a different link + /// (Row::nullify_link(), Row::set_link(), LinkView::remove_link(), + /// LinkView::set_link(), LinkView::clear()), or if + /// + /// - the origin row is explicitly removed (Row::move_last_over(), + /// Table::clear()), or if + /// + /// - the origin row is cascade-removed, or if + /// + /// - the origin column is removed from the table (Table::remove_column()), + /// or if + /// + /// - the origin table is removed from the group. + /// + /// Note that a link is *not* considered broken when it is replaced by a + /// link to the same target row. I.e., no no rows will be cascade-removed + /// due to such an operation. + /// + /// When a row is explicitly removed (such as by Table::move_last_over()), + /// all links to it are automatically removed or nullified. For single link + /// columns (type_Link), links to the removed row are nullified. For link + /// list columns (type_LinkList), links to the removed row are removed from + /// the list. + /// + /// When a row is cascade-removed there can no longer be any strong links to + /// it, but if there are any weak links, they will be removed or nullified. + /// + /// It is important to understand that this cascade-removal scheme is too + /// simplistic to enable detection and removal of orphaned link-cycles. In + /// this respect, it suffers from the same limitations as a reference + /// counting scheme generally does. + /// + /// It is also important to understand, that the possible presence of a link + /// cycle can cause a row to be cascade-removed as a consequence of being + /// modified. This happens, for example, if two rows, A and B, have strong + /// links to each other, and there are no other strong links to either of + /// them. In this case, if A->B is changed to A->C, then both A and B will + /// be cascade-removed. This can lead to obscure bugs in some applications, + /// such as in the following case: + /// + /// table.set_link(col_ndx_1, row_ndx, ...); + /// table.set_int(col_ndx_2, row_ndx, ...); // Oops, `row_ndx` may no longer refer to the same row + /// + /// To be safe, applications, that may encounter cycles, are advised to + /// adopt the following pattern: + /// + /// Row row = table[row_ndx]; + /// row.set_link(col_ndx_1, ...); + /// if (row) + /// row.set_int(col_ndx_2, ...); // Ok, because we check whether the row has disappeared + /// + /// \param col_ndx The index of the link column (`type_Link` or + /// `type_LinkList`) to be modified. It is an error to specify an index that + /// is greater than, or equal to the number of columns, or to specify the + /// index of a non-link column. + /// + /// \param link_type The type of links the column should store. + void set_link_type(size_t col_ndx, LinkType link_type); + + //@{ + /// Get the descriptor for the specified subtable column. + /// + /// This function provides access to the shared subtable + /// descriptor for the subtables in the specified column. The + /// specified column must be a column whose type is 'table'. The + /// consequences of specifying a column of a different type, or + /// specifying an index that is out of range, are undefined. + /// + /// Note that this function cannot be used with 'mixed' columns, + /// since subtables of that kind have independent dynamic types, + /// and therefore, have independent descriptors. You can only get + /// access to the descriptor of a subtable in a mixed column by + /// first getting access to the subtable itself. + /// + /// \sa is_root() + DescriptorRef get_subdescriptor(size_t column_ndx); + ConstDescriptorRef get_subdescriptor(size_t column_ndx) const; + //@} + + //@{ + /// Returns the parent table descriptor, if any. + /// + /// If this descriptor is the *root descriptor*, then this + /// function returns null. Otherwise it returns the accessor of + /// the parent descriptor. + /// + /// \sa is_root() + DescriptorRef get_parent() noexcept; + ConstDescriptorRef get_parent() const noexcept; + //@} + + //@{ + /// Get the table associated with the root descriptor. + /// + /// \sa get_parent() + /// \sa is_root() + TableRef get_root_table() noexcept; + ConstTableRef get_root_table() const noexcept; + //@} + + //@{ + /// Get the target table associated with the specified link column. This + /// descriptor must be a root descriptor (is_root()), and the specified + /// column must be a link column (`type_Link` or `type_LinkList`). + TableRef get_link_target(size_t col_ndx) noexcept; + ConstTableRef get_link_target(size_t col_ndx) const noexcept; + //@} + + /// Is this a root descriptor? + /// + /// Descriptors of tables with independent dynamic type are root + /// descriptors. Root descriptors are never shared. Tables that + /// are direct members of groups have independent dynamic + /// types. The same is true for free-standing tables and subtables + /// in columns of type 'mixed'. + /// + /// When a table has a column of type 'table', the cells in that + /// column contain subtables. All those subtables have the same + /// dynamic type, and they share a single dynamic type + /// descriptor. Such shared descriptors are never root + /// descriptors. + /// + /// A type descriptor can even be shared by subtables with + /// different parent tables, but only if the parent tables + /// themselves have a shared type descriptor. For example, if a + /// table has a column `foo` of type 'table', and each of the + /// subtables in `foo` has a column `bar` of type 'table', then + /// all the subtables in all the `bar` columns share the same + /// dynamic type descriptor. + /// + /// \sa Table::has_shared_type() + bool is_root() const noexcept; + + /// Determine whether this accessor is still attached. + /// + /// A table descriptor accessor may get detached from the + /// underlying descriptor for various reasons (see below). When it + /// does, it no longer refers to that descriptor, and can no + /// longer be used, except for calling is_attached(). The + /// consequences of calling other methods on a detached accessor + /// are undefined. Descriptor accessors obtained by calling + /// functions in the Realm API are always in the 'attached' + /// state immediately upon return from those functions. + /// + /// A descriptor accessor that is obtained directly from a table + /// becomes detached if the table becomes detached. A shared + /// subtable descriptor accessor that is obtained by a call to + /// get_subdescriptor() becomes detached if the parent descriptor + /// accessor becomes detached, or if the corresponding subtable + /// column is removed. A descriptor accessor does not get detached + /// under any other circumstances. + bool is_attached() const noexcept; + + //@{ + /// \brief Compare two table descriptors. + /// + /// Two table descriptors are equal if they have the same number of columns, + /// and for each column index, the two columns have the same name, data + /// type, and set of attributes. + /// + /// For link columns (`type_Link` and `type_LinkList`), the target table + /// (get_link_target()) of the two columns must be the same. + /// + /// For subtable columns (`type_Table`), the two corresponding + /// subdescriptors must themselves be equal, as if by a recursive call to + /// operator==(). + /// + /// The consequences of comparing a detached descriptor are + /// undefined. + bool operator==(const Descriptor&) const noexcept; + bool operator!=(const Descriptor&) const noexcept; + //@} + + /// If the specified column is optimized to store only unique values, then + /// this function returns the number of unique values currently + /// stored. Otherwise it returns zero. This function is mainly intended for + /// debugging purposes. + size_t get_num_unique_values(size_t column_ndx) const; + + ~Descriptor() noexcept; + +private: + // for initialization through make_shared + struct PrivateTag { + }; + +public: + Descriptor(const PrivateTag&) + : Descriptor() + { + } + +private: + // Table associated with root descriptor. Detached iff null. + TableRef m_root_table; + DescriptorRef m_parent; // Null iff detached or root descriptor. + Spec* m_spec; // Valid if attached. Owned iff valid and `m_parent`. + + // Whenever a subtable descriptor accessor is created, it is + // stored in this map. This ensures that when get_subdescriptor() + // is called to created multiple DescriptorRef objects that + // overlap in time, then they will all refer to the same + // descriptor object. + // + // It also enables the necessary recursive detaching of descriptor + // objects. + struct subdesc_entry { + size_t m_column_ndx; + std::weak_ptr m_subdesc; + subdesc_entry(size_t column_ndx, DescriptorRef); + }; + typedef std::vector subdesc_map; + mutable subdesc_map m_subdesc_map; + + Descriptor() noexcept; + + // Called by the root table if this becomes the root + // descriptor. Otherwise it is called by the descriptor that + // becomes its parent. + // + // Puts this descriptor accessor into the attached state. This + // attaches it to the underlying structure of array nodes. It does + // not establish the parents reference to this descriptor, that is + // the job of the parent. When this function returns, + // is_attached() will return true. + // + // Not idempotent. + // + // The specified table is not allowed to be a subtable with a + // shareable spec. That is, Table::has_shared_spec() must return + // false. + // + // The specified spec must be the spec of the specified table or + // of one of its direct or indirect subtable columns. + // + // When the specified spec is the spec of the root table, the + // parent must be specified as null. When the specified spec is + // not the root spec, a proper parent must be specified. + void attach(Table*, DescriptorRef parent, Spec*) noexcept; + + // Detach accessor from underlying descriptor. Caller must ensure + // that a reference count exists upon return, for example by + // obtaining an extra reference count before the call. + // + // This function is called either by the root table if this is the + // root descriptor, or by the parent descriptor, if it is not. + // + // Puts this descriptor accessor into the detached state. This + // detaches it from the underlying structure of array nodes. It + // also calls detach_subdesc_accessors(). When this function + // returns, is_attached() will return false. + // + // Not idempotent. + void detach() noexcept; + + // Recursively detach all subtable descriptor accessors that + // exist, that is, all subtable descriptor accessors that have + // this descriptor as ancestor. + void detach_subdesc_accessors() noexcept; + + // Record the path in terms of subtable column indexes from the + // root descriptor to this descriptor. If this descriptor is a + // root descriptor, the path is empty. Returns zero if the path is + // too long to fit in the specified buffer. Otherwise the path + // indexes will be stored between `begin_2`and `end`, where + // `begin_2` is the returned pointer. + size_t* record_subdesc_path(size_t* begin, size_t* end) const noexcept; + + // Returns a pointer to the accessor of the specified + // subdescriptor if that accessor exists, otherwise this function + // return null. + DescriptorRef get_subdesc_accessor(size_t column_ndx) noexcept; + + void move_column(size_t from_ndx, size_t to_ndx); + + void adj_insert_column(size_t col_ndx) noexcept; + void adj_erase_column(size_t col_ndx) noexcept; + void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; + + friend class util::bind_ptr; + friend class util::bind_ptr; + friend class _impl::DescriptorFriend; +}; + + +// Implementation: + +inline size_t Descriptor::get_column_count() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_public_column_count(); +} + +inline StringData Descriptor::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_name(ndx); +} + +inline DataType Descriptor::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_public_column_type(ndx); +} + +inline bool Descriptor::is_nullable(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_attr(ndx) & col_attr_Nullable; +} + +inline size_t Descriptor::get_column_index(StringData name) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_index(name); +} + +inline size_t Descriptor::get_column_link_target(size_t column_ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_opposite_link_table_ndx(column_ndx); +} + +inline size_t Descriptor::add_column(DataType type, StringData name, DescriptorRef* subdesc, bool nullable) +{ + size_t col_ndx = m_spec->get_public_column_count(); + insert_column(col_ndx, type, name, subdesc, nullable); // Throws + return col_ndx; +} + +inline void Descriptor::insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc, + bool nullable) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx > get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(tf::is_link_type(ColumnType(type)))) + throw LogicError(LogicError::illegal_type); + + LinkTargetInfo invalid_link; + tf::insert_column(*this, col_ndx, type, name, invalid_link, nullable); // Throws + adj_insert_column(col_ndx); + if (subdesc && type == type_Table) + *subdesc = get_subdescriptor(col_ndx); +} + +inline size_t Descriptor::add_column_link(DataType type, StringData name, Table& target, LinkType link_type) +{ + size_t col_ndx = m_spec->get_public_column_count(); + insert_column_link(col_ndx, type, name, target, link_type); // Throws + return col_ndx; +} + +inline void Descriptor::insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target, + LinkType link_type) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached() || !target.is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx > get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(type)))) + throw LogicError(LogicError::illegal_type); + if (REALM_UNLIKELY(!is_root())) + throw LogicError(LogicError::wrong_kind_of_descriptor); + // Both origin and target must be group-level tables, and in the same group. + Group* origin_group = tf::get_parent_group(*get_root_table()); + Group* target_group = tf::get_parent_group(target); + if (!origin_group || !target_group) + throw LogicError(LogicError::wrong_kind_of_table); + if (origin_group != target_group) + throw LogicError(LogicError::group_mismatch); + + LinkTargetInfo link(&target); + tf::insert_column(*this, col_ndx, type, name, link); // Throws + adj_insert_column(col_ndx); + + tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws +} + +inline void Descriptor::remove_column(size_t col_ndx) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + + tf::erase_column(*this, col_ndx); // Throws + adj_erase_column(col_ndx); +} + +inline void Descriptor::rename_column(size_t col_ndx, StringData name) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + + tf::rename_column(*this, col_ndx, name); // Throws +} + +inline void Descriptor::move_column(size_t from_ndx, size_t to_ndx) +{ + REALM_ASSERT(is_attached()); + typedef _impl::TableFriend tf; + tf::move_column(*this, from_ndx, to_ndx); // Throws + adj_move_column(from_ndx, to_ndx); +} + +inline void Descriptor::set_link_type(size_t col_ndx, LinkType link_type) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(get_column_type(col_ndx))))) + throw LogicError(LogicError::illegal_type); + + tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws +} + +inline ConstDescriptorRef Descriptor::get_subdescriptor(size_t column_ndx) const +{ + return const_cast(this)->get_subdescriptor(column_ndx); +} + +inline DescriptorRef Descriptor::get_parent() noexcept +{ + return m_parent; +} + +inline ConstDescriptorRef Descriptor::get_parent() const noexcept +{ + return const_cast(this)->get_parent(); +} + +inline TableRef Descriptor::get_root_table() noexcept +{ + return m_root_table; +} + +inline ConstTableRef Descriptor::get_root_table() const noexcept +{ + return const_cast(this)->get_root_table(); +} + +inline TableRef Descriptor::get_link_target(size_t col_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(is_root()); + return get_root_table()->get_link_target(col_ndx); +} + +inline ConstTableRef Descriptor::get_link_target(size_t col_ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(is_root()); + return get_root_table()->get_link_target(col_ndx); +} + +inline bool Descriptor::is_root() const noexcept +{ + return !m_parent; +} + +inline Descriptor::Descriptor() noexcept +{ +} + +inline void Descriptor::attach(Table* table, DescriptorRef parent, Spec* spec) noexcept +{ + REALM_ASSERT(!is_attached()); + REALM_ASSERT(!table->has_shared_type()); + m_root_table.reset(table); + m_parent = parent; + m_spec = spec; +} + +inline bool Descriptor::is_attached() const noexcept +{ + return bool(m_root_table); +} + +inline Descriptor::subdesc_entry::subdesc_entry(size_t n, DescriptorRef d) + : m_column_ndx(n) + , m_subdesc(d) +{ +} + +inline bool Descriptor::operator==(const Descriptor& d) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(d.is_attached()); + return *m_spec == *d.m_spec; +} + +inline bool Descriptor::operator!=(const Descriptor& d) const noexcept +{ + return !(*this == d); +} + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Descriptor class. +class _impl::DescriptorFriend { +public: + static DescriptorRef create() + { + return std::make_shared(Descriptor::PrivateTag()); // Throws + } + + static void attach(Descriptor& desc, Table* table, DescriptorRef parent, Spec* spec) noexcept + { + desc.attach(table, parent, spec); + } + + static void detach(Descriptor& desc) noexcept + { + desc.detach(); + } + + static Table& get_root_table(Descriptor& desc) noexcept + { + return *desc.m_root_table; + } + + static const Table& get_root_table(const Descriptor& desc) noexcept + { + return *desc.m_root_table; + } + + static Spec& get_spec(Descriptor& desc) noexcept + { + return *desc.m_spec; + } + + static const Spec& get_spec(const Descriptor& desc) noexcept + { + return *desc.m_spec; + } + + static size_t* record_subdesc_path(const Descriptor& desc, size_t* begin, size_t* end) noexcept + { + return desc.record_subdesc_path(begin, end); + } + + static DescriptorRef get_subdesc_accessor(Descriptor& desc, size_t column_ndx) noexcept + { + return desc.get_subdesc_accessor(column_ndx); + } + + static void move_column(Descriptor& desc, size_t from_ndx, size_t to_ndx) + { + return desc.move_column(from_ndx, to_ndx); + } + + static void adj_insert_column(Descriptor& desc, size_t col_ndx) noexcept + { + desc.adj_insert_column(col_ndx); + } + + static void adj_erase_column(Descriptor& desc, size_t col_ndx) noexcept + { + desc.adj_erase_column(col_ndx); + } + + static void adj_move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) noexcept + { + desc.adj_move_column(col_ndx_1, col_ndx_2); + } +}; + +} // namespace realm + +#endif // REALM_DESCRIPTOR_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp new file mode 100644 index 0000000..2937724 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp @@ -0,0 +1,33 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_DESCRIPTOR_FWD_HPP +#define REALM_DESCRIPTOR_FWD_HPP + +#include + + +namespace realm { + +class Descriptor; +typedef std::shared_ptr DescriptorRef; +typedef std::shared_ptr ConstDescriptorRef; + +} // namespace realm + +#endif // REALM_DESCRIPTOR_FWD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp new file mode 100644 index 0000000..f642d6f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_DISABLE_SYNC_TO_DISK_HPP +#define REALM_DISABLE_SYNC_TO_DISK_HPP + +#include + +namespace realm { + +/// Completely disable synchronization with storage device to speed up unit +/// testing. This is an unsafe mode of operation, and should never be used in +/// production. This function is thread safe. +void disable_sync_to_disk(); + +/// Returns true after disable_sync_to_disk() has been called. This function is +/// thread safe. +bool get_disable_sync_to_disk() noexcept; + +} // namespace realm + +#endif // REALM_DISABLE_SYNC_TO_DISK_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/exceptions.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/exceptions.hpp new file mode 100644 index 0000000..cca6fc4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/exceptions.hpp @@ -0,0 +1,278 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_EXCEPTIONS_HPP +#define REALM_EXCEPTIONS_HPP + +#include + +#include + +namespace realm { + +/// Thrown by various functions to indicate that a specified table does not +/// exist. +class NoSuchTable : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown by various functions to indicate that a specified table name is +/// already in use. +class TableNameInUse : public std::exception { +public: + const char* what() const noexcept override; +}; + + +// Thrown by functions that require a table to **not** be the target of link +// columns, unless those link columns are part of the table itself. +class CrossTableLinkTarget : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown by various functions to indicate that the dynamic type of a table +/// does not match a particular other table type (dynamic or static). +class DescriptorMismatch : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// The FileFormatUpgradeRequired exception can be thrown by the SharedGroup +/// constructor when opening a database that uses a deprecated file format +/// and/or a deprecated history schema, and the user has indicated he does not +/// want automatic upgrades to be performed. This exception indicates that until +/// an upgrade of the file format is performed, the database will be unavailable +/// for read or write operations. +class FileFormatUpgradeRequired : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown when a sync agent attempts to join a session in which there is +/// already a sync agent. A session may only contain one sync agent at any given +/// time. +class MultipleSyncAgents : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown when memory can no longer be mapped to. When mmap/remap fails. +class AddressSpaceExhausted : public std::runtime_error { +public: + AddressSpaceExhausted(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + + +/// The \c LogicError exception class is intended to be thrown only when +/// applications (or bindings) violate rules that are stated (or ought to have +/// been stated) in the documentation of the public API, and only in cases +/// where the violation could have been easily and efficiently predicted by the +/// application. In other words, this exception class is for the cases where +/// the error is due to incorrect use of the public API. +/// +/// This class is not supposed to be caught by applications. It is not even +/// supposed to be considered part of the public API, and therefore the +/// documentation of the public API should **not** mention the \c LogicError +/// exception class by name. Note how this contrasts with other exception +/// classes, such as \c NoSuchTable, which are part of the public API, and are +/// supposed to be mentioned in the documentation by name. The \c LogicError +/// exception is part of Realm's private API. +/// +/// In other words, the \c LogicError class should exclusively be used in +/// replacement (or in addition to) asserts (debug or not) in order to +/// guarantee program interruption, while still allowing for complete +/// test-cases to be written and run. +/// +/// To this effect, the special `CHECK_LOGIC_ERROR()` macro is provided as a +/// test framework plugin to allow unit tests to check that the functions in +/// the public API do throw \c LogicError when rules are violated. +/// +/// The reason behind hiding this class from the public API is to prevent users +/// from getting used to the idea that "Undefined Behaviour" equates a specific +/// exception being thrown. The whole point of properly documenting "Undefined +/// Behaviour" cases is to help the user know what the limits are, without +/// constraining the database to handle every and any use-case thrown at it. +/// +/// FIXME: This exception class should probably be moved to the `_impl` +/// namespace, in order to avoid some confusion. +class LogicError : public std::exception { +public: + enum ErrorKind { + string_too_big, + binary_too_big, + table_name_too_long, + column_name_too_long, + table_index_out_of_range, + row_index_out_of_range, + column_index_out_of_range, + string_position_out_of_range, + link_index_out_of_range, + bad_version, + illegal_type, + + /// Indicates that an argument has a value that is illegal in combination + /// with another argument, or with the state of an involved object. + illegal_combination, + + /// Indicates a data type mismatch, such as when `Table::find_pkey_int()` is + /// called and the type of the primary key is not `type_Int`. + type_mismatch, + + /// Indicates that two involved tables are not in the same group. + group_mismatch, + + /// Indicates that an involved descriptor is of the wrong kind, i.e., if + /// it is a subtable descriptor, and the function requires a root table + /// descriptor. + wrong_kind_of_descriptor, + + /// Indicates that an involved table is of the wrong kind, i.e., if it + /// is a subtable, and the function requires a root table, or if it is a + /// free-standing table, and the function requires a group-level table. + wrong_kind_of_table, + + /// Indicates that an involved accessor is was detached, i.e., was not + /// attached to an underlying object. + detached_accessor, + + /// Indicates that a specified row index of a target table (a link) is + /// out of range. This is used for disambiguation in cases such as + /// Table::set_link() where one specifies both a row index of the origin + /// table, and a row index of the target table. + target_row_index_out_of_range, + + // Indicates that an involved column lacks a search index. + no_search_index, + + /// Indicates that a modification was attempted that would have produced a + /// duplicate primary value. + unique_constraint_violation, + + /// User attempted to insert null in non-nullable column + column_not_nullable, + + /// Group::open() is called on a group accessor that is already in the + /// attached state. Or Group::open() or Group::commit() is called on a + /// group accessor that is managed by a SharedGroup object. + wrong_group_state, + + /// No active transaction on a particular SharedGroup object (e.g., + /// SharedGroup::commit()), or the active transaction on the SharedGroup + /// object is of the wrong type (read/write), or an attampt was made to + /// initiate a new transaction while one is already in progress on the + /// same SharedGroup object. + wrong_transact_state, + + /// Attempted use of a continuous transaction through a SharedGroup + /// object with no history. See Replication::get_history(). + no_history, + + /// Durability setting (as passed to the SharedGroup constructor) was + /// not consistent across the session. + mixed_durability, + + /// History type (as specified by the Replication implementation passed + /// to the SharedGroup constructor) was not consistent across the + /// session. + mixed_history_type, + + /// History schema version (as specified by the Replication + /// implementation passed to the SharedGroup constructor) was not + /// consistent across the session. + mixed_history_schema_version, + + /// Adding rows to a table with no columns is not supported. + table_has_no_columns, + + /// Referring to a column that has been deleted. + column_does_not_exist + }; + + LogicError(ErrorKind message); + + const char* what() const noexcept override; + ErrorKind kind() const noexcept; + +private: + ErrorKind m_kind; +}; + + +// Implementation: + +// LCOV_EXCL_START (Wording of what() strings are not to be tested) + +inline const char* NoSuchTable::what() const noexcept +{ + return "No such table exists"; +} + +inline const char* TableNameInUse::what() const noexcept +{ + return "The specified table name is already in use"; +} + +inline const char* CrossTableLinkTarget::what() const noexcept +{ + return "Table is target of cross-table link columns"; +} + +inline const char* DescriptorMismatch::what() const noexcept +{ + return "Table descriptor mismatch"; +} + +inline const char* FileFormatUpgradeRequired::what() const noexcept +{ + return "Database upgrade required but prohibited"; +} + +inline const char* MultipleSyncAgents::what() const noexcept +{ + return "Multiple sync agents attempted to join the same session"; +} + +// LCOV_EXCL_STOP + +inline AddressSpaceExhausted::AddressSpaceExhausted(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline LogicError::LogicError(LogicError::ErrorKind k) + : m_kind(k) +{ +} + +inline LogicError::ErrorKind LogicError::kind() const noexcept +{ + return m_kind; +} + + +} // namespace realm + +#endif // REALM_EXCEPTIONS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/group.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group.hpp new file mode 100644 index 0000000..b50cfe5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group.hpp @@ -0,0 +1,1300 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_GROUP_HPP +#define REALM_GROUP_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +class SharedGroup; +namespace _impl { +class GroupFriend; +class TransactLogConvenientEncoder; +class TransactLogParser; +} + + +/// A group is a collection of named tables. +/// +/// Tables occur in the group in an unspecified order, but an order that +/// generally remains fixed. The order is guaranteed to remain fixed between two +/// points in time if no tables are added to, or removed from the group during +/// that time. When tables are added to, or removed from the group, the order +/// may change arbitrarily. +/// +/// If `table` is a table accessor attached to a group-level table, and `group` +/// is a group accessor attached to the group, then the following is guaranteed, +/// even after a change in the table order: +/// +/// \code{.cpp} +/// +/// table == group.get_table(table.get_index_in_group()) +/// +/// \endcode +/// +class Group : private Table::Parent { +public: + /// Construct a free-standing group. This group instance will be + /// in the attached state, but neither associated with a file, nor + /// with an external memory buffer. + Group(); + + enum OpenMode { + /// Open in read-only mode. Fail if the file does not already exist. + mode_ReadOnly, + /// Open in read/write mode. Create the file if it doesn't exist. + mode_ReadWrite, + /// Open in read/write mode. Fail if the file does not already exist. + mode_ReadWriteNoCreate + }; + + /// Equivalent to calling open(const std::string&, const char*, OpenMode) + /// on an unattached group accessor. + explicit Group(const std::string& file, const char* encryption_key = nullptr, OpenMode = mode_ReadOnly); + + /// Equivalent to calling open(BinaryData, bool) on an unattached + /// group accessor. Note that if this constructor throws, the + /// ownership of the memory buffer will remain with the caller, + /// regardless of whether \a take_ownership is set to `true` or + /// `false`. + explicit Group(BinaryData, bool take_ownership = true); + + struct unattached_tag { + }; + + /// Create a Group instance in its unattached state. It may then + /// be attached to a database file later by calling one of the + /// open() methods. You may test whether this instance is + /// currently in its attached state by calling + /// is_attached(). Calling any other method (except the + /// destructor) while in the unattached state has undefined + /// behavior. + Group(unattached_tag) noexcept; + + // FIXME: Implement a proper copy constructor (fairly trivial). + Group(const Group&) = delete; + Group& operator=(const Group&) = delete; + + ~Group() noexcept override; + + /// Attach this Group instance to the specified database file. + /// + /// By default, the specified file is opened in read-only mode + /// (mode_ReadOnly). This allows opening a file even when the + /// caller lacks permission to write to that file. The opened + /// group may still be modified freely, but the changes cannot be + /// written back to the same file using the commit() function. An + /// attempt to do that, will cause an exception to be thrown. When + /// opening in read-only mode, it is an error if the specified + /// file does not already exist in the file system. + /// + /// Alternatively, the file can be opened in read/write mode + /// (mode_ReadWrite). This allows use of the commit() function, + /// but, of course, it also requires that the caller has + /// permission to write to the specified file. When opening in + /// read-write mode, an attempt to create the specified file will + /// be made, if it does not already exist in the file system. + /// + /// In any case, if the file already exists, it must contain a + /// valid Realm database. In many cases invalidity will be + /// detected and cause the InvalidDatabase exception to be thrown, + /// but you should not rely on it. + /// + /// Note that changes made to the database via a Group instance + /// are not automatically committed to the specified file. You + /// may, however, at any time, explicitly commit your changes by + /// calling the commit() method, provided that the specified + /// open-mode is not mode_ReadOnly. Alternatively, you may call + /// write() to write the entire database to a new file. Writing + /// the database to a new file does not end, or in any other way + /// change the association between the Group instance and the file + /// that was specified in the call to open(). + /// + /// A file that is passed to Group::open(), may not be modified by + /// a third party until after the Group object is + /// destroyed. Behavior is undefined if a file is modified by a + /// third party while any Group object is associated with it. + /// + /// Calling open() on a Group instance that is already in the + /// attached state has undefined behavior. + /// + /// Accessing a Realm database file through manual construction + /// of a Group object does not offer any level of thread safety or + /// transaction safety. When any of those kinds of safety are a + /// concern, consider using a SharedGroup instead. When accessing + /// a database file in read/write mode through a manually + /// constructed Group object, it is entirely the responsibility of + /// the application that the file is not accessed in any way by a + /// third party during the life-time of that group object. It is, + /// on the other hand, safe to concurrently access a database file + /// by multiple manually created Group objects, as long as all of + /// them are opened in read-only mode, and there is no other party + /// that modifies the file concurrently. + /// + /// Do not call this function on a group instance that is managed + /// by a shared group. Doing so will result in undefined behavior. + /// + /// Even if this function throws, it may have the side-effect of + /// creating the specified file, and the file may get left behind + /// in an invalid state. Of course, this can only happen if + /// read/write mode (mode_ReadWrite) was requested, and the file + /// did not already exist. + /// + /// \param file File system path to a Realm database file. + /// + /// \param encryption_key 32-byte key used to encrypt and decrypt + /// the database file, or nullptr to disable encryption. + /// + /// \param mode Specifying a mode that is not mode_ReadOnly + /// requires that the specified file can be opened in read/write + /// mode. In general there is no reason to open a group in + /// read/write mode unless you want to be able to call + /// Group::commit(). + /// + /// \throw util::File::AccessError If the file could not be + /// opened. If the reason corresponds to one of the exception + /// types that are derived from util::File::AccessError, the + /// derived exception type is thrown. Note that InvalidDatabase is + /// among these derived exception types. + void open(const std::string& file, const char* encryption_key = nullptr, OpenMode mode = mode_ReadOnly); + + /// Attach this Group instance to the specified memory buffer. + /// + /// This is similar to constructing a group from a file except + /// that in this case the database is assumed to be stored in the + /// specified memory buffer. + /// + /// If \a take_ownership is `true`, you pass the ownership of the + /// specified buffer to the group. In this case the buffer will + /// eventually be freed using std::free(), so the buffer you pass, + /// must have been allocated using std::malloc(). + /// + /// On the other hand, if \a take_ownership is set to `false`, it + /// is your responsibility to keep the memory buffer alive during + /// the lifetime of the group, and in case the buffer needs to be + /// deallocated afterwards, that is your responsibility too. + /// + /// If this function throws, the ownership of the memory buffer + /// will remain with the caller, regardless of whether \a + /// take_ownership is set to `true` or `false`. + /// + /// Calling open() on a Group instance that is already in the + /// attached state has undefined behavior. + /// + /// Do not call this function on a group instance that is managed + /// by a shared group. Doing so will result in undefined behavior. + /// + /// \throw InvalidDatabase If the specified buffer does not appear + /// to contain a valid database. + void open(BinaryData, bool take_ownership = true); + + /// A group may be created in the unattached state, and then later + /// attached to a file with a call to open(). Calling any method + /// other than open(), and is_attached() on an unattached instance + /// results in undefined behavior. + bool is_attached() const noexcept; + + /// Returns true if, and only if the number of tables in this + /// group is zero. + bool is_empty() const noexcept; + + /// Returns the number of tables in this group. + size_t size() const noexcept; + + /// \defgroup group_table_access Table Accessors + /// + /// has_table() returns true if, and only if this group contains a table + /// with the specified name. + /// + /// find_table() returns the index of the first table in this group with the + /// specified name, or `realm::not_found` if this group does not contain a + /// table with the specified name. + /// + /// get_table_name() returns the name of table at the specified index. + /// + /// The versions of get_table(), that accepts a \a name argument, return the + /// first table with the specified name, or null if no such table exists. + /// + /// add_table() adds a table with the specified name to this group. It + /// throws TableNameInUse if \a require_unique_name is true and \a name + /// clashes with the name of an existing table. If \a require_unique_name is + /// false, it is possible to add more than one table with the same + /// name. Whenever a table is added, the order of the preexisting tables may + /// change arbitrarily, and the new table may not end up as the last one + /// either. But know that you can always call Table::get_index_in_group() on + /// the returned table accessor to find out at which index it ends up. + /// + /// get_or_add_table() checks if a table exists in this group with the specified + /// name. If it doesn't exist, a table is created. + /// + /// get_or_insert_table() works slightly differently from get_or_add_table(), + /// in that it considers the position of the requested table as part of that + /// table's identifying "key", in addition to the name. + /// + /// remove_table() removes the specified table from this group. A table can + /// be removed only when it is not the target of a link column of a + /// different table. Whenever a table is removed, the order of the remaining + /// tables may change arbitrarily. + /// + /// rename_table() changes the name of a preexisting table. If \a + /// require_unique_name is false, it becomes possible to have more than one + /// table with a given name in a single group. + /// + /// The template functions work exactly like their non-template namesakes + /// except as follows: The template versions of get_table() and + /// get_or_add_table() throw DescriptorMismatch if the dynamic type of the + /// specified table does not match the statically specified custom table + /// type. The template versions of add_table() and get_or_add_table() set + /// the dynamic type (descriptor) to match the statically specified custom + /// table type. + /// + /// \param index Index of table in this group. + /// + /// \param name Name of table. All strings are valid table names as long as + /// they are valid UTF-8 encodings and the number of bytes does not exceed + /// `max_table_name_length`. A call to add_table() or get_or_add_table() + /// with a name that is longer than `max_table_name_length` will cause an + /// exception to be thrown. + /// + /// \param new_name New name for preexisting table. + /// + /// \param require_unique_name When set to true (the default), it becomes + /// impossible to add a table with a name that is already in use, or to + /// rename a table to a name that is already in use. + /// + /// \param was_added When specified, the boolean variable is set to true if + /// the table was added, and to false otherwise. If the function throws, the + /// boolean variable retains its original value. + /// + /// \return get_table(), add_table(), and get_or_add_table() return a table + /// accessor attached to the requested (or added) table. get_table() may + /// return null. + /// + /// \throw DescriptorMismatch Thrown by get_table() and get_or_add_table() + /// tf the dynamic table type does not match the statically specified custom + /// table type (\a T). + /// + /// \throw NoSuchTable Thrown by remove_table() and rename_table() if there + /// is no table with the specified \a name. + /// + /// \throw TableNameInUse Thrown by add_table() if \a require_unique_name is + /// true and \a name clashes with the name of a preexisting table. Thrown by + /// rename_table() if \a require_unique_name is true and \a new_name clashes + /// with the name of a preexisting table. + /// + /// \throw CrossTableLinkTarget Thrown by remove_table() if the specified + /// table is the target of a link column of a different table. + /// + //@{ + + static const size_t max_table_name_length = 63; + + bool has_table(StringData name) const noexcept; + size_t find_table(StringData name) const noexcept; + StringData get_table_name(size_t table_ndx) const; + + TableRef get_table(size_t index); + ConstTableRef get_table(size_t index) const; + + TableRef get_table(StringData name); + ConstTableRef get_table(StringData name) const; + + TableRef add_table(StringData name, bool require_unique_name = true); + TableRef insert_table(size_t index, StringData name, bool require_unique_name = true); + TableRef get_or_add_table(StringData name, bool* was_added = nullptr); + TableRef get_or_insert_table(size_t index, StringData name, bool* was_added = nullptr); + + void remove_table(size_t index); + void remove_table(StringData name); + + void rename_table(size_t index, StringData new_name, bool require_unique_name = true); + void rename_table(StringData name, StringData new_name, bool require_unique_name = true); + + //@} + + /// Move the table at \a from_index such that it ends up at \a + /// to_index. Other tables are shifted as necessary in such a way that their + /// order is preserved. + /// + /// Note that \a to_index is the desired final index of the moved table, + /// therefore, `move_table(1,1)` is a no-op, while `move_table(1,2)` moves + /// the table at index 1 by one position, such that it ends up at index 2. A + /// side-effect of that, is that the table, that was originally at index 2, + /// is moved to index 1. + void move_table(size_t from_index, size_t to_index); + + // Serialization + + /// Write this database to the specified output stream. + /// + /// \param out The destination output stream to write to. + /// + /// \param pad If true, the file is padded to ensure the footer is aligned + /// to the end of a page + void write(std::ostream& out, bool pad = false) const; + + /// Write this database to a new file. It is an error to specify a + /// file that already exists. This is to protect against + /// overwriting a database file that is currently open, which + /// would cause undefined behaviour. + /// + /// \param file A filesystem path. + /// + /// \param encryption_key 32-byte key used to encrypt the database file, + /// or nullptr to disable encryption. + /// + /// \throw util::File::AccessError If the file could not be + /// opened. If the reason corresponds to one of the exception + /// types that are derived from util::File::AccessError, the + /// derived exception type is thrown. In particular, + /// util::File::Exists will be thrown if the file exists already. + void write(const std::string& file, const char* encryption_key = nullptr) const; + + /// Write this database to a memory buffer. + /// + /// Ownership of the returned buffer is transferred to the + /// caller. The memory will have been allocated using + /// std::malloc(). + BinaryData write_to_mem() const; + + /// Commit changes to the attached file. This requires that the + /// attached file is opened in read/write mode. + /// + /// Calling this function on an unattached group, a free-standing + /// group, a group whose attached file is opened in read-only + /// mode, a group that is attached to a memory buffer, or a group + /// that is managed by a shared group, is an error and will result + /// in undefined behavior. + /// + /// Table accesors will remain valid across the commit. Note that + /// this is not the case when working with proper transactions. + void commit(); + + //@{ + /// Some operations on Tables in a Group can cause indirect changes to other + /// fields, including in other Tables in the same Group. Specifically, + /// removing a row will set any links to that row to null, and if it had the + /// last strong links to other rows, will remove those rows. When this + /// happens, The cascade notification handler will be called with a + /// CascadeNotification containing information about what indirect changes + /// will occur, before any changes are made. + /// + /// has_cascade_notification_handler() returns true if and only if there is + /// currently a non-null notification handler registered. + /// + /// set_cascade_notification_handler() replaces the current handler (if any) + /// with the passed in handler. Pass in nullptr to remove the current handler + /// without registering a new one. + /// + /// CascadeNotification contains a vector of rows which will be removed and + /// a vector of links which will be set to null (or removed, for entries in + /// LinkLists). + struct CascadeNotification { + struct row { + /// Non-zero iff the removal of this row is ordered + /// (Table::remove()), as opposed to ordered + /// (Table::move_last_over()). Implicit removals are always + /// unordered. + /// + /// This flag does not take part in comparisons (operator==() and + /// operator<()). + size_t is_ordered_removal : 1; + + /// Index within group of a group-level table. + size_t table_ndx : std::numeric_limits::digits - 1; + + /// Row index which will be removed. + size_t row_ndx; + + row() + : is_ordered_removal(0) + { + } + + bool operator==(const row&) const noexcept; + bool operator!=(const row&) const noexcept; + + /// Trivial lexicographic order + bool operator<(const row&) const noexcept; + }; + + struct link { + const Table* origin_table; ///< A group-level table. + size_t origin_col_ndx; ///< Link column being nullified. + size_t origin_row_ndx; ///< Row in column being nullified. + /// The target row index which is being removed. Mostly relevant for + /// LinkList (to know which entries are being removed), but also + /// valid for Link. + size_t old_target_row_ndx; + }; + + /// A sorted list of rows which will be removed by the current operation. + std::vector rows; + + /// An unordered list of links which will be nullified by the current + /// operation. + std::vector links; + }; + + bool has_cascade_notification_handler() const noexcept; + void set_cascade_notification_handler(std::function new_handler) noexcept; + + //@} + + //@{ + /// During sync operation, schema changes may happen at runtime as connected + /// clients update their schema as part of an app update. Since this is a + /// relatively rare event, no attempt is made at limiting the amount of work + /// the handler is required to do to update its information about table and + /// column indices (i.e., all table and column indices must be recalculated). + /// + /// At the time of writing, only additive schema changes may occur in that + /// scenario. + /// + /// has_schema_change_notification_handler() returns true iff there is currently + /// a non-null notification handler registered. + /// + /// set_schema_change_notification_handler() replaces the current handler (if any) + /// with the passed in handler. Pass in nullptr to remove the current handler + /// without registering a new one. + + bool has_schema_change_notification_handler() const noexcept; + void set_schema_change_notification_handler(std::function new_handler) noexcept; + + //@} + + // Conversion + template + void to_json(S& out, size_t link_depth = 0, std::map* renames = nullptr) const; + void to_string(std::ostream& out) const; + + /// Compare two groups for equality. Two groups are equal if, and + /// only if, they contain the same tables in the same order, that + /// is, for each table T at index I in one of the groups, there is + /// a table at index I in the other group that is equal to T. + /// Tables are equal if they have the same content and the same table name. + bool operator==(const Group&) const; + + /// Compare two groups for inequality. See operator==(). + bool operator!=(const Group& g) const + { + return !(*this == g); + } + + void verify() const; +#ifdef REALM_DEBUG + void print() const; + void print_free() const; + MemStats stats(); + void enable_mem_diagnostics(bool enable = true) + { + m_alloc.enable_debug(enable); + } + void to_dot(std::ostream&) const; + void to_dot() const; // To std::cerr (for GDB) + void to_dot(const char* file_path) const; +#endif + +private: + SlabAlloc m_alloc; + + /// `m_top` is the root node (or top array) of the Realm, and has the + /// following layout: + /// + ///
+    ///
+    ///                                                     Introduced in file
+    ///   Slot  Value                                       format version
+    ///   ---------------------------------------------------------------------
+    ///    1st   m_table_names
+    ///    2nd   m_tables
+    ///    3rd   Logical file size
+    ///    4th   GroupWriter::m_free_positions (optional)
+    ///    5th   GroupWriter::m_free_lengths   (optional)
+    ///    6th   GroupWriter::m_free_versions  (optional)
+    ///    7th   Transaction number / version  (optional)
+    ///    8th   History type         (optional)             4
+    ///    9th   History ref          (optional)             4
+    ///   10th   History version      (optional)             7
+    ///
+    /// 
+ /// + /// The 'History type' slot stores a value of type + /// Replication::HistoryType. The 'History version' slot stores a history + /// schema version as returned by Replication::get_history_schema_version(). + /// + /// The first three entries are mandatory. In files created by + /// Group::write(), none of the optional entries are present and the size of + /// `m_top` is 3. In files updated by Group::commit(), the 4th and 5th entry + /// are present, and the size of `m_top` is 5. In files updated by way of a + /// transaction (SharedGroup::commit()), the 4th, 5th, 6th, and 7th entry + /// are present, and the size of `m_top` is 7. In files that contain a + /// changeset history, the 8th, 9th, and 10th entry are present, except that + /// if the file was opened in nonshared mode (via Group::open()), and the + /// file format remains at 6 (not previously upgraded to 7 or later), then + /// the 10th entry will be absent. + /// + /// When a group accessor is attached to a newly created file or an empty + /// memory buffer where there is no top array yet, `m_top`, `m_tables`, and + /// `m_table_names` will be left in the detached state until the initiation + /// of the first write transaction. In particular, they will remain in the + /// detached state during read transactions that precede the first write + /// transaction. + Array m_top; + ArrayInteger m_tables; + ArrayString m_table_names; + + typedef std::vector table_accessors; + mutable table_accessors m_table_accessors; + + bool m_attached = false; + const bool m_is_shared; + + std::function m_notify_handler; + std::function m_schema_change_handler; + + struct shared_tag { + }; + Group(shared_tag) noexcept; + + void init_array_parents() noexcept; + + /// If `top_ref` is not zero, attach this group accessor to the specified + /// underlying node structure. If `top_ref` is zero and \a + /// create_group_when_missing is true, create a new node structure that + /// represents an empty group, and attach this group accessor to it. It is + /// an error to call this function on an already attached group accessor. + void attach(ref_type top_ref, bool create_group_when_missing); + + /// Detach this group accessor from the underlying node structure. If this + /// group accessors is already in the detached state, this function does + /// nothing (idempotency). + void detach() noexcept; + + /// \param writable Must be set to true when, and only when attaching for a + /// write transaction. + void attach_shared(ref_type new_top_ref, size_t new_file_size, bool writable); + + void create_empty_group(); + + void reset_free_space_tracking(); + + void remap(size_t new_file_size); + void remap_and_update_refs(ref_type new_top_ref, size_t new_file_size); + + /// Recursively update refs stored in all cached array + /// accessors. This includes cached array accessors in any + /// currently attached table accessors. This ensures that the + /// group instance itself, as well as any attached table accessor + /// that exists across Group::commit() will remain valid. This + /// function is not appropriate for use in conjunction with + /// commits via shared group. + void update_refs(ref_type top_ref, size_t old_baseline) noexcept; + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + // Overriding method in Table::Parent + StringData get_child_name(size_t) const noexcept override; + + // Overriding method in Table::Parent + void child_accessor_destroyed(Table*) noexcept override; + + // Overriding method in Table::Parent + Group* get_parent_group() noexcept override; + + class TableWriter; + class DefaultTableWriter; + + static void write(std::ostream&, const Allocator&, TableWriter&, bool no_top_array, bool pad_for_encryption, + uint_fast64_t version_number); + + typedef void (*DescSetter)(Table&); + typedef bool (*DescMatcher)(const Spec&); + + Table* do_get_table(size_t table_ndx, DescMatcher desc_matcher); + const Table* do_get_table(size_t table_ndx, DescMatcher desc_matcher) const; + Table* do_get_table(StringData name, DescMatcher desc_matcher); + const Table* do_get_table(StringData name, DescMatcher desc_matcher) const; + Table* do_insert_table(size_t, StringData name, DescSetter desc_setter, bool require_unique_name); + Table* do_insert_table(size_t, StringData name, DescSetter desc_setter); + Table* do_get_or_add_table(StringData name, DescMatcher desc_matcher, DescSetter setter, bool* was_added); + Table* do_get_or_insert_table(size_t, StringData name, DescMatcher desc_matcher, DescSetter desc_setter, + bool* was_added); + + void create_and_insert_table(size_t new_table_ndx, StringData name); + Table* create_table_accessor(size_t table_ndx); + + void detach_table_accessors() noexcept; // Idempotent + + void mark_all_table_accessors() noexcept; + + void write(const std::string& file, const char* encryption_key, uint_fast64_t version_number) const; + void write(util::File& file, const char* encryption_key, uint_fast64_t version_number) const; + void write(std::ostream&, bool pad, uint_fast64_t version_numer) const; + + Replication* get_replication() const noexcept; + void set_replication(Replication*) noexcept; + class TransactAdvancer; + void advance_transact(ref_type new_top_ref, size_t new_file_size, _impl::NoCopyInputStream&); + void refresh_dirty_accessors(); + template + void update_table_indices(F&& map_function); + + int get_file_format_version() const noexcept; + void set_file_format_version(int) noexcept; + int get_committed_file_format_version() const noexcept; + + /// The specified history type must be a value of Replication::HistoryType. + static int get_target_file_format_version_for_session(int current_file_format_version, int history_type) noexcept; + + /// Must be called from within a write transaction + void upgrade_file_format(int target_file_format_version); + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + + void send_cascade_notification(const CascadeNotification& notification) const; + void send_schema_change_notification() const; + + static void get_version_and_history_info(const Array& top, _impl::History::version_type& version, + int& history_type, int& history_schema_version) noexcept; + static ref_type get_history_ref(const Array& top) noexcept; + static int get_history_schema_version(const Array& top) noexcept; + void set_history_schema_version(int version); + void set_history_parent(Array& history_root) noexcept; + void prepare_history_parent(Array& history_root, int history_type, int history_schema_version); + + friend class Table; + friend class GroupWriter; + friend class SharedGroup; + friend class _impl::GroupFriend; + friend class _impl::TransactLogConvenientEncoder; + friend class _impl::TransactLogParser; + friend class Replication; + friend class TrivialReplication; +}; + + +// Implementation + +inline Group::Group(const std::string& file, const char* key, OpenMode mode) + : m_alloc() // Throws + , m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); + + open(file, key, mode); // Throws +} + +inline Group::Group(BinaryData buffer, bool take_ownership) + : m_alloc() // Throws + , m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); + open(buffer, take_ownership); // Throws +} + +inline Group::Group(unattached_tag) noexcept + : m_alloc() + , // Throws + m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); +} + +inline Group* Group::get_parent_group() noexcept +{ + return this; +} + +inline Group::Group(shared_tag) noexcept + : m_alloc() + , // Throws + m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(true) +{ + init_array_parents(); +} + +inline bool Group::is_attached() const noexcept +{ + return m_attached; +} + +inline bool Group::is_empty() const noexcept +{ + if (!is_attached()) + return false; + if (m_table_names.is_attached()) + return m_table_names.is_empty(); + return true; +} + +inline size_t Group::size() const noexcept +{ + if (!is_attached()) + return 0; + if (m_table_names.is_attached()) + return m_table_names.size(); + return 0; +} + +inline StringData Group::get_table_name(size_t table_ndx) const +{ + if (table_ndx >= size()) + throw LogicError(LogicError::table_index_out_of_range); + return m_table_names.get(table_ndx); +} + +inline bool Group::has_table(StringData name) const noexcept +{ + size_t ndx = find_table(name); + return ndx != not_found; +} + +inline size_t Group::find_table(StringData name) const noexcept +{ + if (!is_attached()) + return 0; + if (m_table_names.is_attached()) + return m_table_names.find_first(name); + return not_found; +} + +inline TableRef Group::get_table(size_t table_ndx) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return TableRef(table); +} + +inline ConstTableRef Group::get_table(size_t table_ndx) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + const Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return ConstTableRef(table); +} + +inline TableRef Group::get_table(StringData name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + Table* table = do_get_table(name, desc_matcher); // Throws + return TableRef(table); +} + +inline ConstTableRef Group::get_table(StringData name) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + const Table* table = do_get_table(name, desc_matcher); // Throws + return ConstTableRef(table); +} + +inline TableRef Group::insert_table(size_t table_ndx, StringData name, bool require_unique_name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_insert_table(table_ndx, name, desc_setter, require_unique_name); // Throws + return TableRef(table); +} + +inline TableRef Group::add_table(StringData name, bool require_unique_name) +{ + return insert_table(size(), name, require_unique_name); +} + +inline TableRef Group::get_or_insert_table(size_t table_ndx, StringData name, bool* was_added) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_added); // Throws + return TableRef(table); +} + +inline TableRef Group::get_or_add_table(StringData name, bool* was_added) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_get_or_add_table(name, desc_matcher, desc_setter, was_added); // Throws + return TableRef(table); +} + +template +void Group::to_json(S& out, size_t link_depth, std::map* renames) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + + std::map renames2; + renames = renames ? renames : &renames2; + + out << "{"; + + for (size_t i = 0; i < m_tables.size(); ++i) { + StringData name = m_table_names.get(i); + std::map& m = *renames; + if (m[name] != "") + name = m[name]; + + ConstTableRef table = get_table(i); + + if (i) + out << ","; + out << "\"" << name << "\""; + out << ":"; + table->to_json(out, link_depth, renames); + } + + out << "}"; +} + +inline void Group::init_array_parents() noexcept +{ + m_table_names.set_parent(&m_top, 0); + m_tables.set_parent(&m_top, 1); +} + +inline void Group::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + m_tables.set(child_ndx, new_ref); +} + +inline ref_type Group::get_child_ref(size_t child_ndx) const noexcept +{ + return m_tables.get_as_ref(child_ndx); +} + +inline StringData Group::get_child_name(size_t child_ndx) const noexcept +{ + return m_table_names.get(child_ndx); +} + +inline void Group::child_accessor_destroyed(Table*) noexcept +{ + // Ignore +} + +inline bool Group::has_cascade_notification_handler() const noexcept +{ + return !!m_notify_handler; +} + +inline void +Group::set_cascade_notification_handler(std::function new_handler) noexcept +{ + m_notify_handler = std::move(new_handler); +} + +inline void Group::send_cascade_notification(const CascadeNotification& notification) const +{ + if (m_notify_handler) + m_notify_handler(notification); +} + +inline bool Group::has_schema_change_notification_handler() const noexcept +{ + return !!m_schema_change_handler; +} + +inline void Group::set_schema_change_notification_handler(std::function new_handler) noexcept +{ + m_schema_change_handler = std::move(new_handler); +} + +inline void Group::send_schema_change_notification() const +{ + if (m_schema_change_handler) + m_schema_change_handler(); +} + +inline void Group::get_version_and_history_info(const Array& top, _impl::History::version_type& version, + int& history_type, int& history_schema_version) noexcept +{ + using version_type = _impl::History::version_type; + version_type version_2 = 0; + int history_type_2 = 0; + int history_schema_version_2 = 0; + if (top.is_attached()) { + if (top.size() >= 6) { + REALM_ASSERT(top.size() >= 7); + version_2 = version_type(top.get_as_ref_or_tagged(6).get_as_int()); + } + if (top.size() >= 8) { + REALM_ASSERT(top.size() >= 9); + history_type_2 = int(top.get_as_ref_or_tagged(7).get_as_int()); + } + if (top.size() >= 10) { + history_schema_version_2 = int(top.get_as_ref_or_tagged(9).get_as_int()); + } + } + // Version 0 is not a legal initial version, so it has to be set to 1 + // instead. + if (version_2 == 0) + version_2 = 1; + version = version_2; + history_type = history_type_2; + history_schema_version = history_schema_version_2; +} + +inline ref_type Group::get_history_ref(const Array& top) noexcept +{ + bool has_history = (top.is_attached() && top.size() >= 8); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(top.size() >= 10); + return top.get_as_ref(8); + } + return 0; +} + +inline int Group::get_history_schema_version(const Array& top) noexcept +{ + bool has_history = (top.is_attached() && top.size() >= 8); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(top.size() >= 10); + return int(top.get_as_ref_or_tagged(9).get_as_int()); + } + return 0; +} + +inline void Group::set_history_schema_version(int version) +{ + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(m_top.size() >= 10); + m_top.set(9, RefOrTagged::make_tagged(unsigned(version))); // Throws +} + +inline void Group::set_history_parent(Array& history_root) noexcept +{ + history_root.set_parent(&m_top, 8); +} + +class Group::TableWriter { +public: + virtual ref_type write_names(_impl::OutputStream&) = 0; + virtual ref_type write_tables(_impl::OutputStream&) = 0; + virtual ~TableWriter() noexcept + { + } +}; + +inline const Table* Group::do_get_table(size_t table_ndx, DescMatcher desc_matcher) const +{ + return const_cast(this)->do_get_table(table_ndx, desc_matcher); // Throws +} + +inline const Table* Group::do_get_table(StringData name, DescMatcher desc_matcher) const +{ + return const_cast(this)->do_get_table(name, desc_matcher); // Throws +} + +inline void Group::reset_free_space_tracking() +{ + m_alloc.reset_free_space_tracking(); // Throws +} + +inline Replication* Group::get_replication() const noexcept +{ + return m_alloc.get_replication(); +} + +inline void Group::set_replication(Replication* repl) noexcept +{ + m_alloc.set_replication(repl); +} + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Group class. +class _impl::GroupFriend { +public: + static Allocator& get_alloc(Group& group) noexcept + { + return group.m_alloc; + } + + static Table& get_table(Group& group, size_t ndx_in_group) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + Table* table = group.do_get_table(ndx_in_group, desc_matcher); // Throws + return *table; + } + + static const Table& get_table(const Group& group, size_t ndx_in_group) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + const Table* table = group.do_get_table(ndx_in_group, desc_matcher); // Throws + return *table; + } + + static Table* get_table(Group& group, StringData name) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + Table* table = group.do_get_table(name, desc_matcher); // Throws + return table; + } + + static const Table* get_table(const Group& group, StringData name) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + const Table* table = group.do_get_table(name, desc_matcher); // Throws + return table; + } + + static Table& insert_table(Group& group, size_t table_ndx, StringData name, bool require_unique_name) + { + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_insert_table(table_ndx, name, desc_setter, require_unique_name); + } + + static Table& add_table(Group& group, StringData name, bool require_unique_name) + { + return insert_table(group, group.size(), name, require_unique_name); + } + + static Table& get_or_insert_table(Group& group, size_t table_ndx, StringData name, bool* was_inserted) + { + Group::DescMatcher desc_matcher = nullptr; // Do not check descriptor + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_inserted); + } + + static Table& get_or_add_table(Group& group, StringData name, bool* was_inserted) + { + Group::DescMatcher desc_matcher = nullptr; // Do not check descriptor + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_get_or_add_table(name, desc_matcher, desc_setter, was_inserted); + } + + static void send_cascade_notification(const Group& group, const Group::CascadeNotification& notification) + { + group.send_cascade_notification(notification); + } + + static Replication* get_replication(const Group& group) noexcept + { + return group.get_replication(); + } + + static void set_replication(Group& group, Replication* repl) noexcept + { + group.set_replication(repl); + } + + static void detach(Group& group) noexcept + { + group.detach(); + } + + static void attach_shared(Group& group, ref_type new_top_ref, size_t new_file_size, bool writable) + { + group.attach_shared(new_top_ref, new_file_size, writable); // Throws + } + + static void reset_free_space_tracking(Group& group) + { + group.reset_free_space_tracking(); // Throws + } + + static void remap(Group& group, size_t new_file_size) + { + group.remap(new_file_size); // Throws + } + + static void remap_and_update_refs(Group& group, ref_type new_top_ref, size_t new_file_size) + { + group.remap_and_update_refs(new_top_ref, new_file_size); // Throws + } + + static void advance_transact(Group& group, ref_type new_top_ref, size_t new_file_size, + _impl::NoCopyInputStream& in) + { + group.advance_transact(new_top_ref, new_file_size, in); // Throws + } + + static void create_empty_group_when_missing(Group& group) + { + if (!group.m_top.is_attached()) + group.create_empty_group(); // Throws + } + + static void get_version_and_history_info(Allocator& alloc, ref_type top_ref, + _impl::History::version_type& version, + int& history_type, + int& history_schema_version) noexcept + { + Array top(alloc); + if (top_ref != 0) + top.init_from_ref(top_ref); + Group::get_version_and_history_info(top, version, history_type, history_schema_version); + } + + static ref_type get_history_ref(const Group& group) noexcept + { + return Group::get_history_ref(group.m_top); + } + + static ref_type get_history_ref(Allocator& alloc, ref_type top_ref) noexcept + { + Array top(alloc); + if (top_ref != 0) + top.init_from_ref(top_ref); + return Group::get_history_ref(top); + } + + static int get_history_schema_version(const Group& group) noexcept + { + return Group::get_history_schema_version(group.m_top); + } + + static int get_history_schema_version(Allocator& alloc, ref_type top_ref) noexcept + { + Array top{alloc}; + if (top_ref != 0) + top.init_from_ref(top_ref); + return Group::get_history_schema_version(top); + } + + static void set_history_schema_version(Group& group, int version) + { + group.set_history_schema_version(version); // Throws + } + + static void set_history_parent(Group& group, Array& history_root) noexcept + { + group.set_history_parent(history_root); + } + + static void prepare_history_parent(Group& group, Array& history_root, int history_type, + int history_schema_version) + { + group.prepare_history_parent(history_root, history_type, history_schema_version); // Throws + } + + static int get_file_format_version(const Group& group) noexcept + { + return group.get_file_format_version(); + } + + static void set_file_format_version(Group& group, int file_format_version) noexcept + { + group.set_file_format_version(file_format_version); + } + + static int get_committed_file_format_version(const Group& group) noexcept + { + return group.get_committed_file_format_version(); + } + + static int get_target_file_format_version_for_session(int current_file_format_version, int history_type) noexcept + { + return Group::get_target_file_format_version_for_session(current_file_format_version, history_type); + } + + static void upgrade_file_format(Group& group, int target_file_format_version) + { + group.upgrade_file_format(target_file_format_version); // Throws + } +}; + + +struct CascadeState : Group::CascadeNotification { + /// If non-null, then no recursion will be performed for rows of that + /// table. The effect is then exactly as if all the rows of that table were + /// added to \a state.rows initially, and then removed again after the + /// explicit invocations of Table::cascade_break_backlinks_to() (one for + /// each initiating row). This is used by Table::clear() to avoid + /// reentrance. + /// + /// Must never be set concurrently with stop_on_link_list_column. + Table* stop_on_table = nullptr; + + /// If non-null, then Table::cascade_break_backlinks_to() will skip the + /// removal of reciprocal backlinks for the link list at + /// stop_on_link_list_row_ndx in this column, and no recursion will happen + /// on its behalf. This is used by LinkView::clear() to avoid reentrance. + /// + /// Must never be set concurrently with stop_on_table. + LinkListColumn* stop_on_link_list_column = nullptr; + + /// Is ignored if stop_on_link_list_column is null. + size_t stop_on_link_list_row_ndx = 0; + + /// If false, the links field is not needed, so any work done just for that + /// can be skipped. + bool track_link_nullifications = false; +}; + +inline bool Group::CascadeNotification::row::operator==(const row& r) const noexcept +{ + return table_ndx == r.table_ndx && row_ndx == r.row_ndx; +} + +inline bool Group::CascadeNotification::row::operator!=(const row& r) const noexcept +{ + return !(*this == r); +} + +inline bool Group::CascadeNotification::row::operator<(const row& r) const noexcept +{ + return table_ndx < r.table_ndx || (table_ndx == r.table_ndx && row_ndx < r.row_ndx); +} + +} // namespace realm + +#endif // REALM_GROUP_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared.hpp new file mode 100644 index 0000000..4678981 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared.hpp @@ -0,0 +1,1183 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_GROUP_SHARED_HPP +#define REALM_GROUP_SHARED_HPP + +#ifdef REALM_DEBUG +#include // usleep() +#endif + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +namespace _impl { +class SharedGroupFriend; +class WriteLogCollector; +} + +/// Thrown by SharedGroup::open() if the lock file is already open in another +/// process which can't share mutexes with this process +struct IncompatibleLockFile : std::runtime_error { + IncompatibleLockFile(const std::string& msg) + : std::runtime_error("Incompatible lock file. " + msg) + { + } +}; + +/// Thrown by SharedGroup::open() if the realm database was generated with a +/// format for Realm Mobile Platform but is being opened as a Realm Mobile +/// Database or vice versa, or of the history schema in the Realm file could not +/// be upgraded to the needed version. +struct IncompatibleHistories : util::File::AccessError { + IncompatibleHistories(const std::string& msg, const std::string& path) + : util::File::AccessError("Incompatible histories. " + msg, path) + { + } +}; + +/// A SharedGroup facilitates transactions. +/// +/// When multiple threads or processes need to access a database +/// concurrently, they must do so using transactions. By design, +/// Realm does not allow for multiple threads (or processes) to +/// share a single instance of SharedGroup. Instead, each concurrently +/// executing thread or process must use a separate instance of +/// SharedGroup. +/// +/// Each instance of SharedGroup manages a single transaction at a +/// time. That transaction can be either a read transaction, or a +/// write transaction. +/// +/// Utility classes ReadTransaction and WriteTransaction are provided +/// to make it safe and easy to work with transactions in a scoped +/// manner (by means of the RAII idiom). However, transactions can +/// also be explicitly started (begin_read(), begin_write()) and +/// stopped (end_read(), commit(), rollback()). +/// +/// If a transaction is active when the SharedGroup is destroyed, that +/// transaction is implicitly terminated, either by a call to +/// end_read() or rollback(). +/// +/// Two processes that want to share a database file must reside on +/// the same host. +/// +/// +/// Desired exception behavior (not yet fully implemented) +/// ------------------------------------------------------ +/// +/// - If any data access API function throws an unexpected exception during a +/// read transaction, the shared group accessor is left in state "error +/// during read". +/// +/// - If any data access API function throws an unexpected exception during a +/// write transaction, the shared group accessor is left in state "error +/// during write". +/// +/// - If SharedGroup::begin_write() or SharedGroup::begin_read() throws an +/// unexpected exception, the shared group accessor is left in state "no +/// transaction in progress". +/// +/// - SharedGroup::end_read() and SharedGroup::rollback() do not throw. +/// +/// - If SharedGroup::commit() throws an unexpected exception, the shared group +/// accessor is left in state "error during write" and the transaction was +/// not committed. +/// +/// - If SharedGroup::advance_read() or SharedGroup::promote_to_write() throws +/// an unexpected exception, the shared group accessor is left in state +/// "error during read". +/// +/// - If SharedGroup::commit_and_continue_as_read() or +/// SharedGroup::rollback_and_continue_as_read() throws an unexpected +/// exception, the shared group accessor is left in state "error during +/// write". +/// +/// It has not yet been decided exactly what an "unexpected exception" is, but +/// `std::bad_alloc` is surely one example. On the other hand, an expected +/// exception is one that is mentioned in the function specific documentation, +/// and is used to abort an operation due to a special, but expected condition. +/// +/// States +/// ------ +/// +/// - A newly created shared group accessor is in state "no transaction in +/// progress". +/// +/// - In state "error during read", almost all Realm API functions are +/// illegal on the connected group of accessors. The only valid operations +/// are destruction of the shared group, and SharedGroup::end_read(). If +/// SharedGroup::end_read() is called, the new state becomes "no transaction +/// in progress". +/// +/// - In state "error during write", almost all Realm API functions are +/// illegal on the connected group of accessors. The only valid operations +/// are destruction of the shared group, and SharedGroup::rollback(). If +/// SharedGroup::end_write() is called, the new state becomes "no transaction +/// in progress" +class SharedGroup { +public: + /// \brief Same as calling the corresponding version of open() on a instance + /// constructed in the unattached state. Exception safety note: if the + /// `upgrade_callback` throws, then the file will be closed properly and the + /// upgrade will be aborted. + explicit SharedGroup(const std::string& file, bool no_create = false, + const SharedGroupOptions options = SharedGroupOptions()); + + /// \brief Same as calling the corresponding version of open() on a instance + /// constructed in the unattached state. Exception safety note: if the + /// `upgrade_callback` throws, then the file will be closed properly and + /// the upgrade will be aborted. + explicit SharedGroup(Replication& repl, const SharedGroupOptions options = SharedGroupOptions()); + + struct unattached_tag { + }; + + /// Create a SharedGroup instance in its unattached state. It may + /// then be attached to a database file later by calling + /// open(). You may test whether this instance is currently in its + /// attached state by calling is_attached(). Calling any other + /// function (except the destructor) while in the unattached state + /// has undefined behavior. + SharedGroup(unattached_tag) noexcept; + + ~SharedGroup() noexcept; + + // Disable copying to prevent accessor errors. If you really want another + // instance, open another SharedGroup object on the same file. + SharedGroup(const SharedGroup&) = delete; + SharedGroup& operator=(const SharedGroup&) = delete; + + /// Attach this SharedGroup instance to the specified database file. + /// + /// While at least one instance of SharedGroup exists for a specific + /// database file, a "lock" file will be present too. The lock file will be + /// placed in the same directory as the database file, and its name will be + /// derived by appending ".lock" to the name of the database file. + /// + /// When multiple SharedGroup instances refer to the same file, they must + /// specify the same durability level, otherwise an exception will be + /// thrown. + /// + /// \param file Filesystem path to a Realm database file. + /// + /// \param no_create If the database file does not already exist, it will be + /// created (unless this is set to true.) When multiple threads are involved, + /// it is safe to let the first thread, that gets to it, create the file. + /// + /// \param options See SharedGroupOptions for details of each option. + /// Sensible defaults are provided if this parameter is left out. + /// + /// Calling open() on a SharedGroup instance that is already in the attached + /// state has undefined behavior. + /// + /// \throw util::File::AccessError If the file could not be opened. If the + /// reason corresponds to one of the exception types that are derived from + /// util::File::AccessError, the derived exception type is thrown. Note that + /// InvalidDatabase is among these derived exception types. + /// + /// \throw FileFormatUpgradeRequired only if \a SharedGroupOptions::allow_upgrade + /// is `false` and an upgrade is required. + void open(const std::string& file, bool no_create = false, + const SharedGroupOptions options = SharedGroupOptions()); + + /// Open this group in replication mode. The specified Replication instance + /// must remain in existence for as long as the SharedGroup. + void open(Replication&, const SharedGroupOptions options = SharedGroupOptions()); + + /// Close any open database, returning to the unattached state. + void close() noexcept; + + /// A SharedGroup may be created in the unattached state, and then + /// later attached to a file with a call to open(). Calling any + /// function other than open(), is_attached(), and ~SharedGroup() + /// on an unattached instance results in undefined behavior. + bool is_attached() const noexcept; + + /// Reserve disk space now to avoid allocation errors at a later + /// point in time, and to minimize on-disk fragmentation. In some + /// cases, less fragmentation translates into improved + /// performance. + /// + /// When supported by the system, a call to this function will + /// make the database file at least as big as the specified size, + /// and cause space on the target device to be allocated (note + /// that on many systems on-disk allocation is done lazily by + /// default). If the file is already bigger than the specified + /// size, the size will be unchanged, and on-disk allocation will + /// occur only for the initial section that corresponds to the + /// specified size. On systems that do not support preallocation, + /// this function has no effect. To know whether preallocation is + /// supported by Realm on your platform, call + /// util::File::is_prealloc_supported(). + /// + /// It is an error to call this function on an unattached shared + /// group. Doing so will result in undefined behavior. + void reserve(size_t size_in_bytes); + + /// Querying for changes: + /// + /// NOTE: + /// "changed" means that one or more commits has been made to the database + /// since the SharedGroup (on which wait_for_change() is called) last + /// started, committed, promoted or advanced a transaction. If the + /// SharedGroup has not yet begun a transaction, "changed" is undefined. + /// + /// No distinction is made between changes done by another process + /// and changes done by another thread in the same process as the caller. + /// + /// Has db been changed ? + bool has_changed(); + + /// The calling thread goes to sleep until the database is changed, or + /// until wait_for_change_release() is called. After a call to + /// wait_for_change_release() further calls to wait_for_change() will return + /// immediately. To restore the ability to wait for a change, a call to + /// enable_wait_for_change() is required. Return true if the database has + /// changed, false if it might have. + bool wait_for_change(); + + /// release any thread waiting in wait_for_change() on *this* SharedGroup. + void wait_for_change_release(); + + /// re-enable waiting for change + void enable_wait_for_change(); + // Transactions: + + using version_type = _impl::History::version_type; + using VersionID = realm::VersionID; + + /// Thrown by begin_read() if the specified version does not correspond to a + /// bound (or tethered) snapshot. + struct BadVersion; + + /// \defgroup group_shared_transactions + //@{ + + /// begin_read() initiates a new read transaction. A read transaction is + /// bound to, and provides access to a particular snapshot of the underlying + /// Realm (in general the latest snapshot, but see \a version). It cannot be + /// used to modify the Realm, and in that sense, a read transaction is not a + /// real transaction. + /// + /// begin_write() initiates a new write transaction. A write transaction + /// allows the application to both read and modify the underlying Realm + /// file. At most one write transaction can be in progress at any given time + /// for a particular underlying Realm file. If another write transaction is + /// already in progress, begin_write() will block the caller until the other + /// write transaction terminates. No guarantees are made about the order in + /// which multiple concurrent requests will be served. + /// + /// It is an error to call begin_read() or begin_write() on a SharedGroup + /// object with an active read or write transaction. + /// + /// If begin_read() or begin_write() throws, no transaction is initiated, + /// and the application may try to initiate a new read or write transaction + /// later. + /// + /// end_read() terminates the active read transaction. If no read + /// transaction is active, end_read() does nothing. It is an error to call + /// this function on a SharedGroup object with an active write + /// transaction. end_read() does not throw. + /// + /// commit() commits all changes performed in the context of the active + /// write transaction, and thereby terminates that transaction. This + /// produces a new snapshot in the underlying Realm. commit() returns the + /// version associated with the new snapshot. It is an error to call + /// commit() when there is no active write transaction. If commit() throws, + /// no changes will have been committed, and the transaction will still be + /// active, but in a bad state. In that case, the application must either + /// call rollback() to terminate the bad transaction (in which case a new + /// transaction can be initiated), call close() which also terminates the + /// bad transaction, or destroy the SharedGroup object entirely. When the + /// transaction is in a bad state, the application is not allowed to call + /// any method on the Group accessor or on any of its subordinate accessors + /// (Table, Row, Descriptor). Note that the transaction is also left in a + /// bad state when a modifying operation on any subordinate accessor throws. + /// + /// rollback() terminates the active write transaction and discards any + /// changes performed in the context of it. If no write transaction is + /// active, rollback() does nothing. It is an error to call this function in + /// a SharedGroup object with an active read transaction. rollback() does + /// not throw. + /// + /// the Group accessor and all subordinate accessors (Table, Row, + /// Descriptor) that are obtained in the context of a particular read or + /// write transaction will become detached upon termination of that + /// transaction, which means that they can no longer be used to access the + /// underlying objects. + /// + /// Subordinate accessors that were detached at the end of the previous + /// read or write transaction will not be automatically reattached when a + /// new transaction is initiated. The application must reobtain new + /// accessors during a new transaction to regain access to the underlying + /// objects. + /// + /// \param version If specified, this must be the version associated with a + /// *bound* snapshot. A snapshot is said to be bound (or tethered) if there + /// is at least one active read or write transaction bound to it. A read + /// transaction is bound to the snapshot that it provides access to. A write + /// transaction is bound to the latest snapshot available at the time of + /// initiation of the write transaction. If the specified version is not + /// associated with a bound snapshot, this function throws BadVersion. + /// + /// \throw BadVersion Thrown by begin_read() if the specified version does + /// not correspond to a bound (or tethered) snapshot. + + const Group& begin_read(VersionID version = VersionID()); + void end_read() noexcept; + Group& begin_write(); + // Return true (and take the write lock) if there is no other write + // in progress. In case of contention return false immediately. + // If the write lock is obtained, also provide the Group associated + // with the SharedGroup for further operations. + bool try_begin_write(Group*& group); + version_type commit(); + void rollback() noexcept; + // report statistics of last commit done on THIS shared group. + // The free space reported is what can be expected to be freed + // by compact(). This may not correspond to the space which is free + // at the point where get_stats() is called, since that will include + // memory required to hold older versions of data, which still + // needs to be available. + void get_stats(size_t& free_space, size_t& used_space); + //@} + + enum TransactStage { + transact_Ready, + transact_Reading, + transact_Writing, + }; + + /// Get the current transaction type + TransactStage get_transact_stage() const noexcept; + + /// Get a version id which may be used to request a different SharedGroup + /// to start transaction at a specific version. + VersionID get_version_of_current_transaction(); + + /// Report the number of distinct versions currently stored in the database. + /// Note: the database only cleans up versions as part of commit, so ending + /// a read transaction will not immediately release any versions. + uint_fast64_t get_number_of_versions(); + + /// Compact the database file. + /// - The method will throw if called inside a transaction. + /// - The method will throw if called in unattached state. + /// - The method will return false if other SharedGroups are accessing the + /// database in which case compaction is not done. This is not + /// necessarily an error. + /// It will return true following successful compaction. + /// While compaction is in progress, attempts by other + /// threads or processes to open the database will wait. + /// Be warned that resource requirements for compaction is proportional to + /// the amount of live data in the database. + /// Compaction works by writing the database contents to a temporary + /// database file and then replacing the database with the temporary one. + /// The name of the temporary file is formed by appending + /// ".tmp_compaction_space" to the name of the database + /// + /// FIXME: This function is not yet implemented in an exception-safe manner, + /// therefore, if it throws, the application should not attempt to + /// continue. If may not even be safe to destroy the SharedGroup object. + /// + /// WARNING / FIXME: compact() should NOT be exposed publicly on Windows + /// because it's not crash safe! It may corrupt your database if something fails + bool compact(); + +#ifdef REALM_DEBUG + void test_ringbuf(); +#endif + + /// To handover a table view, query, linkview or row accessor of type T, you + /// must wrap it into a Handover for the transfer. Wrapping and + /// unwrapping of a handover object is done by the methods + /// 'export_for_handover()' and 'import_from_handover()' declared below. + /// 'export_for_handover()' returns a Handover object, and + /// 'import_for_handover()' consumes that object, producing a new accessor + /// which is ready for use in the context of the importing SharedGroup. + /// + /// The Handover always creates a new accessor object at the importing side. + /// For TableViews, there are 3 forms of handover. + /// + /// - with payload move: the payload is handed over and ends up as a payload + /// held by the accessor at the importing side. The accessor on the + /// exporting side will rerun its query and generate a new payload, if + /// TableView::sync_if_needed() is called. If the original payload was in + /// sync at the exporting side, it will also be in sync at the importing + /// side. This is indicated to handover_export() by the argument + /// MutableSourcePayload::Move + /// + /// - with payload copy: a copy of the payload is handed over, so both the + /// accessors on the exporting side *and* the accessors created at the + /// importing side has their own payload. This is indicated to + /// handover_export() by the argument ConstSourcePayload::Copy + /// + /// - without payload: the payload stays with the accessor on the exporting + /// side. On the importing side, the new accessor is created without + /// payload. A call to TableView::sync_if_needed() will trigger generation + /// of a new payload. This form of handover is indicated to + /// handover_export() by the argument ConstSourcePayload::Stay. + /// + /// For all other (non-TableView) accessors, handover is done with payload + /// copy, since the payload is trivial. + /// + /// Handover *without* payload is useful when you want to ship a tableview + /// with its query for execution in a background thread. Handover with + /// *payload move* is useful when you want to transfer the result back. + /// + /// Handover *without* payload or with payload copy is guaranteed *not* to + /// change the accessors on the exporting side. + /// + /// Handover is *not* thread safe and should be carried out + /// by the thread that "owns" the involved accessors. + /// + /// Handover is transitive: + /// If the object being handed over depends on other views + /// (table- or link- ), those objects will be handed over as well. The mode + /// of handover (payload copy, payload move, without payload) is applied + /// recursively. Note: If you are handing over a tableview dependent upon + /// another tableview and using MutableSourcePayload::Move, + /// you are on thin ice! + /// + /// On the importing side, the top-level accessor being created during + /// import takes ownership of all other accessors (if any) being created as + /// part of the import. + + /// Type used to support handover of accessors between shared groups. + template + struct Handover; + + /// thread-safe/const export (mode is Stay or Copy) + /// during export, the following operations on the shared group is locked: + /// - advance_read(), promote_to_write(), commit_and_continue_as_read(), + /// rollback_and_continue_as_read(), close() + template + std::unique_ptr> export_for_handover(const T& accessor, ConstSourcePayload mode); + + // specialization for handover of Rows + template + std::unique_ptr>> export_for_handover(const BasicRow& accessor); + + // destructive export (mode is Move) + template + std::unique_ptr> export_for_handover(T& accessor, MutableSourcePayload mode); + + /// Import an accessor wrapped in a handover object. The import will fail + /// if the importing SharedGroup is viewing a version of the database that + /// is different from the exporting SharedGroup. The call to + /// import_from_handover is not thread-safe. + template + std::unique_ptr import_from_handover(std::unique_ptr> handover); + + // We need two cases for handling of LinkViews, because they are ref counted. + std::unique_ptr> export_linkview_for_handover(const LinkViewRef& accessor); + LinkViewRef import_linkview_from_handover(std::unique_ptr> handover); + + // likewise for Tables. + std::unique_ptr> export_table_for_handover(const TableRef& accessor); + TableRef import_table_from_handover(std::unique_ptr> handover); + + /// When doing handover to background tasks that may be run later, we + /// may want to momentarily pin the current version until the other thread + /// has retrieved it. + /// + /// Pinning can be done in both read- and write-transactions, but with different + /// semantics. When pinning during a read-transaction, the version pinned is the + /// one accessible during the read-transaction. When pinning during a write-transaction, + /// the version pinned will be the last version that was succesfully committed to the + /// realm file at the point in time, when the write-transaction was started. + /// + /// The release is not thread-safe, so it has to be done on the SharedGroup + /// associated with the thread calling unpin_version(), and the SharedGroup + /// must be attached to the realm file at the point of unpinning. + + // Pin version for handover (not thread safe) + VersionID pin_version(); + + // Release pinned version (not thread safe) + void unpin_version(VersionID version); + +private: + struct SharedInfo; + struct ReadCount; + struct ReadLockInfo { + uint_fast64_t m_version = std::numeric_limits::max(); + uint_fast32_t m_reader_idx = 0; + ref_type m_top_ref = 0; + size_t m_file_size = 0; + }; + class ReadLockUnlockGuard; + + // Member variables + size_t m_free_space = 0; + size_t m_used_space = 0; + Group m_group; + ReadLockInfo m_read_lock; + uint_fast32_t m_local_max_entry; + util::File m_file; + util::File::Map m_file_map; // Never remapped + util::File::Map m_reader_map; + bool m_wait_for_change_enabled; + std::string m_lockfile_path; + std::string m_lockfile_prefix; + std::string m_db_path; + std::string m_coordination_dir; + const char* m_key; + TransactStage m_transact_stage; + util::InterprocessMutex m_writemutex; +#ifdef REALM_ASYNC_DAEMON + util::InterprocessMutex m_balancemutex; +#endif + util::InterprocessMutex m_controlmutex; +#ifndef _WIN32 +#ifdef REALM_ASYNC_DAEMON + util::InterprocessCondVar m_room_to_write; + util::InterprocessCondVar m_work_to_do; + util::InterprocessCondVar m_daemon_becomes_ready; +#endif + util::InterprocessCondVar m_new_commit_available; + util::InterprocessCondVar m_pick_next_writer; +#endif + std::function m_upgrade_callback; + + void do_open(const std::string& file, bool no_create, bool is_backend, const SharedGroupOptions options); + + // Ring buffer management + bool ringbuf_is_empty() const noexcept; + size_t ringbuf_size() const noexcept; + size_t ringbuf_capacity() const noexcept; + bool ringbuf_is_first(size_t ndx) const noexcept; + void ringbuf_remove_first() noexcept; + size_t ringbuf_find(uint64_t version) const noexcept; + ReadCount& ringbuf_get(size_t ndx) noexcept; + ReadCount& ringbuf_get_first() noexcept; + ReadCount& ringbuf_get_last() noexcept; + void ringbuf_put(const ReadCount& v); + void ringbuf_expand(); + + /// Grab a read lock on the snapshot associated with the specified + /// version. If `version_id == VersionID()`, a read lock will be grabbed on + /// the latest available snapshot. Fails if the snapshot is no longer + /// available. + /// + /// As a side effect update memory mapping to ensure that the ringbuffer + /// entries referenced in the readlock info is accessible. + /// + /// FIXME: It needs to be made more clear exactly under which conditions + /// this function fails. Also, why is it useful to promise anything about + /// detection of bad versions? Can we really promise enough to make such a + /// promise useful to the caller? + void grab_read_lock(ReadLockInfo&, VersionID); + + // Release a specific read lock. The read lock MUST have been obtained by a + // call to grab_read_lock(). + void release_read_lock(ReadLockInfo&) noexcept; + + void do_begin_read(VersionID, bool writable); + void do_end_read() noexcept; + /// return true if write transaction can commence, false otherwise. + bool do_try_begin_write(); + void do_begin_write(); + version_type do_commit(); + void do_end_write() noexcept; + + /// Returns the version of the latest snapshot. + version_type get_version_of_latest_snapshot(); + + /// Returns the version of the snapshot bound in the current read or write + /// transaction. It is an error to call this function when no transaction is + /// in progress. + version_type get_version_of_bound_snapshot() const noexcept; + + // make sure the given index is within the currently mapped area. + // if not, expand the mapped area. Returns true if the area is expanded. + bool grow_reader_mapping(uint_fast32_t index); + + // Must be called only by someone that has a lock on the write + // mutex. + void low_level_commit(uint_fast64_t new_version); + + void do_async_commits(); + + /// Upgrade file format and/or history schema + void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version, + int current_hist_schema_version, int target_hist_schema_version); + + //@{ + /// See LangBindHelper. + template + void advance_read(O* observer, VersionID); + template + void promote_to_write(O* observer); + version_type commit_and_continue_as_read(); + template + void rollback_and_continue_as_read(O* observer); + //@} + + /// Returns true if, and only if _impl::History::update_early_from_top_ref() + /// was called during the execution of this function. + template + bool do_advance_read(O* observer, VersionID, _impl::History&); + + /// If there is an associated \ref Replication object, then this function + /// returns `repl->get_history()` where `repl` is that Replication object, + /// otherwise this function returns null. + _impl::History* get_history(); + + int get_file_format_version() const noexcept; + + /// finish up the process of starting a write transaction. Internal use only. + void finish_begin_write(); + + friend class _impl::SharedGroupFriend; +}; + + +inline void SharedGroup::get_stats(size_t& free_space, size_t& used_space) { + free_space = m_free_space; + used_space = m_used_space; +} + + +class ReadTransaction { +public: + ReadTransaction(SharedGroup& sg) + : m_shared_group(sg) + { + m_shared_group.begin_read(); // Throws + } + + ~ReadTransaction() noexcept + { + m_shared_group.end_read(); + } + + bool has_table(StringData name) const noexcept + { + return get_group().has_table(name); + } + + ConstTableRef get_table(size_t table_ndx) const + { + return get_group().get_table(table_ndx); // Throws + } + + ConstTableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + const Group& get_group() const noexcept; + + /// Get the version of the snapshot to which this read transaction is bound. + SharedGroup::version_type get_version() const noexcept; + +private: + SharedGroup& m_shared_group; +}; + + +class WriteTransaction { +public: + WriteTransaction(SharedGroup& sg) + : m_shared_group(&sg) + { + m_shared_group->begin_write(); // Throws + } + + ~WriteTransaction() noexcept + { + if (m_shared_group) + m_shared_group->rollback(); + } + + bool has_table(StringData name) const noexcept + { + return get_group().has_table(name); + } + + TableRef get_table(size_t table_ndx) const + { + return get_group().get_table(table_ndx); // Throws + } + + TableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + TableRef add_table(StringData name, bool require_unique_name = true) const + { + return get_group().add_table(name, require_unique_name); // Throws + } + + TableRef get_or_add_table(StringData name, bool* was_added = nullptr) const + { + return get_group().get_or_add_table(name, was_added); // Throws + } + + Group& get_group() const noexcept; + + /// Get the version of the snapshot on which this write transaction is + /// based. + SharedGroup::version_type get_version() const noexcept; + + SharedGroup::version_type commit() + { + REALM_ASSERT(m_shared_group); + SharedGroup::version_type new_version = m_shared_group->commit(); + m_shared_group = nullptr; + return new_version; + } + + void rollback() noexcept + { + REALM_ASSERT(m_shared_group); + m_shared_group->rollback(); + m_shared_group = nullptr; + } + +private: + SharedGroup* m_shared_group; +}; + + +// Implementation: + +struct SharedGroup::BadVersion : std::exception { +}; + +inline SharedGroup::SharedGroup(const std::string& file, bool no_create, const SharedGroupOptions options) + : m_group(Group::shared_tag()) + , m_upgrade_callback(std::move(options.upgrade_callback)) +{ + open(file, no_create, options); // Throws +} + +inline SharedGroup::SharedGroup(unattached_tag) noexcept + : m_group(Group::shared_tag()) +{ +} + +inline SharedGroup::SharedGroup(Replication& repl, const SharedGroupOptions options) + : m_group(Group::shared_tag()) + , m_upgrade_callback(std::move(options.upgrade_callback)) +{ + open(repl, options); // Throws +} + +inline void SharedGroup::open(const std::string& path, bool no_create_file, const SharedGroupOptions options) +{ + // Exception safety: Since open() is called from constructors, if it throws, + // it must leave the file closed. + + bool is_backend = false; + do_open(path, no_create_file, is_backend, options); // Throws +} + +inline void SharedGroup::open(Replication& repl, const SharedGroupOptions options) +{ + // Exception safety: Since open() is called from constructors, if it throws, + // it must leave the file closed. + + REALM_ASSERT(!is_attached()); + + repl.initialize(*this); // Throws + + typedef _impl::GroupFriend gf; + gf::set_replication(m_group, &repl); + + std::string file = repl.get_database_path(); + bool no_create = false; + bool is_backend = false; + do_open(file, no_create, is_backend, options); // Throws +} + +inline bool SharedGroup::is_attached() const noexcept +{ + return m_file_map.is_attached(); +} + +inline SharedGroup::TransactStage SharedGroup::get_transact_stage() const noexcept +{ + return m_transact_stage; +} + +inline SharedGroup::version_type SharedGroup::get_version_of_bound_snapshot() const noexcept +{ + return m_read_lock.m_version; +} + +class SharedGroup::ReadLockUnlockGuard { +public: + ReadLockUnlockGuard(SharedGroup& shared_group, ReadLockInfo& read_lock) noexcept + : m_shared_group(shared_group) + , m_read_lock(&read_lock) + { + } + ~ReadLockUnlockGuard() noexcept + { + if (m_read_lock) + m_shared_group.release_read_lock(*m_read_lock); + } + void release() noexcept + { + m_read_lock = 0; + } + +private: + SharedGroup& m_shared_group; + ReadLockInfo* m_read_lock; +}; + + +template +struct SharedGroup::Handover { + std::unique_ptr patch; + std::unique_ptr clone; + VersionID version; +}; + +template +std::unique_ptr> SharedGroup::export_for_handover(const T& accessor, ConstSourcePayload mode) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr> result(new Handover()); + // Implementation note: + // often, the return value from clone will be T*, BUT it may be ptr to some + // base of T instead, so we must cast it to T*. This is always safe, because + // no matter the type, clone() will clone the actual accessor instance, and + // hence return an instance of the same type. + result->clone.reset(dynamic_cast(accessor.clone_for_handover(result->patch, mode).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr>> SharedGroup::export_for_handover(const BasicRow& accessor) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr>> result(new Handover>()); + // See implementation note above. + result->clone.reset(dynamic_cast*>(accessor.clone_for_handover(result->patch).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr> SharedGroup::export_for_handover(T& accessor, MutableSourcePayload mode) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr> result(new Handover()); + // see implementation note above. + result->clone.reset(dynamic_cast(accessor.clone_for_handover(result->patch, mode).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr SharedGroup::import_from_handover(std::unique_ptr> handover) +{ + if (handover->version != get_version_of_current_transaction()) { + throw BadVersion(); + } + std::unique_ptr result = move(handover->clone); + result->apply_and_consume_patch(handover->patch, m_group); + return result; +} + +template +inline void SharedGroup::advance_read(O* observer, VersionID version_id) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + + // It is an error if the new version precedes the currently bound one. + if (version_id.version < m_read_lock.m_version) + throw LogicError(LogicError::bad_version); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + do_advance_read(observer, version_id, *hist); // Throws +} + +template +inline void SharedGroup::promote_to_write(O* observer) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + do_begin_write(); // Throws + try { + VersionID version = VersionID(); // Latest + bool history_updated = do_advance_read(observer, version, *hist); // Throws + + Replication* repl = m_group.get_replication(); + REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` + version_type current_version = m_read_lock.m_version; + repl->initiate_transact(current_version, history_updated); // Throws + + // If the group has no top array (top_ref == 0), create a new node + // structure for an empty group now, to be ready for modifications. See + // also Group::attach_shared(). + using gf = _impl::GroupFriend; + gf::create_empty_group_when_missing(m_group); // Throws + } + catch (...) { + do_end_write(); + throw; + } + + m_transact_stage = transact_Writing; +} + +template +inline void SharedGroup::rollback_and_continue_as_read(O* observer) +{ + if (m_transact_stage != transact_Writing) + throw LogicError(LogicError::wrong_transact_state); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + // Mark all managed space (beyond the attached file) as free. + using gf = _impl::GroupFriend; + gf::reset_free_space_tracking(m_group); // Throws + + BinaryData uncommitted_changes = hist->get_uncommitted_changes(); + + // FIXME: We are currently creating two transaction log parsers, one here, + // and one in advance_transact(). That is wasteful as the parser creation is + // expensive. + _impl::SimpleInputStream in(uncommitted_changes.data(), uncommitted_changes.size()); + _impl::TransactLogParser parser; // Throws + _impl::TransactReverser reverser; + parser.parse(in, reverser); // Throws + + if (observer && uncommitted_changes.size()) { + _impl::ReversedNoCopyInputStream reversed_in(reverser); + parser.parse(reversed_in, *observer); // Throws + observer->parse_complete(); // Throws + } + + ref_type top_ref = m_read_lock.m_top_ref; + size_t file_size = m_read_lock.m_file_size; + _impl::ReversedNoCopyInputStream reversed_in(reverser); + gf::advance_transact(m_group, top_ref, file_size, reversed_in); // Throws + + do_end_write(); + + Replication* repl = gf::get_replication(m_group); + REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` + repl->abort_transact(); + + m_transact_stage = transact_Reading; +} + +template +inline bool SharedGroup::do_advance_read(O* observer, VersionID version_id, _impl::History& hist) +{ + ReadLockInfo new_read_lock; + grab_read_lock(new_read_lock, version_id); // Throws + REALM_ASSERT(new_read_lock.m_version >= m_read_lock.m_version); + if (new_read_lock.m_version == m_read_lock.m_version) { + release_read_lock(new_read_lock); + // _impl::History::update_early_from_top_ref() was not called + return false; + } + + ReadLockUnlockGuard g(*this, new_read_lock); + { + version_type new_version = new_read_lock.m_version; + size_t new_file_size = new_read_lock.m_file_size; + ref_type new_top_ref = new_read_lock.m_top_ref; + + // Synchronize readers view of the file + SlabAlloc& alloc = m_group.m_alloc; + alloc.update_reader_view(new_file_size); + + hist.update_early_from_top_ref(new_version, new_file_size, new_top_ref); // Throws + } + + if (observer) { + // This has to happen in the context of the originally bound snapshot + // and while the read transaction is still in a fully functional state. + _impl::TransactLogParser parser; + version_type old_version = m_read_lock.m_version; + version_type new_version = new_read_lock.m_version; + _impl::ChangesetInputStream in(hist, old_version, new_version); + parser.parse(in, *observer); // Throws + observer->parse_complete(); // Throws + } + + // The old read lock must be retained for as long as the change history is + // accessed (until Group::advance_transact() returns). This ensures that the + // oldest needed changeset remains in the history, even when the history is + // implemented as a separate unversioned entity outside the Realm (i.e., the + // old implementation and ShortCircuitHistory in + // test_lang_Bind_helper.cpp). On the other hand, if it had been the case, + // that the history was always implemented as a versioned entity, that was + // part of the Realm state, then it would not have been necessary to retain + // the old read lock beyond this point. + + { + version_type old_version = m_read_lock.m_version; + version_type new_version = new_read_lock.m_version; + ref_type new_top_ref = new_read_lock.m_top_ref; + size_t new_file_size = new_read_lock.m_file_size; + _impl::ChangesetInputStream in(hist, old_version, new_version); + m_group.advance_transact(new_top_ref, new_file_size, in); // Throws + } + + g.release(); + release_read_lock(m_read_lock); + m_read_lock = new_read_lock; + + return true; // _impl::History::update_early_from_top_ref() was called +} + +inline _impl::History* SharedGroup::get_history() +{ + using gf = _impl::GroupFriend; + if (Replication* repl = gf::get_replication(m_group)) + return repl->get_history(); + return 0; +} + +inline int SharedGroup::get_file_format_version() const noexcept +{ + using gf = _impl::GroupFriend; + return gf::get_file_format_version(m_group); +} + + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the SharedGroup class. +class _impl::SharedGroupFriend { +public: + static Group& get_group(SharedGroup& sg) noexcept + { + return sg.m_group; + } + + template + static void advance_read(SharedGroup& sg, O* obs, SharedGroup::VersionID ver) + { + sg.advance_read(obs, ver); // Throws + } + + template + static void promote_to_write(SharedGroup& sg, O* obs) + { + sg.promote_to_write(obs); // Throws + } + + static SharedGroup::version_type commit_and_continue_as_read(SharedGroup& sg) + { + return sg.commit_and_continue_as_read(); // Throws + } + + template + static void rollback_and_continue_as_read(SharedGroup& sg, O* obs) + { + sg.rollback_and_continue_as_read(obs); // Throws + } + + static void async_daemon_open(SharedGroup& sg, const std::string& file) + { + bool no_create = true; + bool is_backend = true; + SharedGroupOptions options; + options.durability = SharedGroupOptions::Durability::Async; + options.encryption_key = nullptr; + options.allow_file_format_upgrade = false; + sg.do_open(file, no_create, is_backend, options); // Throws + } + + static int get_file_format_version(const SharedGroup& sg) noexcept + { + return sg.get_file_format_version(); + } + + static SharedGroup::version_type get_version_of_latest_snapshot(SharedGroup& sg) + { + return sg.get_version_of_latest_snapshot(); + } + + static SharedGroup::version_type get_version_of_bound_snapshot(const SharedGroup& sg) noexcept + { + return sg.get_version_of_bound_snapshot(); + } +}; + +inline const Group& ReadTransaction::get_group() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_group(m_shared_group); +} + +inline SharedGroup::version_type ReadTransaction::get_version() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_bound_snapshot(m_shared_group); +} + +inline Group& WriteTransaction::get_group() const noexcept +{ + REALM_ASSERT(m_shared_group); + using sgf = _impl::SharedGroupFriend; + return sgf::get_group(*m_shared_group); +} + +inline SharedGroup::version_type WriteTransaction::get_version() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_bound_snapshot(*m_shared_group); +} + +} // namespace realm + +#endif // REALM_GROUP_SHARED_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared_options.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared_options.hpp new file mode 100644 index 0000000..2a2de19 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_shared_options.hpp @@ -0,0 +1,103 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_GROUP_SHARED_OPTIONS_HPP +#define REALM_GROUP_SHARED_OPTIONS_HPP + +#include +#include + +namespace realm { + +struct SharedGroupOptions { + + /// The persistence level of the SharedGroup. + /// uint16_t is the type of SharedGroup::SharedInfo::durability + enum class Durability : uint16_t { + Full, + MemOnly, + Async ///< Not yet supported on windows. + }; + + explicit SharedGroupOptions(Durability level = Durability::Full, const char* key = nullptr, + bool allow_upgrade = true, + std::function file_upgrade_callback = std::function(), + std::string temp_directory = sys_tmp_dir) + : durability(level) + , encryption_key(key) + , allow_file_format_upgrade(allow_upgrade) + , upgrade_callback(file_upgrade_callback) + , temp_dir(temp_directory) + { + } + + explicit SharedGroupOptions(const char* key) + : durability(Durability::Full) + , encryption_key(key) + , allow_file_format_upgrade(true) + , upgrade_callback(std::function()) + , temp_dir(sys_tmp_dir) + { + } + + /// The persistence level of the Realm file. See Durability. + Durability durability; + + /// The key to encrypt and decrypt the Realm file with, or nullptr to + /// indicate that encryption should not be used. + const char* encryption_key; + + /// If \a allow_file_format_upgrade is set to `true`, this function will + /// automatically upgrade the file format used in the specified Realm file + /// if necessary (and if it is possible). In order to prevent this, set \a + /// allow_upgrade to `false`. + /// + /// If \a allow_upgrade is set to `false`, only two outcomes are possible: + /// + /// - the specified Realm file is already using the latest file format, and + /// can be used, or + /// + /// - the specified Realm file uses a deprecated file format, resulting a + /// the throwing of FileFormatUpgradeRequired. + bool allow_file_format_upgrade; + + /// Optionally allows a custom function to be called immediately after the + /// Realm file is upgraded. The two parameters in the function are the + /// previous version and the version just upgraded to, respectively. + /// If the callback function throws, the Realm file will safely abort the + /// upgrade (rollback the transaction) but the SharedGroup will not be opened. + std::function upgrade_callback; + + /// A path to a directory where Realm can write temporary files or pipes to. + /// This string should include a trailing slash '/'. + std::string temp_dir; + + /// sys_tmp_dir will be used if the temp_dir is empty when creating SharedGroupOptions. + /// It must be writable and allowed to create pipe/fifo file on it. + /// set_sys_tmp_dir is not a thread-safe call and it is only supposed to be called once + // when process starts. + static void set_sys_tmp_dir(const std::string& dir) noexcept { sys_tmp_dir = dir; } + static std::string get_sys_tmp_dir() noexcept { return sys_tmp_dir; } + +private: + static std::string sys_tmp_dir; +}; + +} // end namespace realm + +#endif // REALM_GROUP_SHARED_OPTIONS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_writer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_writer.hpp new file mode 100644 index 0000000..5067f18 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/group_writer.hpp @@ -0,0 +1,165 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_GROUP_WRITER_HPP +#define REALM_GROUP_WRITER_HPP + +#include // unint8_t etc +#include + +#include +#include +#include +#include + + +namespace realm { + +// Pre-declarations +class Group; +class SlabAlloc; + + +/// This class is not supposed to be reused for multiple write sessions. In +/// particular, do not reuse it in case any of the functions throw. +/// +/// FIXME: Move this class to namespace realm::_impl and to subdir src/realm/impl. +class GroupWriter : public _impl::ArrayWriterBase { +public: + // For groups in transactional mode (Group::m_is_shared), this constructor + // must be called while a write transaction is in progress. + // + // The constructor adds free-space tracking information to the specified + // group, if it is not already present (4th and 5th entry in + // Group::m_top). If the specified group is in transactional mode + // (Group::m_is_shared), the constructor also adds version tracking + // information to the group, if it is not already present (6th and 7th entry + // in Group::m_top). + GroupWriter(Group&); + ~GroupWriter(); + + void set_versions(uint64_t current, uint64_t read_lock) noexcept; + + /// Write all changed array nodes into free space. + /// + /// Returns the new top ref. When in full durability mode, call + /// commit() with the returned top ref. + ref_type write_group(); + + /// Flush changes to physical medium, then write the new top ref + /// to the file header, then flush again. Pass the top ref + /// returned by write_group(). + void commit(ref_type new_top_ref); + + size_t get_file_size() const noexcept; + + /// Write the specified chunk into free space. + void write(const char* data, size_t size); + + ref_type write_array(const char*, size_t, uint32_t) override; + +#ifdef REALM_DEBUG + void dump(); +#endif + + size_t get_free_space(); +private: + class MapWindow; + Group& m_group; + SlabAlloc& m_alloc; + ArrayInteger m_free_positions; // 4th slot in Group::m_top + ArrayInteger m_free_lengths; // 5th slot in Group::m_top + ArrayInteger m_free_versions; // 6th slot in Group::m_top + uint64_t m_current_version; + uint64_t m_readlock_version; + + // Currently cached memory mappings. We keep as many as 16 1MB windows + // open for writing. The allocator will favor sequential allocation + // from a modest number of windows, depending upon fragmentation, so + // 16 windows should be more than enough. If more than 16 windows are + // needed, the least recently used is sync'ed and closed to make room + // for a new one. The windows are kept in MRU (most recently used) order. + const static int num_map_windows = 16; + std::vector> m_map_windows; + + // Get a suitable memory mapping for later access: + // potentially adding it to the cache, potentially closing + // the least recently used and sync'ing it to disk + MapWindow* get_window(ref_type start_ref, size_t size); + + // Sync all cached memory mappings + void sync_all_mappings(); + + // Merge adjacent chunks + void merge_free_space(); + + /// Allocate a chunk of free space of the specified size. The + /// specified size must be 8-byte aligned. Extend the file if + /// required. The returned chunk is removed from the amount of + /// remaing free space. The returned chunk is guaranteed to be + /// within a single contiguous memory mapping. + /// + /// \return The position within the database file of the allocated + /// chunk. + size_t get_free_space(size_t size); + + /// Find a block of free space that is at least as big as the + /// specified size and which will allow an allocation that is mapped + /// inside a contiguous address range. The specified size does not + /// need to be 8-byte aligned. Extend the file if required. + /// The returned chunk is not removed from the amount of remaing + /// free space. + /// + /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` + /// is the index of a chunk whose size is at least the requestd + /// size, and `chunk_size` is the size of that chunk. + std::pair reserve_free_space(size_t size); + + /// Search only a range of the free list for a block as big as the + /// specified size. Return a pair with index and size of the found chunk. + /// \param found indicates whether a suitable block was found. + std::pair search_free_space_in_part_of_freelist(size_t size, size_t begin, size_t end, + bool& found); + + /// Extend the file to ensure that a chunk of free space of the + /// specified size is available. The specified size does not need + /// to be 8-byte aligned. This function guarantees that it will + /// add at most one entry to the free-lists. + /// + /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` + /// is the index of a chunk whose size is at least the requestd + /// size, and `chunk_size` is the size of that chunk. + std::pair extend_free_space(size_t requested_size); + + void write_array_at(MapWindow* window, ref_type, const char* data, size_t size); + size_t split_freelist_chunk(size_t index, size_t start_pos, size_t alloc_pos, size_t chunk_size, bool is_shared); +}; + + +// Implementation: + +inline void GroupWriter::set_versions(uint64_t current, uint64_t read_lock) noexcept +{ + REALM_ASSERT(read_lock <= current); + m_current_version = current; + m_readlock_version = read_lock; +} + +} // namespace realm + +#endif // REALM_GROUP_WRITER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/handover_defs.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/handover_defs.hpp new file mode 100644 index 0000000..d398017 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/handover_defs.hpp @@ -0,0 +1,85 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_HANDOVER_DEFS +#define REALM_HANDOVER_DEFS + +#include +#include + +namespace realm { + +enum class ConstSourcePayload { Copy, Stay }; +enum class MutableSourcePayload { Move }; + +struct RowBaseHandoverPatch; +struct TableViewHandoverPatch; + +struct TableHandoverPatch { + bool m_is_sub_table; + size_t m_table_num; + size_t m_col_ndx; + size_t m_row_ndx; +}; + +struct LinkViewHandoverPatch { + std::unique_ptr m_table; + size_t m_col_num; + size_t m_row_ndx; +}; + +// Base class for handover patches for query nodes. Subclasses are declared in query_engine.hpp. +struct QueryNodeHandoverPatch { + virtual ~QueryNodeHandoverPatch() = default; +}; + +using QueryNodeHandoverPatches = std::vector>; + +struct QueryHandoverPatch { + std::unique_ptr m_table; + std::unique_ptr table_view_data; + std::unique_ptr link_view_data; + QueryNodeHandoverPatches m_node_data; +}; + +struct SortDescriptorHandoverPatch { + std::vector> columns; + std::vector ascending; +}; + +struct TableViewHandoverPatch { + std::unique_ptr m_table; + std::unique_ptr linked_row; + size_t linked_col; + bool was_in_sync; + QueryHandoverPatch query_patch; + std::unique_ptr linkview_patch; + std::unique_ptr sort_patch; + std::unique_ptr distinct_patch; +}; + + +struct RowBaseHandoverPatch { + std::unique_ptr m_table; + size_t row_ndx; +}; + + +} // end namespace Realm + +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/history.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/history.hpp new file mode 100644 index 0000000..9710d0b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/history.hpp @@ -0,0 +1,35 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_HISTORY_HPP +#define REALM_HISTORY_HPP + +#include +#include + +#include + + +namespace realm { + +std::unique_ptr make_in_realm_history(const std::string& realm_path); + +} // namespace realm + + +#endif // REALM_HISTORY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp new file mode 100644 index 0000000..f039ad5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp @@ -0,0 +1,44 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_WRITER_HPP +#define REALM_ARRAY_WRITER_HPP + +#include + +namespace realm { +namespace _impl { + +class ArrayWriterBase { +public: + virtual ~ArrayWriterBase() + { + } + + /// Write the specified array data and its checksum into free + /// space. + /// + /// Returns the ref (position in the target stream) of the written copy of + /// the specified array data. + virtual ref_type write_array(const char* data, size_t size, uint32_t checksum) = 0; +}; + +} // namespace impl_ +} // namespace realm + +#endif // REALM_ARRAY_WRITER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp new file mode 100644 index 0000000..e50e35e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp @@ -0,0 +1,153 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP +#define REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP + +#include +#include + +#include +#include + +namespace realm { + +class Group; + +namespace _impl { + +/// Read-only access to history of changesets as needed to enable continuous +/// transactions. +class History { +public: + using version_type = VersionID::version_type; + + /// May be called during a read transaction to gain early access to the + /// history as it appears in a new snapshot that succeeds the one bound in + /// the current read transaction. + /// + /// May also be called at other times as long as the caller owns a read lock + /// (SharedGroup::grab_read_lock()) on the Realm for the specified file size + /// and top ref, and the allocator is in a 'free space clean' state + /// (SlabAlloc::is_free_space_clean()). + /// + /// This function may cause a remapping of the Realm file + /// (SlabAlloc::remap()) if it needs to make the new snapshot fully visible + /// in memory. + /// + /// Note that this method of gaining early access to the history in a new + /// snaphot only gives read access. It does not allow for modifications of + /// the history or any other part of the new snapshot. For modifications to + /// be allowed, `Group::m_top` (the parent of the history) would first have + /// to be updated to reflect the new snapshot, but at that time we are no + /// longer in an 'early access' situation. + /// + /// This is not a problem from the point of view of this history interface, + /// as it only contains methods for reading from the history, but some + /// implementations will want to also provide for ways to modify the + /// history, but in those cases, modifications must occur only after the + /// Group accessor has been fully updated to reflect the new snapshot. + virtual void update_early_from_top_ref(version_type new_version, size_t new_file_size, ref_type new_top_ref) = 0; + + virtual void update_from_parent(version_type current_version) = 0; + + /// Get all changesets between the specified versions. References to those + /// changesets will be made availble in successive entries of `buffer`. The + /// number of retreived changesets is exactly `end_version - + /// begin_version`. If this number is greater than zero, the changeset made + /// avaialable in `buffer[0]` is the one that brought the database from + /// `begin_version` to `begin_version + 1`. + /// + /// It is an error to specify a version (for \a begin_version or \a + /// end_version) that is outside the range [V,W] where V is the version that + /// immediately precedes the first changeset available in the history as the + /// history appears in the **latest** available snapshot, and W is the + /// versionm that immediately succeeds the last changeset available in the + /// history as the history appears in the snapshot bound to the **current** + /// transaction. This restriction is necessary to allow for different kinds + /// of implementations of the history (separate standalone history or + /// history as part of versioned Realm state). + /// + /// The calee retains ownership of the memory referenced by those entries, + /// i.e., the memory referenced by `buffer[i].changeset` is **not** handed + /// over to the caller. + /// + /// This function may be called only during a transaction (prior to + /// initiation of commit operation), and only after a successfull invocation + /// of update_early_from_top_ref(). In that case, the caller may assume that + /// the memory references stay valid for the remainder of the transaction + /// (up until initiation of the commit operation). + virtual void get_changesets(version_type begin_version, version_type end_version, BinaryIterator* buffer) const + noexcept = 0; + + /// \brief Specify the version of the oldest bound snapshot. + /// + /// This function must be called by the associated SharedGroup object during + /// each successfully committed write transaction. It must be called before + /// the transaction is finalized (Replication::finalize_commit()) or aborted + /// (Replication::abort_transact()), but after the initiation of the commit + /// operation (Replication::prepare_commit()). This allows history + /// implementations to add new history entries before triming off old ones, + /// and this, in turn, guarantees that the history never becomes empty, + /// except in the initial empty Realm state. + /// + /// The caller must pass the version (\a version) of the oldest snapshot + /// that is currently (or was recently) bound via a transaction of the + /// current session. This gives the history implementation an opportunity to + /// trim off leading (early) history entries. + /// + /// Since this function must be called during a write transaction, there + /// will always be at least one snapshot that is currently bound via a + /// transaction. + /// + /// The caller must guarantee that the passed version (\a version) is less + /// than or equal to `begin_version` in all future invocations of + /// get_changesets(). + /// + /// The caller is allowed to pass a version that is less than the version + /// passed in a preceeding invocation. + /// + /// This function should be called as late as possible, to maximize the + /// trimming opportunity, but at a time where the write transaction is still + /// open for additional modifications. This is necessary because some types + /// of histories are stored inside the Realm file. + virtual void set_oldest_bound_version(version_type version) = 0; + + /// Get the list of uncommited changes accumulated so far in the current + /// write transaction. + /// + /// The callee retains ownership of the referenced memory. The ownership is + /// not handed over the the caller. + /// + /// This function may be called only during a write transaction (prior to + /// initiation of commit operation). In that case, the caller may assume that the + /// returned memory reference stays valid for the remainder of the transaction (up + /// until initiation of the commit operation). + virtual BinaryData get_uncommitted_changes() noexcept = 0; + + virtual void verify() const = 0; + + virtual ~History() noexcept + { + } +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp new file mode 100644 index 0000000..f5b5e37 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp @@ -0,0 +1,237 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_DESTROY_GUARD_HPP +#define REALM_IMPL_DESTROY_GUARD_HPP + +#include +#include + +namespace realm { +namespace _impl { + + +/// Calls `ptr->destroy()` if the guarded pointer (`ptr`) is not null +/// when the guard is destroyed. For arrays (`T` = `Array`) this means +/// that the array is destroyed in a shallow fashion. See +/// `DeepArrayDestroyGuard` for an alternative. +template +class DestroyGuard { +public: + DestroyGuard() noexcept; + + DestroyGuard(T*) noexcept; + + ~DestroyGuard() noexcept; + + // Default implementations of copy/assign can trigger multiple destructions + DestroyGuard(const DestroyGuard&) = delete; + DestroyGuard& operator=(const DestroyGuard&) = delete; + + void reset(T*) noexcept; + + T* get() const noexcept; + + T* release() noexcept; + +private: + T* m_ptr; +}; + +using ShallowArrayDestroyGuard = DestroyGuard; + + +/// Calls `ptr->destroy_deep()` if the guarded Array pointer (`ptr`) +/// is not null when the guard is destroyed. +class DeepArrayDestroyGuard { +public: + DeepArrayDestroyGuard() noexcept; + + DeepArrayDestroyGuard(Array*) noexcept; + + ~DeepArrayDestroyGuard() noexcept; + + // Default implementations of copy/assign can trigger multiple destructions + DeepArrayDestroyGuard(const DeepArrayDestroyGuard&) = delete; + DeepArrayDestroyGuard& operator=(const DeepArrayDestroyGuard&) = delete; + + void reset(Array*) noexcept; + + Array* get() const noexcept; + + Array* release() noexcept; + +private: + Array* m_ptr; +}; + + +/// Calls `Array::destroy_deep(ref, alloc)` if the guarded 'ref' +/// (`ref`) is not zero when the guard is destroyed. +class DeepArrayRefDestroyGuard { +public: + DeepArrayRefDestroyGuard(Allocator&) noexcept; + + DeepArrayRefDestroyGuard(ref_type, Allocator&) noexcept; + + ~DeepArrayRefDestroyGuard() noexcept; + + // Default implementations of copy/assign can trigger multiple destructions + DeepArrayRefDestroyGuard(const DeepArrayRefDestroyGuard&) = delete; + DeepArrayRefDestroyGuard& operator=(const DeepArrayRefDestroyGuard&) = delete; + + void reset(ref_type) noexcept; + + ref_type get() const noexcept; + + ref_type release() noexcept; + +private: + ref_type m_ref; + Allocator& m_alloc; +}; + + +// Implementation: + +// DestroyGuard + +template +inline DestroyGuard::DestroyGuard() noexcept + : m_ptr(nullptr) +{ +} + +template +inline DestroyGuard::DestroyGuard(T* ptr) noexcept + : m_ptr(ptr) +{ +} + +template +inline DestroyGuard::~DestroyGuard() noexcept +{ + if (m_ptr) + m_ptr->destroy(); +} + +template +inline void DestroyGuard::reset(T* ptr) noexcept +{ + if (m_ptr) + m_ptr->destroy(); + m_ptr = ptr; +} + +template +inline T* DestroyGuard::get() const noexcept +{ + return m_ptr; +} + +template +inline T* DestroyGuard::release() noexcept +{ + T* ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + + +// DeepArrayDestroyGuard + +inline DeepArrayDestroyGuard::DeepArrayDestroyGuard() noexcept + : m_ptr(nullptr) +{ +} + +inline DeepArrayDestroyGuard::DeepArrayDestroyGuard(Array* ptr) noexcept + : m_ptr(ptr) +{ +} + +inline DeepArrayDestroyGuard::~DeepArrayDestroyGuard() noexcept +{ + if (m_ptr) + m_ptr->destroy_deep(); +} + +inline void DeepArrayDestroyGuard::reset(Array* ptr) noexcept +{ + if (m_ptr) + m_ptr->destroy_deep(); + m_ptr = ptr; +} + +inline Array* DeepArrayDestroyGuard::get() const noexcept +{ + return m_ptr; +} + +inline Array* DeepArrayDestroyGuard::release() noexcept +{ + Array* ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + + +// DeepArrayRefDestroyGuard + +inline DeepArrayRefDestroyGuard::DeepArrayRefDestroyGuard(Allocator& alloc) noexcept + : m_ref(0) + , m_alloc(alloc) +{ +} + +inline DeepArrayRefDestroyGuard::DeepArrayRefDestroyGuard(ref_type ref, Allocator& alloc) noexcept + : m_ref(ref) + , m_alloc(alloc) +{ +} + +inline DeepArrayRefDestroyGuard::~DeepArrayRefDestroyGuard() noexcept +{ + if (m_ref) + Array::destroy_deep(m_ref, m_alloc); +} + +inline void DeepArrayRefDestroyGuard::reset(ref_type ref) noexcept +{ + if (m_ref) + Array::destroy_deep(m_ref, m_alloc); + m_ref = ref; +} + +inline ref_type DeepArrayRefDestroyGuard::get() const noexcept +{ + return m_ref; +} + +inline ref_type DeepArrayRefDestroyGuard::release() noexcept +{ + ref_type ref = m_ref; + m_ref = 0; + return ref; +} + + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_DESTROY_GUARD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp new file mode 100644 index 0000000..db04b9c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp @@ -0,0 +1,255 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_INPUT_STREAM_HPP +#define REALM_IMPL_INPUT_STREAM_HPP + +#include + +#include +#include +#include + + +namespace realm { +namespace _impl { + + +class InputStream { +public: + /// Read bytes from this input stream and place them in the specified + /// buffer. The returned value is the actual number of bytes that were read, + /// and this is some number `n` such that `n <= min(size, m)` where `m` is + /// the number of bytes that could have been read from this stream before + /// reaching its end. Also, `n` cannot be zero unless `m` or `size` is + /// zero. The intention is that `size` should be non-zero, a the return + /// value used as the end-of-input indicator. + /// + /// Implementations are only allowed to block (put the calling thread to + /// sleep) up until the point in time where the first byte can be made + /// availble. + virtual size_t read(char* buffer, size_t size) = 0; + + virtual ~InputStream() noexcept + { + } +}; + + +class SimpleInputStream : public InputStream { +public: + SimpleInputStream(const char* data, size_t size) noexcept + : m_ptr(data) + , m_end(data + size) + { + } + size_t read(char* buffer, size_t size) override + { + size_t n = std::min(size, size_t(m_end - m_ptr)); + const char* begin = m_ptr; + m_ptr += n; + realm::safe_copy_n(begin, n, buffer); + return n; + } + +private: + const char* m_ptr; + const char* const m_end; +}; + + +class NoCopyInputStream { +public: + /// \return if any bytes was read. + /// A value of false indicates end-of-input. + /// If return value is true, \a begin and \a end are + /// updated to reflect the start and limit of a + /// contiguous memory chunk. + virtual bool next_block(const char*& begin, const char*& end) = 0; + + virtual ~NoCopyInputStream() noexcept + { + } +}; + + +class NoCopyInputStreamAdaptor : public NoCopyInputStream { +public: + NoCopyInputStreamAdaptor(InputStream& in, char* buffer, size_t buffer_size) noexcept + : m_in(in) + , m_buffer(buffer) + , m_buffer_size(buffer_size) + { + } + bool next_block(const char*& begin, const char*& end) override + { + size_t n = m_in.read(m_buffer, m_buffer_size); + begin = m_buffer; + end = m_buffer + n; + return n; + } + +private: + InputStream& m_in; + char* m_buffer; + size_t m_buffer_size; +}; + + +class SimpleNoCopyInputStream : public NoCopyInputStream { +public: + SimpleNoCopyInputStream(const char* data, size_t size) + : m_data(data) + , m_size(size) + { + } + + bool next_block(const char*& begin, const char*& end) override + { + if (m_size == 0) + return 0; + size_t size = m_size; + begin = m_data; + end = m_data + size; + m_size = 0; + return size; + } + +private: + const char* m_data; + size_t m_size; +}; + +class MultiLogNoCopyInputStream : public NoCopyInputStream { +public: + MultiLogNoCopyInputStream(const BinaryData* logs_begin, const BinaryData* logs_end) + : m_logs_begin(logs_begin) + , m_logs_end(logs_end) + { + if (m_logs_begin != m_logs_end) + m_curr_buf_remaining_size = m_logs_begin->size(); + } + + size_t read(char* buffer, size_t size) + { + if (m_logs_begin == m_logs_end) + return 0; + for (;;) { + if (m_curr_buf_remaining_size > 0) { + size_t offset = m_logs_begin->size() - m_curr_buf_remaining_size; + const char* data = m_logs_begin->data() + offset; + size_t size_2 = std::min(m_curr_buf_remaining_size, size); + m_curr_buf_remaining_size -= size_2; + // FIXME: Eliminate the need for copying by changing the API of + // Replication::InputStream such that blocks can be handed over + // without copying. This is a straight forward change, but the + // result is going to be more complicated and less conventional. + realm::safe_copy_n(data, size_2, buffer); + return size_2; + } + + ++m_logs_begin; + if (m_logs_begin == m_logs_end) + return 0; + m_curr_buf_remaining_size = m_logs_begin->size(); + } + } + + bool next_block(const char*& begin, const char*& end) override + { + while (m_logs_begin < m_logs_end) { + size_t result = m_logs_begin->size(); + const char* data = m_logs_begin->data(); + m_logs_begin++; + if (result == 0) + continue; // skip empty blocks + begin = data; + end = data + result; + return result; + } + return 0; + } + +private: + const BinaryData* m_logs_begin; + const BinaryData* m_logs_end; + size_t m_curr_buf_remaining_size; +}; + + +class ChangesetInputStream : public NoCopyInputStream { +public: + using version_type = History::version_type; + static constexpr unsigned NB_BUFFERS = 8; + + ChangesetInputStream(History& hist, version_type begin_version, version_type end_version) + : m_history(hist) + , m_begin_version(begin_version) + , m_end_version(end_version) + { + get_changeset(); + } + + bool next_block(const char*& begin, const char*& end) override + { + while (m_valid) { + BinaryData actual = m_changesets_begin->get_next(); + + if (actual.size() > 0) { + begin = actual.data(); + end = actual.data() + actual.size(); + return true; + } + + m_changesets_begin++; + + if (REALM_UNLIKELY(m_changesets_begin == m_changesets_end)) { + get_changeset(); + } + } + return false; // End of input + } + +private: + History& m_history; + version_type m_begin_version, m_end_version; + BinaryIterator m_changesets[NB_BUFFERS]; // Buffer + BinaryIterator* m_changesets_begin = nullptr; + BinaryIterator* m_changesets_end = nullptr; + bool m_valid; + + void get_changeset() + { + auto versions_to_get = m_end_version - m_begin_version; + m_valid = versions_to_get > 0; + if (m_valid) { + if (versions_to_get > NB_BUFFERS) + versions_to_get = NB_BUFFERS; + version_type end_version = m_begin_version + versions_to_get; + m_history.get_changesets(m_begin_version, end_version, m_changesets); + m_begin_version = end_version; + m_changesets_begin = m_changesets; + m_changesets_end = m_changesets_begin + versions_to_get; + } + } +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INPUT_STREAM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/instructions.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/instructions.hpp new file mode 100644 index 0000000..9f54d81 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/instructions.hpp @@ -0,0 +1,380 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_INSTRUCTIONS_HPP +#define REALM_IMPL_INSTRUCTIONS_HPP + +#include // size_t +#include + +#include +#include +#include +#include +#include + +namespace realm { +namespace _impl { + +class TransactLogParser; +class TransactLogEncoder; +class InputStream; + +#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \ + X(SelectTable) \ + X(SelectDescriptor) \ + X(SelectLinkList) \ + X(InsertGroupLevelTable) \ + X(EraseGroupLevelTable) \ + X(RenameGroupLevelTable) \ + X(MoveGroupLevelTable) \ + X(InsertEmptyRows) \ + X(Remove) \ + X(MoveLastOver) \ + X(Swap) \ + X(MergeRows) \ + X(Set) \ + X(SetDefault) \ + X(SetUnique) \ + X(AddInteger) \ + X(InsertSubstring) \ + X(EraseSubstring) \ + X(ClearTable) \ + X(OptimizeTable) \ + X(InsertColumn) \ + X(EraseColumn) \ + X(RenameColumn) \ + X(MoveColumn) \ + X(AddSearchIndex) \ + X(RemoveSearchIndex) \ + X(SetLinkType) \ + X(LinkListSet) \ + X(LinkListInsert) \ + X(LinkListMove) \ + X(LinkListSwap) \ + X(LinkListErase) \ + X(LinkListClear) \ + + +enum class InstrType { +#define REALM_DEFINE_INSTRUCTION_TYPE(X) X, +REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE) +#undef REALM_DEFINE_INSTRUCTION_TYPE +}; + +struct StringBufferRange { + size_t offset, size; +}; + + +// Note: All specializations must be "POD", so that they can be +// part of a union. +template struct Instr; + +template <> struct Instr { + size_t group_level_ndx; + size_t num_pairs; + size_t pairs[2]; // FIXME: max 1 level of subtables +}; + +template <> struct Instr { + size_t num_pairs; + size_t pairs[2]; // FIXME: max 1 level of subtables +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + size_t link_target_group_level_ndx; +}; + +template <> struct Instr { + Instr() {} + size_t table_ndx; + size_t num_tables; + StringBufferRange name; +}; + +template <> struct Instr { + size_t table_ndx; + size_t num_tables; +}; + +template <> struct Instr { + size_t table_ndx; + StringBufferRange new_name; +}; + +template <> struct Instr { + size_t table_ndx_1; + size_t table_ndx_2; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_insert; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_erase; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_erase; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx_1; + size_t row_ndx_2; +}; + +template <> struct Instr { + size_t row_ndx; + size_t new_row_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + + struct Payload { + DataType type; + + struct LinkPayload { + size_t target_row; // npos means null + size_t target_group_level_ndx; + bool implicit_nullify; + }; + + union PayloadData { + bool boolean; + int64_t integer; + float fnum; + double dnum; + StringBufferRange str; + Timestamp timestamp; + LinkPayload link; + + PayloadData() {} + PayloadData(const PayloadData&) = default; + PayloadData& operator=(const PayloadData&) = default; + }; + PayloadData data; + + bool is_null() const; + }; + + Payload payload; +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + int64_t value; +}; + +template <> struct Instr : Instr { +}; + +template <> struct Instr : Instr { + size_t prior_num_rows; +}; + +template<> struct Instr { + Instr() {} + size_t col_ndx; + size_t row_ndx; + size_t pos; + StringBufferRange value; +}; + +template<> struct Instr { + Instr() {} + size_t col_ndx; + size_t row_ndx; + size_t pos; + size_t size; +}; + +template <> struct Instr { +}; + +template <> struct Instr { +}; + +template <> struct Instr { + size_t link_ndx; + size_t value; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx; + size_t value; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx_1; + size_t link_ndx_2; +}; + +template <> struct Instr { + size_t link_ndx; + bool implicit_nullify; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx_1; + size_t link_ndx_2; +}; + +template <> struct Instr { + size_t num_links; +}; + +template <> struct Instr { + size_t col_ndx; + DataType type; + StringBufferRange name; + size_t link_target_table_ndx; + size_t backlink_col_ndx; + bool nullable; +}; + +template <> struct Instr { + size_t col_ndx; + size_t link_target_table_ndx; + size_t backlink_col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + StringBufferRange new_name; +}; + +template <> struct Instr { + size_t col_ndx_1; + size_t col_ndx_2; +}; + +template <> struct Instr { + size_t col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + LinkType type; +}; + +struct AnyInstruction { + using Type = InstrType; + + AnyInstruction() {} + template + AnyInstruction(Instr instr): type(t) + { + get_as() = std::move(instr); + } + + InstrType type; + union InstrUnion { +#define REALM_DEFINE_INSTRUCTION_MEMBER(X) Instr m_ ## X; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_MEMBER) +#undef REALM_DEFINE_INSTRUCTION_MEMBER + + InstrUnion() { +#if defined(REALM_DEBUG) + char* mem = reinterpret_cast(this); + std::fill(mem, mem + sizeof(*this), 0xef); +#endif // REALM_DEBUG + } + + InstrUnion(const InstrUnion&) = default; + InstrUnion& operator=(const InstrUnion&) = default; + }; + InstrUnion instr; + + template + void visit(F lambda); + template + void visit(F lambda) const; + + template Instr& get_as(); + + template + const Instr& get_as() const + { + return const_cast(this)->template get_as(); + } +}; + +#define REALM_DEFINE_GETTER(X) \ + template<> inline Instr& AnyInstruction::get_as() \ + { \ + return instr.m_ ## X; \ + } + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_GETTER) +#undef REALM_DEFINE_GETTER + +using InstructionList = std::vector; // FIXME: Consider using std::deque + +InstructionList parse_changeset_as_instructions(_impl::TransactLogParser&, _impl::InputStream&, + util::StringBuffer&); +void encode_instructions_as_changeset(const InstructionList&, const util::StringBuffer&, + _impl::TransactLogEncoder&); + + + + +/// Implementation: + +template +void AnyInstruction::visit(F lambda) +{ + switch (type) { +#define REALM_VISIT_INSTRUCTION(X) \ + case InstrType::X: return lambda(get_as()); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) +#undef REALM_VISIT_INSTRUCTION + } + REALM_UNREACHABLE(); +} + +template +void AnyInstruction::visit(F lambda) const +{ + const_cast(this)->visit(lambda); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INSTRUCTIONS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp new file mode 100644 index 0000000..1d022cd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp @@ -0,0 +1,75 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_OUTPUT_STREAM_HPP +#define REALM_IMPL_OUTPUT_STREAM_HPP + +#include +#include + +#include + +#include + +#include + +namespace realm { +namespace _impl { + + +class OutputStream : public ArrayWriterBase { +public: + OutputStream(std::ostream&); + ~OutputStream() noexcept; + + ref_type get_ref_of_next_array() const noexcept; + + void write(const char* data, size_t size); + + ref_type write_array(const char* data, size_t size, uint32_t checksum) override; + +private: + ref_type m_next_ref; + std::ostream& m_out; + + void do_write(const char* data, size_t size); +}; + + +// Implementation: + +inline OutputStream::OutputStream(std::ostream& out) + : m_next_ref(0) + , m_out(out) +{ +} + +inline OutputStream::~OutputStream() noexcept +{ +} + +inline size_t OutputStream::get_ref_of_next_array() const noexcept +{ + return m_next_ref; +} + + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_OUTPUT_STREAM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp new file mode 100644 index 0000000..c7dd866 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp @@ -0,0 +1,130 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_SEQUENTIAL_GETTER_HPP +#define REALM_IMPL_SEQUENTIAL_GETTER_HPP + +namespace realm { + +class SequentialGetterBase { +public: + virtual ~SequentialGetterBase() noexcept + { + } +}; + +template +class SequentialGetter : public SequentialGetterBase { +public: + using T = typename ColType::value_type; + using ArrayType = typename ColType::LeafType; + + SequentialGetter() + { + } + + SequentialGetter(const Table& table, size_t column_ndx) + { + init(static_cast(&table.get_column_base(column_ndx))); + } + + SequentialGetter(const ColType* column) + { + init(column); + } + + ~SequentialGetter() noexcept override + { + } + + void init(const ColType* column) + { + REALM_ASSERT(column != nullptr); + m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr.reset(new (&m_leaf_accessor_storage) ArrayType(column->get_alloc())); + m_column = column; + m_leaf_end = 0; + } + + REALM_FORCEINLINE bool cache_next(size_t index) + { + // Set m_leaf_ptr to point at the leaf that contains the value at column row `index`. Return whether or not + // the leaf has changed (could be useful to know for caller). + + // FIXME: Below line has been commented away because array leafs might relocate during the lifetime of the + // object that owns this SequentialGetter. Enable again when we have proper support for that. + // if (index >= m_leaf_end || index < m_leaf_start) + { + typename ColType::LeafInfo leaf{&m_leaf_ptr, m_array_ptr.get()}; + size_t ndx_in_leaf; + m_column->get_leaf(index, ndx_in_leaf, leaf); + m_leaf_start = index - ndx_in_leaf; + const size_t leaf_size = m_leaf_ptr->size(); + m_leaf_end = m_leaf_start + leaf_size; + return true; + } + return false; + } + + + REALM_FORCEINLINE T get_next(size_t index) + { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4800) // Disable the Microsoft warning about bool performance issue. +#endif + return m_column->get(index); + + // FIXME: Below optimization is skipped because array leafs might relocate during the lifetime of the + // object that owns this SequentialGetter. Enable again when we have proper support for that. +// +// cache_next(index); +// T av = m_leaf_ptr->get(index - m_leaf_start); +// return av; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + + size_t local_end(size_t global_end) + { + if (global_end > m_leaf_end) + return m_leaf_end - m_leaf_start; + else + return global_end - m_leaf_start; + } + + size_t m_leaf_start = 0; + size_t m_leaf_end = 0; + const ColType* m_column = nullptr; + + const ArrayType* m_leaf_ptr = nullptr; + +private: + // Leaf cache for when the root of the column is not a leaf. + // This dog and pony show is because Array has a reference to Allocator internally, + // but we need to be able to transfer queries between contexts, so init() reinitializes + // the leaf cache in the context of the current column. + typename std::aligned_storage::type m_leaf_accessor_storage; + std::unique_ptr m_array_ptr; +}; + +} // namespace realm + +#endif // REALM_IMPL_SEQUENTIAL_GETTER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp new file mode 100644 index 0000000..4958151 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp @@ -0,0 +1,228 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_SIMULATED_FAILURE_HPP +#define REALM_IMPL_SIMULATED_FAILURE_HPP + +#include +#include + +#include + +#ifdef REALM_DEBUG +#define REALM_ENABLE_SIMULATED_FAILURE +#endif + +namespace realm { +namespace _impl { + +class SimulatedFailure : public std::system_error { +public: + enum FailureType { + generic, + slab_alloc__reset_free_space_tracking, + slab_alloc__remap, + shared_group__grow_reader_mapping, + sync_client__read_head, + sync_server__read_head, + _num_failure_types + }; + + class OneShotPrimeGuard; + class RandomPrimeGuard; + + /// Prime the specified failure type on the calling thread for triggering + /// once. + static void prime_one_shot(FailureType); + + /// Prime the specified failure type on the calling thread for triggering + /// randomly \a n out of \a m times. + static void prime_random(FailureType, int n, int m, uint_fast64_t seed = 0); + + /// Unprime the specified failure type on the calling thread. + static void unprime(FailureType) noexcept; + + /// Returns true according to the mode of priming of the specified failure + /// type on the calling thread, but only if REALM_ENABLE_SIMULATED_FAILURE + /// was defined during compilation. If REALM_ENABLE_SIMULATED_FAILURE was + /// not defined, this function always return false. + static bool check_trigger(FailureType) noexcept; + + /// The specified error code is set to `make_error_code(failure_type)` if + /// check_trigger() returns true. Otherwise it is set to + /// `std::error_code()`. Returns a copy of the updated error code. + static std::error_code trigger(FailureType failure_type, std::error_code&) noexcept; + + /// Throws SimulatedFailure if check_trigger() returns true. The exception + /// will be constructed with an error code equal to + /// `make_error_code(failure_type)`. + static void trigger(FailureType failure_type); + + /// Returns true when, and only when REALM_ENABLE_SIMULATED_FAILURE was + /// defined during compilation. + static constexpr bool is_enabled(); + + SimulatedFailure(std::error_code); + +private: +#ifdef REALM_ENABLE_SIMULATED_FAILURE + static void do_prime_one_shot(FailureType); + static void do_prime_random(FailureType, int n, int m, uint_fast64_t seed); + static void do_unprime(FailureType) noexcept; + static bool do_check_trigger(FailureType) noexcept; +#endif +}; + +std::error_code make_error_code(SimulatedFailure::FailureType) noexcept; + +class SimulatedFailure::OneShotPrimeGuard { +public: + OneShotPrimeGuard(FailureType); + ~OneShotPrimeGuard() noexcept; + +private: + const FailureType m_type; +}; + + +class SimulatedFailure::RandomPrimeGuard { +public: + RandomPrimeGuard(FailureType, int n, int m, uint_fast64_t seed = 0); + ~RandomPrimeGuard() noexcept; + +private: + const FailureType m_type; +}; + +std::error_code make_error_code(SimulatedFailure::FailureType) noexcept; + +} // namespace _impl +} // namespace realm + +namespace std { + +template<> struct is_error_code_enum { + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace _impl { + + +// Implementation + +inline void SimulatedFailure::prime_one_shot(FailureType failure_type) +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_prime_one_shot(failure_type); +#else + static_cast(failure_type); +#endif +} + +inline void SimulatedFailure::prime_random(FailureType failure_type, int n, int m, uint_fast64_t seed) +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_prime_random(failure_type, n, m, seed); +#else + static_cast(failure_type); + static_cast(n); + static_cast(m); + static_cast(seed); +#endif +} + +inline void SimulatedFailure::unprime(FailureType failure_type) noexcept +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_unprime(failure_type); +#else + static_cast(failure_type); +#endif +} + +inline bool SimulatedFailure::check_trigger(FailureType failure_type) noexcept +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + return do_check_trigger(failure_type); +#else + static_cast(failure_type); + return false; +#endif +} + +inline std::error_code SimulatedFailure::trigger(FailureType failure_type, std::error_code& ec) noexcept +{ + if (check_trigger(failure_type)) { + ec = make_error_code(failure_type); + } + else { + ec = std::error_code(); + } + return ec; +} + +inline void SimulatedFailure::trigger(FailureType failure_type) +{ + if (check_trigger(failure_type)) + throw SimulatedFailure(make_error_code(failure_type)); +} + +inline constexpr bool SimulatedFailure::is_enabled() +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + return true; +#else + return false; +#endif +} + +inline SimulatedFailure::SimulatedFailure(std::error_code ec) + : std::system_error(ec) +{ +} + +inline SimulatedFailure::OneShotPrimeGuard::OneShotPrimeGuard(FailureType failure_type) + : m_type(failure_type) +{ + prime_one_shot(m_type); +} + +inline SimulatedFailure::OneShotPrimeGuard::~OneShotPrimeGuard() noexcept +{ + unprime(m_type); +} + +inline SimulatedFailure::RandomPrimeGuard::RandomPrimeGuard(FailureType failure_type, int n, int m, + uint_fast64_t seed) + : m_type(failure_type) +{ + prime_random(m_type, n, m, seed); +} + +inline SimulatedFailure::RandomPrimeGuard::~RandomPrimeGuard() noexcept +{ + unprime(m_type); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_SIMULATED_FAILURE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/table_path.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/table_path.hpp new file mode 100644 index 0000000..c03b6cb --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/table_path.hpp @@ -0,0 +1,88 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_TABLE_PATH_HPP +#define REALM_IMPL_TABLE_PATH_HPP + +#include +#include +#include + +namespace realm { +namespace _impl { + +class TablePath { +public: + TablePath() + { + } + + TablePath(const size_t* begin, size_t len) : m_coords(begin, begin + len) + { + } + + TablePath(size_t group_level_ndx, size_t num_pairs, const size_t* pairs) + { + m_coords.reserve(num_pairs * 2 + 1); + m_coords.push_back(group_level_ndx); + std::copy(pairs, pairs + num_pairs * 2, std::back_inserter(m_coords)); + } + + bool operator==(const TablePath& other) const + { + if (m_coords.size() != other.m_coords.size()) { + return false; + } + for (size_t i = 0; i < m_coords.size(); ++i) { + if (m_coords[i] != other.m_coords[i]) { + return false; + } + } + return true; + } + + bool operator!=(const TablePath& other) const + { + return !((*this) == other); + } + + void push(size_t coord) + { + m_coords.push_back(coord); + } + + size_t size() const + { + return m_coords.size(); + } + + void clear() + { + m_coords.clear(); + } + + // FIXME: Should be private, but is accessed directly from sync.cpp. + std::vector m_coords; +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_TABLE_PATH_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp new file mode 100644 index 0000000..d5a0fa8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp @@ -0,0 +1,2787 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPL_TRANSACT_LOG_HPP +#define REALM_IMPL_TRANSACT_LOG_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace realm { +namespace _impl { + +/// Transaction log instruction encoding +/// NOTE: Any change to this enum is a file-format breaking change. +enum Instruction { + instr_InsertGroupLevelTable = 1, + instr_EraseGroupLevelTable = 2, // Remove columnless table from group + instr_RenameGroupLevelTable = 3, + instr_MoveGroupLevelTable = 4, + instr_SelectTable = 5, + instr_Set = 6, + instr_SetUnique = 7, + instr_SetDefault = 8, + instr_AddInteger = 9, // Add value to integer field + instr_NullifyLink = 10, // Set link to null due to target being erased + instr_InsertSubstring = 11, + instr_EraseFromString = 12, + instr_InsertEmptyRows = 13, + instr_EraseRows = 14, // Remove (multiple) rows + instr_SwapRows = 15, + instr_MergeRows = 16, // Replace links pointing to row A with links to row B + instr_ClearTable = 17, // Remove all rows in selected table + instr_OptimizeTable = 18, + instr_SelectDescriptor = 19, // Select descriptor from currently selected root table + instr_InsertColumn = + 20, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn) + instr_InsertLinkColumn = 21, // do, but for a link-type column + instr_InsertNullableColumn = 22, // Insert nullable column + instr_EraseColumn = 23, // Remove column from selected descriptor + instr_EraseLinkColumn = 24, // Remove link-type column from selected descriptor + instr_RenameColumn = 25, // Rename column in selected descriptor + instr_MoveColumn = 26, // Move column in selected descriptor + instr_AddSearchIndex = 27, // Add a search index to a column + instr_RemoveSearchIndex = 28, // Remove a search index from a column + instr_SetLinkType = 29, // Strong/weak + instr_SelectLinkList = 30, + instr_LinkListSet = 31, // Assign to link list entry + instr_LinkListInsert = 32, // Insert entry into link list + instr_LinkListMove = 33, // Move an entry within a link list + instr_LinkListSwap = 34, // Swap two entries within a link list + instr_LinkListErase = 35, // Remove an entry from a link list + instr_LinkListNullify = 36, // Remove an entry from a link list due to linked row being erased + instr_LinkListClear = 37, // Ramove all entries from a link list + instr_LinkListSetAll = 38, // Assign to link list entry +}; + +class TransactLogStream { +public: + /// Ensure contiguous free space in the transaction log + /// buffer. This method must update `out_free_begin` + /// and `out_free_end` such that they refer to a chunk + /// of free space whose size is at least \a n. + /// + /// \param n The required amount of contiguous free space. Must be + /// small (probably not greater than 1024) + /// \param n Must be small (probably not greater than 1024) + virtual void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) = 0; + + /// Copy the specified data into the transaction log buffer. This + /// function should be called only when the specified data does + /// not fit inside the chunk of free space currently referred to + /// by `out_free_begin` and `out_free_end`. + /// + /// This method must update `out_begin` and + /// `out_end` such that, upon return, they still + /// refer to a (possibly empty) chunk of free space. + virtual void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) = 0; +}; + +class TransactLogBufferStream : public TransactLogStream { +public: + void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) override; + void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) override; + + const char* transact_log_data() const; + + util::Buffer m_buffer; +}; + + +// LCOV_EXCL_START (because the NullInstructionObserver is trivial) +class NullInstructionObserver { +public: + /// The following methods are also those that TransactLogParser expects + /// to find on the `InstructionHandler`. + + // No selection needed: + bool select_table(size_t, size_t, const size_t*) + { + return true; + } + bool select_descriptor(size_t, const size_t*) + { + return true; + } + bool select_link_list(size_t, size_t, size_t) + { + return true; + } + bool insert_group_level_table(size_t, size_t, StringData) + { + return true; + } + bool erase_group_level_table(size_t, size_t) + { + return true; + } + bool rename_group_level_table(size_t, StringData) + { + return true; + } + bool move_group_level_table(size_t, size_t) + { + return true; + } + + // Must have table selected: + bool insert_empty_rows(size_t, size_t, size_t, bool) + { + return true; + } + bool erase_rows(size_t, size_t, size_t, bool) + { + return true; + } + bool swap_rows(size_t, size_t) + { + return true; + } + bool merge_rows(size_t, size_t) + { + return true; + } + bool clear_table() + { + return true; + } + bool set_int(size_t, size_t, int_fast64_t, Instruction, size_t) + { + return true; + } + bool add_int(size_t, size_t, int_fast64_t) + { + return true; + } + bool set_bool(size_t, size_t, bool, Instruction) + { + return true; + } + bool set_float(size_t, size_t, float, Instruction) + { + return true; + } + bool set_double(size_t, size_t, double, Instruction) + { + return true; + } + bool set_string(size_t, size_t, StringData, Instruction, size_t) + { + return true; + } + bool set_binary(size_t, size_t, BinaryData, Instruction) + { + return true; + } + bool set_olddatetime(size_t, size_t, OldDateTime, Instruction) + { + return true; + } + bool set_timestamp(size_t, size_t, Timestamp, Instruction) + { + return true; + } + bool set_table(size_t, size_t, Instruction) + { + return true; + } + bool set_mixed(size_t, size_t, const Mixed&, Instruction) + { + return true; + } + bool set_link(size_t, size_t, size_t, size_t, Instruction) + { + return true; + } + bool set_null(size_t, size_t, Instruction, size_t) + { + return true; + } + bool nullify_link(size_t, size_t, size_t) + { + return true; + } + bool insert_substring(size_t, size_t, size_t, StringData) + { + return true; + } + bool erase_substring(size_t, size_t, size_t, size_t) + { + return true; + } + bool optimize_table() + { + return true; + } + + // Must have descriptor selected: + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) + { + return true; + } + bool insert_column(size_t, DataType, StringData, bool) + { + return true; + } + bool erase_link_column(size_t, size_t, size_t) + { + return true; + } + bool erase_column(size_t) + { + return true; + } + bool rename_column(size_t, StringData) + { + return true; + } + bool move_column(size_t, size_t) + { + return true; + } + bool add_search_index(size_t) + { + return true; + } + bool remove_search_index(size_t) + { + return true; + } + bool set_link_type(size_t, LinkType) + { + return true; + } + + // Must have linklist selected: + bool link_list_set(size_t, size_t, size_t) + { + return true; + } + bool link_list_insert(size_t, size_t, size_t) + { + return true; + } + bool link_list_move(size_t, size_t) + { + return true; + } + bool link_list_swap(size_t, size_t) + { + return true; + } + bool link_list_erase(size_t, size_t) + { + return true; + } + bool link_list_nullify(size_t, size_t) + { + return true; + } + bool link_list_clear(size_t) + { + return true; + } + + void parse_complete() + { + } +}; +// LCOV_EXCL_STOP (NullInstructionObserver) + + +/// See TransactLogConvenientEncoder for information about the meaning of the +/// arguments of each of the functions in this class. +class TransactLogEncoder { +public: + /// The following methods are also those that TransactLogParser expects + /// to find on the `InstructionHandler`. + + // No selection needed: + bool select_table(size_t group_level_ndx, size_t levels, const size_t* path); + bool select_descriptor(size_t levels, const size_t* path); + bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx); + bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); + bool erase_group_level_table(size_t table_ndx, size_t num_tables); + bool rename_group_level_table(size_t table_ndx, StringData new_name); + bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); + + /// Must have table selected. + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered); + bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered); + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2); + bool merge_rows(size_t row_ndx, size_t new_row_ndx); + bool clear_table(); + + bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t, Instruction = instr_Set, size_t = 0); + bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t); + bool set_bool(size_t col_ndx, size_t row_ndx, bool, Instruction = instr_Set); + bool set_float(size_t col_ndx, size_t row_ndx, float, Instruction = instr_Set); + bool set_double(size_t col_ndx, size_t row_ndx, double, Instruction = instr_Set); + bool set_string(size_t col_ndx, size_t row_ndx, StringData, Instruction = instr_Set, size_t = 0); + bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData, Instruction = instr_Set); + bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime, Instruction = instr_Set); + bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp, Instruction = instr_Set); + bool set_table(size_t col_ndx, size_t row_ndx, Instruction = instr_Set); + bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed&, Instruction = instr_Set); + bool set_link(size_t col_ndx, size_t row_ndx, size_t, size_t target_group_level_ndx, Instruction = instr_Set); + bool set_null(size_t col_ndx, size_t row_ndx, Instruction = instr_Set, size_t = 0); + bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx); + bool insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData); + bool erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size); + bool optimize_table(); + + // Must have descriptor selected: + bool insert_link_column(size_t col_ndx, DataType, StringData name, size_t link_target_table_ndx, + size_t backlink_col_ndx); + bool insert_column(size_t col_ndx, DataType, StringData name, bool nullable = false); + bool erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx); + bool erase_column(size_t col_ndx); + bool rename_column(size_t col_ndx, StringData new_name); + bool move_column(size_t col_ndx_1, size_t col_ndx_2); + bool add_search_index(size_t col_ndx); + bool remove_search_index(size_t col_ndx); + bool set_link_type(size_t col_ndx, LinkType); + + // Must have linklist selected: + bool link_list_set(size_t link_ndx, size_t value, size_t prior_size); + bool link_list_set_all(const IntegerColumn& values); + bool link_list_insert(size_t link_ndx, size_t value, size_t prior_size); + bool link_list_move(size_t from_link_ndx, size_t to_link_ndx); + bool link_list_swap(size_t link1_ndx, size_t link2_ndx); + bool link_list_erase(size_t link_ndx, size_t prior_size); + bool link_list_nullify(size_t link_ndx, size_t prior_size); + bool link_list_clear(size_t old_list_size); + + /// End of methods expected by parser. + + + TransactLogEncoder(TransactLogStream& out_stream); + void set_buffer(char* new_free_begin, char* new_free_end); + char* write_position() const + { + return m_transact_log_free_begin; + } + +private: + using IntegerList = std::tuple; + using UnsignedList = std::tuple; + + // Make sure this is in agreement with the actual integer encoding + // scheme (see encode_int()). + static constexpr int max_enc_bytes_per_int = 10; + static constexpr int max_enc_bytes_per_double = sizeof(double); + static constexpr int max_enc_bytes_per_num = + max_enc_bytes_per_int < max_enc_bytes_per_double ? max_enc_bytes_per_double : max_enc_bytes_per_int; +// Space is reserved in chunks to avoid excessive over allocation. +#ifdef REALM_DEBUG + static constexpr int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode +#else + static constexpr int max_numbers_per_chunk = 8; +#endif + + // This value is used in Set* instructions in place of the 'type' field in + // the stream to indicate that the value of the Set* instruction is NULL, + // which doesn't have a type. + static constexpr int set_null_sentinel() + { + return -1; + } + + TransactLogStream& m_stream; + + // These two delimit a contiguous region of free space in a + // transaction log buffer following the last written data. It may + // be empty. + char* m_transact_log_free_begin = nullptr; + char* m_transact_log_free_end = nullptr; + + char* reserve(size_t size); + /// \param ptr Must be in the range [m_transact_log_free_begin, m_transact_log_free_end] + void advance(char* ptr) noexcept; + + template + size_t max_size(T); + + size_t max_size_list() + { + return 0; + } + + template + size_t max_size_list(T val, Args... args) + { + return max_size(val) + max_size_list(args...); + } + + template + char* encode(char* ptr, T value); + + char* encode_list(char* ptr) + { + advance(ptr); + return ptr; + } + + template + char* encode_list(char* ptr, T value, Args... args) + { + return encode_list(encode(ptr, value), args...); + } + + template + void append_simple_instr(L... numbers); + + template + void append_mixed_instr(Instruction instr, const Mixed& value, L... numbers); + + template + static char* encode_int(char*, T value); + friend class TransactLogParser; +}; + +class TransactLogConvenientEncoder { +public: + virtual void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); + virtual void erase_group_level_table(size_t table_ndx, size_t num_tables); + virtual void rename_group_level_table(size_t table_ndx, StringData new_name); + virtual void move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); + virtual void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link, + bool nullable = false); + virtual void erase_column(const Descriptor&, size_t col_ndx); + virtual void rename_column(const Descriptor&, size_t col_ndx, StringData name); + virtual void move_column(const Descriptor&, size_t from, size_t to); + + virtual void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set); + virtual void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value); + virtual void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set); + virtual void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set); + virtual void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set); + virtual void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set); + virtual void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set); + virtual void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value, + Instruction variant = instr_Set); + virtual void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set); + virtual void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + virtual void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set); + virtual void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set); + virtual void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + virtual void set_link_list(const LinkView&, const IntegerColumn& values); + virtual void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData); + virtual void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size); + + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + virtual void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows); + + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + virtual void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool is_move_last_over); + + virtual void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2); + virtual void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx); + virtual void add_search_index(const Table*, size_t col_ndx); + virtual void remove_search_index(const Table*, size_t col_ndx); + virtual void set_link_type(const Table*, size_t col_ndx, LinkType); + virtual void clear_table(const Table*); + virtual void optimize_table(const Table*); + + virtual void link_list_set(const LinkView&, size_t link_ndx, size_t value); + virtual void link_list_insert(const LinkView&, size_t link_ndx, size_t value); + virtual void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx); + virtual void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2); + virtual void link_list_erase(const LinkView&, size_t link_ndx); + virtual void link_list_clear(const LinkView&); + + //@{ + + /// Implicit nullifications due to removal of target row. This is redundant + /// information from the point of view of replication, as the removal of the + /// target row will reproduce the implicit nullifications in the target + /// Realm anyway. The purpose of this instruction is to allow observers + /// (reactor pattern) to be explicitly notified about the implicit + /// nullifications. + + virtual void nullify_link(const Table*, size_t col_ndx, size_t ndx); + virtual void link_list_nullify(const LinkView&, size_t link_ndx); + + //@} + + void on_table_destroyed(const Table*) noexcept; + void on_spec_destroyed(const Spec*) noexcept; + void on_link_list_destroyed(const LinkView&) noexcept; + +protected: + TransactLogConvenientEncoder(TransactLogStream& encoder); + + void reset_selection_caches() noexcept; + void set_buffer(char* new_free_begin, char* new_free_end) + { + m_encoder.set_buffer(new_free_begin, new_free_end); + } + char* write_position() const + { + return m_encoder.write_position(); + } + +private: + TransactLogEncoder m_encoder; + // These are mutable because they are caches. + mutable util::Buffer m_subtab_path_buf; + mutable const Table* m_selected_table; + mutable const Spec* m_selected_spec; + // Has to be atomic to support concurrent reset when a linklist + // is unselected. This can happen on a different thread. In case + // of races, setting of a new value must win. + mutable std::atomic m_selected_link_list; + + void unselect_all() noexcept; + void select_table(const Table*); // unselects descriptor and link list + void select_desc(const Descriptor&); // unselects link list + void select_link_list(const LinkView&); // unselects descriptor + + void record_subtable_path(const Table&, size_t*& out_begin, size_t*& out_end); + void do_select_table(const Table*); + void do_select_desc(const Descriptor&); + void do_select_link_list(const LinkView&); + + friend class TransactReverser; +}; + + +class TransactLogParser { +public: + class BadTransactLog; // Exception + + TransactLogParser(); + ~TransactLogParser() noexcept; + + /// See `TransactLogEncoder` for a list of methods that the `InstructionHandler` must define. + /// parse() promises that the path passed by reference to + /// InstructionHandler::select_descriptor() will remain valid + /// during subsequent calls to all descriptor modifying functions. + template + void parse(InputStream&, InstructionHandler&); + + template + void parse(NoCopyInputStream&, InstructionHandler&); + +private: + util::Buffer m_input_buffer; + + // The input stream is assumed to consist of chunks of memory organised such that + // every instruction resides in a single chunk only. + NoCopyInputStream* m_input; + // pointer into transaction log, each instruction is parsed from m_input_begin and onwards. + // Each instruction are assumed to be contiguous in memory. + const char* m_input_begin; + // pointer to one past current instruction log chunk. If m_input_begin reaches m_input_end, + // a call to next_input_buffer will move m_input_begin and m_input_end to a new chunk of + // memory. Setting m_input_end to 0 disables this check, and is used if it is already known + // that all of the instructions are in memory. + const char* m_input_end; + util::StringBuffer m_string_buffer; + static const int m_max_levels = 1024; + util::Buffer m_path; + + REALM_NORETURN void parser_error() const; + + template + void parse_one(InstructionHandler&); + bool has_next() noexcept; + + template + T read_int(); + + void read_bytes(char* data, size_t size); + BinaryData read_buffer(util::StringBuffer&, size_t size); + + bool read_bool(); + float read_float(); + double read_double(); + + StringData read_string(util::StringBuffer&); + BinaryData read_binary(util::StringBuffer&); + Timestamp read_timestamp(); + void read_mixed(Mixed*); + + // Advance m_input_begin and m_input_end to reflect the next block of instructions + // Returns false if no more input was available + bool next_input_buffer(); + + // return true if input was available + bool read_char(char&); // throws + + bool is_valid_data_type(int type); + bool is_valid_link_type(int type); +}; + + +class TransactLogParser::BadTransactLog : public std::exception { +public: + const char* what() const noexcept override + { + return "Bad transaction log"; + } +}; + + +/// Implementation: + +inline void TransactLogBufferStream::transact_log_reserve(size_t n, char** inout_new_begin, char** out_new_end) +{ + char* data = m_buffer.data(); + REALM_ASSERT(*inout_new_begin >= data); + REALM_ASSERT(*inout_new_begin <= (data + m_buffer.size())); + size_t size = *inout_new_begin - data; + m_buffer.reserve_extra(size, n); + data = m_buffer.data(); // May have changed + *inout_new_begin = data + size; + *out_new_end = data + m_buffer.size(); +} + +inline void TransactLogBufferStream::transact_log_append(const char* data, size_t size, char** out_new_begin, + char** out_new_end) +{ + transact_log_reserve(size, out_new_begin, out_new_end); + *out_new_begin = realm::safe_copy_n(data, size, *out_new_begin); +} + +inline const char* TransactLogBufferStream::transact_log_data() const +{ + return m_buffer.data(); +} + +inline TransactLogEncoder::TransactLogEncoder(TransactLogStream& stream) + : m_stream(stream) +{ +} + +inline void TransactLogEncoder::set_buffer(char* free_begin, char* free_end) +{ + REALM_ASSERT(free_begin <= free_end); + m_transact_log_free_begin = free_begin; + m_transact_log_free_end = free_end; +} + +inline void TransactLogConvenientEncoder::reset_selection_caches() noexcept +{ + unselect_all(); +} + +inline char* TransactLogEncoder::reserve(size_t n) +{ + if (size_t(m_transact_log_free_end - m_transact_log_free_begin) < n) { + m_stream.transact_log_reserve(n, &m_transact_log_free_begin, &m_transact_log_free_end); + } + return m_transact_log_free_begin; +} + +inline void TransactLogEncoder::advance(char* ptr) noexcept +{ + REALM_ASSERT_DEBUG(m_transact_log_free_begin <= ptr); + REALM_ASSERT_DEBUG(ptr <= m_transact_log_free_end); + m_transact_log_free_begin = ptr; +} + + +// The integer encoding is platform independent. Also, it does not +// depend on the type of the specified integer. Integers of any type +// can be encoded as long as the specified buffer is large enough (see +// below). The decoding does not have to use the same type. Decoding +// will fail if, and only if the encoded value falls outside the range +// of the requested destination type. +// +// The encoding uses one or more bytes. It never uses more than 8 bits +// per byte. The last byte in the sequence is the first one that has +// its 8th bit set to zero. +// +// Consider a particular non-negative value V. Let W be the number of +// bits needed to encode V using the trivial binary encoding of +// integers. The total number of bytes produced is then +// ceil((W+1)/7). The first byte holds the 7 least significant bits of +// V. The last byte holds at most 6 bits of V including the most +// significant one. The value of the first bit of the last byte is +// always 2**((N-1)*7) where N is the total number of bytes. +// +// A negative value W is encoded by setting the sign bit to one and +// then encoding the positive result of -(W+1) as described above. The +// advantage of this representation is that it converts small negative +// values to small positive values which require a small number of +// bytes. This would not have been true for 2's complements +// representation, for example. The sign bit is always stored as the +// 7th bit of the last byte. +// +// value bits value + sign max bytes +// -------------------------------------------------- +// int8_t 7 8 2 +// uint8_t 8 9 2 +// int16_t 15 16 3 +// uint16_t 16 17 3 +// int32_t 31 32 5 +// uint32_t 32 33 5 +// int64_t 63 64 10 +// uint64_t 64 65 10 +// +template +char* TransactLogEncoder::encode_int(char* ptr, T value) +{ + static_assert(std::numeric_limits::is_integer, "Integer required"); + bool negative = util::is_negative(value); + if (negative) { + // The following conversion is guaranteed by C++11 to never + // overflow (contrast this with "-value" which indeed could + // overflow). See C99+TC3 section 6.2.6.2 paragraph 2. + REALM_DIAG_PUSH(); + REALM_DIAG_IGNORE_UNSIGNED_MINUS(); + value = -(value + 1); + REALM_DIAG_POP(); + } + // At this point 'value' is always a positive number. Also, small + // negative numbers have been converted to small positive numbers. + REALM_ASSERT(!util::is_negative(value)); + // One sign bit plus number of value bits + const int num_bits = 1 + std::numeric_limits::digits; + // Only the first 7 bits are available per byte. Had it not been + // for the fact that maximum guaranteed bit width of a char is 8, + // this value could have been increased to 15 (one less than the + // number of value bits in 'unsigned'). + const int bits_per_byte = 7; + const int max_bytes = (num_bits + (bits_per_byte - 1)) / bits_per_byte; + static_assert(max_bytes <= max_enc_bytes_per_int, "Bad max_enc_bytes_per_int"); + // An explicit constant maximum number of iterations is specified + // in the hope that it will help the optimizer (to do loop + // unrolling, for example). + typedef unsigned char uchar; + for (int i = 0; i < max_bytes; ++i) { + if (value >> (bits_per_byte - 1) == 0) + break; + *reinterpret_cast(ptr) = uchar((1U << bits_per_byte) | unsigned(value & ((1U << bits_per_byte) - 1))); + ++ptr; + value >>= bits_per_byte; + } + *reinterpret_cast(ptr) = uchar(negative ? (1U << (bits_per_byte - 1)) | unsigned(value) : value); + return ++ptr; +} + +template +char* TransactLogEncoder::encode(char* ptr, T value) +{ + auto value_2 = value + 0; // Perform integral promotion + return encode_int(ptr, value_2); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, char value) +{ + // Write the char as-is without encoding. + *ptr++ = value; + return ptr; +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, Instruction inst) +{ + return encode(ptr, inst); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, bool value) +{ + return encode(ptr, value); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, float value) +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(float) * std::numeric_limits::digits == 32, + "Unsupported 'float' representation"); + const char* val_ptr = reinterpret_cast(&value); + return realm::safe_copy_n(val_ptr, sizeof value, ptr); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, double value) +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(double) * std::numeric_limits::digits == 64, + "Unsupported 'double' representation"); + const char* val_ptr = reinterpret_cast(&value); + return realm::safe_copy_n(val_ptr, sizeof value, ptr); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, DataType type) +{ + return encode(ptr, type); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, StringData s) +{ + ptr = encode_int(ptr, s.size()); + return std::copy_n(s.data(), s.size(), ptr); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, + TransactLogEncoder::IntegerList list) +{ + IntegerColumnIterator i = std::get<0>(list); + IntegerColumnIterator end = std::get<1>(list); + + while (end - i > max_numbers_per_chunk) { + for (int j = 0; j < max_numbers_per_chunk; ++j) + ptr = encode_int(ptr, *i++); + advance(ptr); + size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk; + ptr = reserve(max_required_bytes_2); // Throws + } + + while (i != end) + ptr = encode_int(ptr, *i++); + + return ptr; +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, + TransactLogEncoder::UnsignedList list) +{ + const size_t* i = std::get<0>(list); + const size_t* end = std::get<1>(list); + + while (i != end) + ptr = encode_int(ptr, *i++); + + return ptr; +} + +template +size_t TransactLogEncoder::max_size(T) +{ + return max_enc_bytes_per_num; +} + +template <> +inline size_t TransactLogEncoder::max_size(char) +{ + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(bool) +{ + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(Instruction) +{ + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(DataType) +{ + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(StringData s) +{ + return max_enc_bytes_per_num + s.size(); +} + +template <> +inline size_t TransactLogEncoder::max_size(IntegerList) +{ + // We only allocate space for 'max_numbers_per_chunk' at a time + return max_enc_bytes_per_num * max_numbers_per_chunk; +} + +template <> +inline size_t TransactLogEncoder::max_size(UnsignedList list) +{ + const size_t* begin = std::get<0>(list); + const size_t* end = std::get<1>(list); + // list contains (end - begin) elements + return max_enc_bytes_per_num * (end - begin); +} + +template +void TransactLogEncoder::append_simple_instr(L... numbers) +{ + size_t max_required_bytes = max_size_list(numbers...); + char* ptr = reserve(max_required_bytes); // Throws + encode_list(ptr, numbers...); +} + +template +void TransactLogEncoder::append_mixed_instr(Instruction instr, const Mixed& value, L... numbers) +{ + DataType type = value.get_type(); + switch (type) { + case type_Int: + append_simple_instr(instr, numbers..., type, value.get_int()); // Throws + return; + case type_Bool: + append_simple_instr(instr, numbers..., type, value.get_bool()); // Throws + return; + case type_Float: + append_simple_instr(instr, numbers..., type, value.get_float()); // Throws + return; + case type_Double: + append_simple_instr(instr, numbers..., type, value.get_double()); // Throws + return; + case type_OldDateTime: { + auto value_2 = value.get_olddatetime().get_olddatetime(); + append_simple_instr(instr, numbers..., type, value_2); // Throws + return; + } + case type_String: { + append_simple_instr(instr, numbers..., type, value.get_string()); // Throws + return; + } + case type_Binary: { + BinaryData value_2 = value.get_binary(); + StringData value_3(value_2.data(), value_2.size()); + append_simple_instr(instr, numbers..., type, value_3); // Throws + return; + } + case type_Timestamp: { + Timestamp ts = value.get_timestamp(); + int64_t seconds = ts.get_seconds(); + int32_t nano_seconds = ts.get_nanoseconds(); + append_simple_instr(instr, numbers..., type, seconds, nano_seconds); // Throws + return; + } + case type_Table: + append_simple_instr(instr, numbers..., type); // Throws + return; + case type_Mixed: + // Mixed in mixed is not possible + REALM_ASSERT_RELEASE(false); + case type_Link: + case type_LinkList: + // FIXME: Need to handle new link types here. + REALM_ASSERT_RELEASE(false); + } + REALM_ASSERT_RELEASE(false); +} + +inline void TransactLogConvenientEncoder::unselect_all() noexcept +{ + m_selected_table = nullptr; + m_selected_spec = nullptr; + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_table(const Table* table) +{ + if (table != m_selected_table) + do_select_table(table); // Throws + m_selected_spec = nullptr; + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_desc(const Descriptor& desc) +{ + typedef _impl::DescriptorFriend df; + if (&df::get_spec(desc) != m_selected_spec) + do_select_desc(desc); // Throws + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_link_list(const LinkView& list) +{ + // A race between this and a call to on_link_list_destroyed() must + // end up with m_selected_link_list pointing to the list argument given + // here. We assume that the list given to on_link_list_destroyed() can + // *never* be the same as the list argument given here. We resolve the + // race by a) always updating m_selected_link_list in do_select_link_list() + // and b) only atomically and conditionally updating it in + // on_link_list_destroyed(). + if (&list != m_selected_link_list) { + do_select_link_list(list); // Throws + } + m_selected_spec = nullptr; +} + + +inline bool TransactLogEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, StringData name) +{ + append_simple_instr(instr_InsertGroupLevelTable, table_ndx, prior_num_tables, name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, + StringData name) +{ + unselect_all(); + m_encoder.insert_group_level_table(table_ndx, prior_num_tables, name); // Throws +} + +inline bool TransactLogEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables) +{ + append_simple_instr(instr_EraseGroupLevelTable, table_ndx, prior_num_tables); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables) +{ + unselect_all(); + m_encoder.erase_group_level_table(table_ndx, prior_num_tables); // Throws +} + +inline bool TransactLogEncoder::rename_group_level_table(size_t table_ndx, StringData new_name) +{ + append_simple_instr(instr_RenameGroupLevelTable, table_ndx, new_name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_group_level_table(size_t table_ndx, StringData new_name) +{ + unselect_all(); + m_encoder.rename_group_level_table(table_ndx, new_name); // Throws +} + +inline bool TransactLogEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) +{ + REALM_ASSERT(from_table_ndx != to_table_ndx); + append_simple_instr(instr_MoveGroupLevelTable, from_table_ndx, to_table_ndx); + return true; +} + +inline void TransactLogConvenientEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) +{ + unselect_all(); + m_encoder.move_group_level_table(from_table_ndx, to_table_ndx); +} + +inline bool TransactLogEncoder::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable) +{ + Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn); + append_simple_instr(instr, col_ndx, type, name); // Throws + return true; +} + +inline bool TransactLogEncoder::insert_link_column(size_t col_ndx, DataType type, StringData name, + size_t link_target_table_ndx, size_t backlink_col_ndx) +{ + REALM_ASSERT(_impl::TableFriend::is_link_type(ColumnType(type))); + append_simple_instr(instr_InsertLinkColumn, col_ndx, type, link_target_table_ndx, backlink_col_ndx, + name); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::insert_column(const Descriptor& desc, size_t col_ndx, DataType type, + StringData name, LinkTargetInfo& link, bool nullable) +{ + select_desc(desc); // Throws + if (link.is_valid()) { + typedef _impl::TableFriend tf; + typedef _impl::DescriptorFriend df; + size_t target_table_ndx = link.m_target_table->get_index_in_group(); + const Table& origin_table = df::get_root_table(desc); + REALM_ASSERT(origin_table.is_group_level()); + const Spec& target_spec = tf::get_spec(*(link.m_target_table)); + size_t origin_table_ndx = origin_table.get_index_in_group(); + size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx); + REALM_ASSERT_3(backlink_col_ndx, ==, link.m_backlink_col_ndx); + m_encoder.insert_link_column(col_ndx, type, name, target_table_ndx, backlink_col_ndx); // Throws + } + else { + m_encoder.insert_column(col_ndx, type, name, nullable); // Throws + } +} + +inline bool TransactLogEncoder::erase_column(size_t col_ndx) +{ + append_simple_instr(instr_EraseColumn, col_ndx); // Throws + return true; +} + +inline bool TransactLogEncoder::erase_link_column(size_t col_ndx, size_t link_target_table_ndx, + size_t backlink_col_ndx) +{ + append_simple_instr(instr_EraseLinkColumn, col_ndx, link_target_table_ndx, backlink_col_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_column(const Descriptor& desc, size_t col_ndx) +{ + select_desc(desc); // Throws + + DataType type = desc.get_column_type(col_ndx); + typedef _impl::TableFriend tf; + if (!tf::is_link_type(ColumnType(type))) { + m_encoder.erase_column(col_ndx); // Throws + } + else { // it's a link column: + REALM_ASSERT(desc.is_root()); + typedef _impl::DescriptorFriend df; + const Table& origin_table = df::get_root_table(desc); + REALM_ASSERT(origin_table.is_group_level()); + const Table& target_table = *tf::get_link_target_table_accessor(origin_table, col_ndx); + size_t target_table_ndx = target_table.get_index_in_group(); + const Spec& target_spec = tf::get_spec(target_table); + size_t origin_table_ndx = origin_table.get_index_in_group(); + size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx); + m_encoder.erase_link_column(col_ndx, target_table_ndx, backlink_col_ndx); // Throws + } +} + +inline bool TransactLogEncoder::rename_column(size_t col_ndx, StringData new_name) +{ + append_simple_instr(instr_RenameColumn, col_ndx, new_name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_column(const Descriptor& desc, size_t col_ndx, StringData name) +{ + select_desc(desc); // Throws + m_encoder.rename_column(col_ndx, name); // Throws +} + + +inline bool TransactLogEncoder::move_column(size_t from, size_t to) +{ + append_simple_instr(instr_MoveColumn, from, to); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::move_column(const Descriptor& desc, size_t from, size_t to) +{ + select_desc(desc); // Throws + m_encoder.move_column(from, to); +} + + +inline bool TransactLogEncoder::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant, + size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (REALM_UNLIKELY(variant == instr_SetUnique)) + append_simple_instr(variant, type_Int, col_ndx, ndx, prior_num_rows, value); // Throws + else + append_simple_instr(variant, type_Int, col_ndx, ndx, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_int(col_ndx, ndx, value, variant, prior_num_rows); // Throws +} + + +inline bool TransactLogEncoder::add_int(size_t col_ndx, size_t ndx, int_fast64_t value) +{ + append_simple_instr(instr_AddInteger, col_ndx, ndx, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::add_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value) +{ + select_table(t); // Throws + m_encoder.add_int(col_ndx, ndx, value); +} + +inline bool TransactLogEncoder::set_bool(size_t col_ndx, size_t ndx, bool value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, type_Bool, col_ndx, ndx, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_bool(const Table* t, size_t col_ndx, size_t ndx, bool value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_bool(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_float(size_t col_ndx, size_t ndx, float value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, type_Float, col_ndx, ndx, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_float(const Table* t, size_t col_ndx, size_t ndx, float value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_float(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_double(size_t col_ndx, size_t ndx, double value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(instr_Set, type_Double, col_ndx, ndx, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_double(const Table* t, size_t col_ndx, size_t ndx, double value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_double(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_string(size_t col_ndx, size_t ndx, StringData value, Instruction variant, + size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (value.is_null()) { + set_null(col_ndx, ndx, variant, prior_num_rows); // Throws + } + else { + if (REALM_UNLIKELY(variant == instr_SetUnique)) + append_simple_instr(variant, type_String, col_ndx, ndx, prior_num_rows, value); // Throws + else + append_simple_instr(variant, type_String, col_ndx, ndx, value); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_string(const Table* t, size_t col_ndx, size_t ndx, StringData value, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_string(col_ndx, ndx, value, variant, prior_num_rows); // Throws +} + +inline bool TransactLogEncoder::set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + if (value.is_null()) { + set_null(col_ndx, row_ndx, variant); // Throws + } + else { + StringData value_2(value.data(), value.size()); + append_simple_instr(variant, type_Binary, col_ndx, row_ndx, value_2); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_binary(const Table* t, size_t col_ndx, size_t ndx, BinaryData value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_binary(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, type_OldDateTime, col_ndx, ndx, value.get_olddatetime()); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_olddatetime(const Table* t, size_t col_ndx, size_t ndx, + OldDateTime value, Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_olddatetime(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, type_Timestamp, col_ndx, ndx, value.get_seconds(), + value.get_nanoseconds()); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, size_t col_ndx, size_t ndx, Timestamp value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_timestamp(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_table(size_t col_ndx, size_t ndx, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, type_Table, col_ndx, ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_table(const Table* t, size_t col_ndx, size_t ndx, Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_table(col_ndx, ndx, variant); // Throws +} + +inline bool TransactLogEncoder::set_mixed(size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_mixed_instr(variant, value, type_Mixed, col_ndx, ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_mixed(const Table* t, size_t col_ndx, size_t ndx, const Mixed& value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_mixed(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_link(size_t col_ndx, size_t ndx, size_t value, size_t target_group_level_ndx, + Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + // Map `realm::npos` to zero, and `n` to `n+1`, where `n` is a target row + // index. + size_t value_2 = size_t(1) + value; + append_simple_instr(variant, type_Link, col_ndx, ndx, value_2, target_group_level_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link(const Table* t, size_t col_ndx, size_t ndx, size_t value, + Instruction variant) +{ + select_table(t); // Throws + size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx); + m_encoder.set_link(col_ndx, ndx, value, target_group_level_ndx, variant); // Throws +} + +inline bool TransactLogEncoder::set_null(size_t col_ndx, size_t ndx, Instruction variant, size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (REALM_UNLIKELY(variant == instr_SetUnique)) { + append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx, prior_num_rows); // Throws + } + else { + append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_null(const Table* t, size_t col_ndx, size_t row_ndx, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); // Throws +} + +inline bool TransactLogEncoder::nullify_link(size_t col_ndx, size_t ndx, size_t target_group_level_ndx) +{ + append_simple_instr(instr_NullifyLink, col_ndx, ndx, target_group_level_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::nullify_link(const Table* t, size_t col_ndx, size_t ndx) +{ + select_table(t); // Throws + size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx); + m_encoder.nullify_link(col_ndx, ndx, target_group_level_ndx); // Throws +} + +inline bool TransactLogEncoder::insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData value) +{ + append_simple_instr(instr_InsertSubstring, col_ndx, row_ndx, pos, value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos, + StringData value) +{ + if (value.size() > 0) { + select_table(t); // Throws + m_encoder.insert_substring(col_ndx, row_ndx, pos, value); // Throws + } +} + +inline bool TransactLogEncoder::erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size) +{ + append_simple_instr(instr_EraseFromString, col_ndx, row_ndx, pos, size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos, + size_t size) +{ + if (size > 0) { + select_table(t); // Throws + m_encoder.erase_substring(col_ndx, row_ndx, pos, size); // Throws + } +} + +inline bool TransactLogEncoder::insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool unordered) +{ + append_simple_instr(instr_InsertEmptyRows, row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_empty_rows(const Table* t, size_t row_ndx, size_t num_rows_to_insert, + size_t prior_num_rows) +{ + select_table(t); // Throws + bool unordered = false; + m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws +} + +inline bool TransactLogEncoder::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool unordered) +{ + append_simple_instr(instr_EraseRows, row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::erase_rows(const Table* t, size_t row_ndx, size_t num_rows_to_erase, + size_t prior_num_rows, bool is_move_last_over) +{ + select_table(t); // Throws + bool unordered = is_move_last_over; + m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws +} + +inline bool TransactLogEncoder::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + append_simple_instr(instr_SwapRows, row_ndx_1, row_ndx_2); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::swap_rows(const Table* t, size_t row_ndx_1, size_t row_ndx_2) +{ + REALM_ASSERT(row_ndx_1 < row_ndx_2); + select_table(t); // Throws + m_encoder.swap_rows(row_ndx_1, row_ndx_2); +} + +inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx) +{ + append_simple_instr(instr_MergeRows, row_ndx, new_row_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::merge_rows(const Table* t, size_t row_ndx, size_t new_row_ndx) +{ + select_table(t); // Throws + m_encoder.merge_rows(row_ndx, new_row_ndx); +} + +inline bool TransactLogEncoder::add_search_index(size_t col_ndx) +{ + append_simple_instr(instr_AddSearchIndex, col_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::add_search_index(const Table* t, size_t col_ndx) +{ + select_table(t); // Throws + m_encoder.add_search_index(col_ndx); // Throws +} + + +inline bool TransactLogEncoder::remove_search_index(size_t col_ndx) +{ + append_simple_instr(instr_RemoveSearchIndex, col_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::remove_search_index(const Table* t, size_t col_ndx) +{ + select_table(t); // Throws + m_encoder.remove_search_index(col_ndx); // Throws +} + +inline bool TransactLogEncoder::set_link_type(size_t col_ndx, LinkType link_type) +{ + append_simple_instr(instr_SetLinkType, col_ndx, int(link_type)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link_type(const Table* t, size_t col_ndx, LinkType link_type) +{ + select_table(t); // Throws + m_encoder.set_link_type(col_ndx, link_type); // Throws +} + + +inline bool TransactLogEncoder::clear_table() +{ + append_simple_instr(instr_ClearTable); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::clear_table(const Table* t) +{ + select_table(t); // Throws + m_encoder.clear_table(); // Throws +} + +inline bool TransactLogEncoder::optimize_table() +{ + append_simple_instr(instr_OptimizeTable); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::optimize_table(const Table* t) +{ + select_table(t); // Throws + m_encoder.optimize_table(); // Throws +} + +inline bool TransactLogEncoder::link_list_set(size_t link_ndx, size_t value, size_t prior_size) +{ + append_simple_instr(instr_LinkListSet, link_ndx, value, prior_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_set(const LinkView& list, size_t link_ndx, size_t value) +{ + select_link_list(list); // Throws + m_encoder.link_list_set(link_ndx, value, list.size()); // Throws +} + +inline bool TransactLogEncoder::link_list_nullify(size_t link_ndx, size_t prior_size) +{ + append_simple_instr(instr_LinkListNullify, link_ndx, prior_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_nullify(const LinkView& list, size_t link_ndx) +{ + select_link_list(list); // Throws + size_t prior_size = list.size(); // Instruction is emitted before the fact. + m_encoder.link_list_nullify(link_ndx, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_set_all(const IntegerColumn& values) +{ + size_t num_values = values.size(); + append_simple_instr( + instr_LinkListSetAll, num_values, + std::make_tuple(IntegerColumnIterator(&values, 0), IntegerColumnIterator(&values, num_values))); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link_list(const LinkView& list, const IntegerColumn& values) +{ + select_link_list(list); // Throws + m_encoder.link_list_set_all(values); // Throws +} + +inline bool TransactLogEncoder::link_list_insert(size_t link_ndx, size_t value, size_t prior_size) +{ + append_simple_instr(instr_LinkListInsert, link_ndx, value, prior_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_insert(const LinkView& list, size_t link_ndx, size_t value) +{ + select_link_list(list); // Throws + size_t prior_size = list.size() - 1; // The instruction is emitted after the fact. + m_encoder.link_list_insert(link_ndx, value, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_move(size_t from_link_ndx, size_t to_link_ndx) +{ + REALM_ASSERT(from_link_ndx != to_link_ndx); + append_simple_instr(instr_LinkListMove, from_link_ndx, to_link_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_move(const LinkView& list, size_t from_link_ndx, + size_t to_link_ndx) +{ + select_link_list(list); // Throws + m_encoder.link_list_move(from_link_ndx, to_link_ndx); // Throws +} + +inline bool TransactLogEncoder::link_list_swap(size_t link1_ndx, size_t link2_ndx) +{ + append_simple_instr(instr_LinkListSwap, link1_ndx, link2_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_swap(const LinkView& list, size_t link1_ndx, size_t link2_ndx) +{ + select_link_list(list); // Throws + m_encoder.link_list_swap(link1_ndx, link2_ndx); // Throws +} + +inline bool TransactLogEncoder::link_list_erase(size_t link_ndx, size_t prior_size) +{ + append_simple_instr(instr_LinkListErase, link_ndx, prior_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_erase(const LinkView& list, size_t link_ndx) +{ + select_link_list(list); // Throws + size_t prior_size = list.size(); // The instruction is emitted before the fact. + m_encoder.link_list_erase(link_ndx, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_clear(size_t old_list_size) +{ + append_simple_instr(instr_LinkListClear, old_list_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::on_table_destroyed(const Table* t) noexcept +{ + if (m_selected_table == t) + m_selected_table = nullptr; +} + +inline void TransactLogConvenientEncoder::on_spec_destroyed(const Spec* s) noexcept +{ + if (m_selected_spec == s) + m_selected_spec = nullptr; +} + + +inline void TransactLogConvenientEncoder::on_link_list_destroyed(const LinkView& list) noexcept +{ + const LinkView* lw_ptr = &list; + // atomically clear m_selected_link_list iff it already points to 'list': + // (lw_ptr will be modified if the swap fails, but we ignore that) + m_selected_link_list.compare_exchange_strong(lw_ptr, nullptr, std::memory_order_relaxed, + std::memory_order_relaxed); +} + + +inline TransactLogParser::TransactLogParser() + : m_input_buffer(1024) // Throws +{ +} + + +inline TransactLogParser::~TransactLogParser() noexcept +{ +} + + +template +void TransactLogParser::parse(NoCopyInputStream& in, InstructionHandler& handler) +{ + m_input = ∈ + m_input_begin = m_input_end = nullptr; + + while (has_next()) + parse_one(handler); // Throws +} + +template +void TransactLogParser::parse(InputStream& in, InstructionHandler& handler) +{ + NoCopyInputStreamAdaptor in_2(in, m_input_buffer.data(), m_input_buffer.size()); + parse(in_2, handler); // Throws +} + +inline bool TransactLogParser::has_next() noexcept +{ + return m_input_begin != m_input_end || next_input_buffer(); +} + +template +void TransactLogParser::parse_one(InstructionHandler& handler) +{ + char instr_ch; + if (!read_char(instr_ch)) + parser_error(); // Throws + // std::cerr << "parsing " << util::promote(instr) << " @ " << std::hex << long(m_input_begin) << std::dec << + // "\n"; + Instruction instr = Instruction(instr_ch); + switch (instr) { + case instr_SetDefault: + case instr_SetUnique: + case instr_Set: { + int type = read_int(); // Throws + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t prior_num_rows = 0; + if (REALM_UNLIKELY(instr == instr_SetUnique)) + prior_num_rows = read_int(); // Throws + + if (type == TransactLogEncoder::set_null_sentinel()) { + // Special case for set_null + if (!handler.set_null(col_ndx, row_ndx, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + + switch (DataType(type)) { + case type_Int: { + int_fast64_t value = read_int(); // Throws + if (!handler.set_int(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + case type_Bool: { + bool value = read_bool(); // Throws + if (!handler.set_bool(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Float: { + float value = read_float(); // Throws + if (!handler.set_float(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Double: { + double value = read_double(); // Throws + if (!handler.set_double(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_String: { + StringData value = read_string(m_string_buffer); // Throws + if (!handler.set_string(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + case type_Binary: { + BinaryData value = read_binary(m_string_buffer); // Throws + if (!handler.set_binary(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_OldDateTime: { + int_fast64_t value = read_int(); // Throws + if (!handler.set_olddatetime(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Timestamp: { + int64_t seconds = read_int(); // Throws + int32_t nanoseconds = read_int(); // Throws + Timestamp value = Timestamp(seconds, nanoseconds); + if (!handler.set_timestamp(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Table: { + if (!handler.set_table(col_ndx, row_ndx, instr)) // Throws + parser_error(); + return; + } + case type_Mixed: { + Mixed value; + read_mixed(&value); // Throws + if (!handler.set_mixed(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Link: { + size_t value = read_int(); // Throws + // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index. + size_t target_row_ndx = size_t(value - 1); + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.set_link(col_ndx, row_ndx, target_row_ndx, target_group_level_ndx, instr)) // Throws + parser_error(); + return; + } + case type_LinkList: { + // Unsupported column type for Set. + parser_error(); + return; + } + } + parser_error(); + return; + } + case instr_AddInteger: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + int_fast64_t value = read_int(); // Throws + if (!handler.add_int(col_ndx, row_ndx, value)) // Throws + parser_error(); + return; + } + case instr_NullifyLink: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.nullify_link(col_ndx, row_ndx, target_group_level_ndx)) // Throws + parser_error(); + return; + } + case instr_InsertSubstring: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t pos = read_int(); // Throws + StringData value = read_string(m_string_buffer); // Throws + if (!handler.insert_substring(col_ndx, row_ndx, pos, value)) // Throws + parser_error(); + return; + } + case instr_EraseFromString: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t pos = read_int(); // Throws + size_t size = read_int(); // Throws + if (!handler.erase_substring(col_ndx, row_ndx, pos, size)) // Throws + parser_error(); + return; + } + case instr_InsertEmptyRows: { + size_t row_ndx = read_int(); // Throws + size_t num_rows_to_insert = read_int(); // Throws + size_t prior_num_rows = read_int(); // Throws + bool unordered = read_bool(); // Throws + if (!handler.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered)) // Throws + parser_error(); + return; + } + case instr_EraseRows: { + size_t row_ndx = read_int(); // Throws + size_t num_rows_to_erase = read_int(); // Throws + size_t prior_num_rows = read_int(); // Throws + bool unordered = read_bool(); // Throws + if (!handler.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered)) // Throws + parser_error(); + return; + } + case instr_SwapRows: { + size_t row_ndx_1 = read_int(); // Throws + size_t row_ndx_2 = read_int(); // Throws + if (!handler.swap_rows(row_ndx_1, row_ndx_2)) // Throws + parser_error(); + return; + } + case instr_MergeRows: { + size_t row_ndx = read_int(); // Throws + size_t new_row_ndx = read_int(); // Throws + if (!handler.merge_rows(row_ndx, new_row_ndx)) // Throws + parser_error(); + return; + } + case instr_SelectTable: { + int levels = read_int(); // Throws + if (levels < 0 || levels > m_max_levels) + parser_error(); + m_path.reserve(0, 2 * levels); // Throws + size_t* path = m_path.data(); + size_t group_level_ndx = read_int(); // Throws + for (int i = 0; i != levels; ++i) { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + path[2 * i + 0] = col_ndx; + path[2 * i + 1] = row_ndx; + } + if (!handler.select_table(group_level_ndx, levels, path)) // Throws + parser_error(); + return; + } + case instr_ClearTable: { + if (!handler.clear_table()) // Throws + parser_error(); + return; + } + case instr_LinkListSet: { + size_t link_ndx = read_int(); // Throws + size_t value = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_set(link_ndx, value, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListSetAll: { + // todo, log that it's a SetAll we're doing + size_t size = read_int(); // Throws + for (size_t i = 0; i < size; i++) { + size_t link = read_int(); // Throws + if (!handler.link_list_set(i, link, size)) // Throws + parser_error(); + } + return; + } + case instr_LinkListInsert: { + size_t link_ndx = read_int(); // Throws + size_t value = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_insert(link_ndx, value, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListMove: { + size_t from_link_ndx = read_int(); // Throws + size_t to_link_ndx = read_int(); // Throws + if (!handler.link_list_move(from_link_ndx, to_link_ndx)) // Throws + parser_error(); + return; + } + case instr_LinkListSwap: { + size_t link1_ndx = read_int(); // Throws + size_t link2_ndx = read_int(); // Throws + if (!handler.link_list_swap(link1_ndx, link2_ndx)) // Throws + parser_error(); + return; + } + case instr_LinkListErase: { + size_t link_ndx = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_erase(link_ndx, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListNullify: { + size_t link_ndx = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_nullify(link_ndx, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListClear: { + size_t old_list_size = read_int(); // Throws + if (!handler.link_list_clear(old_list_size)) // Throws + parser_error(); + return; + } + case instr_SelectLinkList: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.select_link_list(col_ndx, row_ndx, target_group_level_ndx)) // Throws + parser_error(); + return; + } + case instr_AddSearchIndex: { + size_t col_ndx = read_int(); // Throws + if (!handler.add_search_index(col_ndx)) // Throws + parser_error(); + return; + } + case instr_RemoveSearchIndex: { + size_t col_ndx = read_int(); // Throws + if (!handler.remove_search_index(col_ndx)) // Throws + parser_error(); + return; + } + case instr_SetLinkType: { + size_t col_ndx = read_int(); // Throws + int link_type = read_int(); // Throws + if (!is_valid_link_type(link_type)) + parser_error(); + if (!handler.set_link_type(col_ndx, LinkType(link_type))) // Throws + parser_error(); + return; + } + case instr_InsertColumn: + case instr_InsertNullableColumn: { + size_t col_ndx = read_int(); // Throws + int type = read_int(); // Throws + if (!is_valid_data_type(type)) + parser_error(); + if (REALM_UNLIKELY(type == type_Link || type == type_LinkList)) + parser_error(); + StringData name = read_string(m_string_buffer); // Throws + bool nullable = (Instruction(instr) == instr_InsertNullableColumn); + if (REALM_UNLIKELY(nullable && (type == type_Table || type == type_Mixed))) { + // Nullability not supported for Table and Mixed columns. + parser_error(); + } + if (!handler.insert_column(col_ndx, DataType(type), name, nullable)) // Throws + parser_error(); + return; + } + case instr_InsertLinkColumn: { + size_t col_ndx = read_int(); // Throws + int type = read_int(); // Throws + if (!is_valid_data_type(type)) + parser_error(); + if (REALM_UNLIKELY(type != type_Link && type != type_LinkList)) + parser_error(); + size_t link_target_table_ndx = read_int(); // Throws + size_t backlink_col_ndx = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.insert_link_column(col_ndx, DataType(type), name, link_target_table_ndx, + backlink_col_ndx)) // Throws + parser_error(); + return; + } + case instr_EraseColumn: { + size_t col_ndx = read_int(); // Throws + if (!handler.erase_column(col_ndx)) // Throws + parser_error(); + return; + } + case instr_EraseLinkColumn: { + size_t col_ndx = read_int(); // Throws + size_t link_target_table_ndx = read_int(); // Throws + size_t backlink_col_ndx = read_int(); // Throws + if (!handler.erase_link_column(col_ndx, link_target_table_ndx, backlink_col_ndx)) // Throws + parser_error(); + return; + } + case instr_RenameColumn: { + size_t col_ndx = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.rename_column(col_ndx, name)) // Throws + parser_error(); + return; + } + case instr_MoveColumn: { + size_t col_ndx_1 = read_int(); // Throws + size_t col_ndx_2 = read_int(); // Throws + if (!handler.move_column(col_ndx_1, col_ndx_2)) // Throws + parser_error(); + return; + } + case instr_SelectDescriptor: { + int levels = read_int(); // Throws + if (levels < 0 || levels > m_max_levels) + parser_error(); + m_path.reserve(0, levels); // Throws + size_t* path = m_path.data(); + for (int i = 0; i != levels; ++i) { + size_t col_ndx = read_int(); // Throws + path[i] = col_ndx; + } + if (!handler.select_descriptor(levels, path)) // Throws + parser_error(); + return; + } + case instr_InsertGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + size_t num_tables = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.insert_group_level_table(table_ndx, num_tables, name)) // Throws + parser_error(); + return; + } + case instr_EraseGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + size_t prior_num_tables = read_int(); // Throws + if (!handler.erase_group_level_table(table_ndx, prior_num_tables)) // Throws + parser_error(); + return; + } + case instr_RenameGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + StringData new_name = read_string(m_string_buffer); // Throws + if (!handler.rename_group_level_table(table_ndx, new_name)) // Throws + parser_error(); + return; + } + case instr_MoveGroupLevelTable: { + size_t from_table_ndx = read_int(); // Throws + size_t to_table_ndx = read_int(); // Throws + if (!handler.move_group_level_table(from_table_ndx, to_table_ndx)) // Throws + parser_error(); + return; + } + case instr_OptimizeTable: { + if (!handler.optimize_table()) // Throws + parser_error(); + return; + } + } + + throw BadTransactLog(); +} + + +template +T TransactLogParser::read_int() +{ + T value = 0; + int part = 0; + const int max_bytes = (std::numeric_limits::digits + 1 + 6) / 7; + for (int i = 0; i != max_bytes; ++i) { + char c; + if (!read_char(c)) + goto bad_transact_log; + part = static_cast(c); + if (0xFF < part) + goto bad_transact_log; // Only the first 8 bits may be used in each byte + if ((part & 0x80) == 0) { + T p = part & 0x3F; + if (util::int_shift_left_with_overflow_detect(p, i * 7)) + goto bad_transact_log; + value |= p; + break; + } + if (i == max_bytes - 1) + goto bad_transact_log; // Too many bytes + value |= T(part & 0x7F) << (i * 7); + } + if (part & 0x40) { + // The real value is negative. Because 'value' is positive at + // this point, the following negation is guaranteed by C++11 + // to never overflow. See C99+TC3 section 6.2.6.2 paragraph 2. + REALM_DIAG_PUSH(); + REALM_DIAG_IGNORE_UNSIGNED_MINUS(); + value = -value; + REALM_DIAG_POP(); + if (util::int_subtract_with_overflow_detect(value, 1)) + goto bad_transact_log; + } + return value; + +bad_transact_log: + throw BadTransactLog(); +} + + +inline void TransactLogParser::read_bytes(char* data, size_t size) +{ + for (;;) { + const size_t avail = m_input_end - m_input_begin; + if (size <= avail) + break; + realm::safe_copy_n(m_input_begin, avail, data); + if (!next_input_buffer()) + throw BadTransactLog(); + data += avail; + size -= avail; + } + const char* to = m_input_begin + size; + realm::safe_copy_n(m_input_begin, size, data); + m_input_begin = to; +} + + +inline BinaryData TransactLogParser::read_buffer(util::StringBuffer& buf, size_t size) +{ + const size_t avail = m_input_end - m_input_begin; + if (avail >= size) { + m_input_begin += size; + return BinaryData(m_input_begin - size, size); + } + + buf.clear(); + buf.resize(size); // Throws + read_bytes(buf.data(), size); + return BinaryData(buf.data(), size); +} + + +inline bool TransactLogParser::read_bool() +{ + return read_int(); +} + + +inline float TransactLogParser::read_float() +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(float) * std::numeric_limits::digits == 32, + "Unsupported 'float' representation"); + float value; + read_bytes(reinterpret_cast(&value), sizeof value); // Throws + return value; +} + + +inline double TransactLogParser::read_double() +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(double) * std::numeric_limits::digits == 64, + "Unsupported 'double' representation"); + double value; + read_bytes(reinterpret_cast(&value), sizeof value); // Throws + return value; +} + + +inline StringData TransactLogParser::read_string(util::StringBuffer& buf) +{ + size_t size = read_int(); // Throws + + if (size > Table::max_string_size) + parser_error(); + + BinaryData buffer = read_buffer(buf, size); + return StringData{buffer.data(), size}; +} + +inline Timestamp TransactLogParser::read_timestamp() +{ + int64_t seconds = read_int(); // Throws + int32_t nanoseconds = read_int(); // Throws + return Timestamp(seconds, nanoseconds); +} + + +inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf) +{ + size_t size = read_int(); // Throws + + return read_buffer(buf, size); +} + + +inline void TransactLogParser::read_mixed(Mixed* mixed) +{ + DataType type = DataType(read_int()); // Throws + switch (type) { + case type_Int: { + int_fast64_t value = read_int(); // Throws + mixed->set_int(value); + return; + } + case type_Bool: { + bool value = read_bool(); // Throws + mixed->set_bool(value); + return; + } + case type_Float: { + float value = read_float(); // Throws + mixed->set_float(value); + return; + } + case type_Double: { + double value = read_double(); // Throws + mixed->set_double(value); + return; + } + case type_OldDateTime: { + int_fast64_t value = read_int(); // Throws + mixed->set_olddatetime(value); + return; + } + case type_Timestamp: { + Timestamp value = read_timestamp(); // Throws + mixed->set_timestamp(value); + return; + } + case type_String: { + StringData value = read_string(m_string_buffer); // Throws + mixed->set_string(value); + return; + } + case type_Binary: { + BinaryData value = read_binary(m_string_buffer); // Throws + mixed->set_binary(value); + return; + } + case type_Table: { + *mixed = Mixed::subtable_tag(); + return; + } + case type_Mixed: + break; + case type_Link: + case type_LinkList: + // FIXME: Need to handle new link types here + break; + } + throw BadTransactLog(); +} + + +inline bool TransactLogParser::next_input_buffer() +{ + return m_input->next_block(m_input_begin, m_input_end); +} + + +inline bool TransactLogParser::read_char(char& c) +{ + if (m_input_begin == m_input_end && !next_input_buffer()) + return false; + c = *m_input_begin++; + return true; +} + + +inline bool TransactLogParser::is_valid_data_type(int type) +{ + switch (DataType(type)) { + case type_Int: + case type_Bool: + case type_Float: + case type_Double: + case type_String: + case type_Binary: + case type_OldDateTime: + case type_Timestamp: + case type_Table: + case type_Mixed: + case type_Link: + case type_LinkList: + return true; + } + return false; +} + + +inline bool TransactLogParser::is_valid_link_type(int type) +{ + switch (LinkType(type)) { + case link_Strong: + case link_Weak: + return true; + } + return false; +} + + +class TransactReverser { +public: + bool select_table(size_t group_level_ndx, size_t levels, const size_t* path) + { + sync_table(); + m_encoder.select_table(group_level_ndx, levels, path); + m_pending_ts_instr = get_inst(); + return true; + } + + bool select_descriptor(size_t levels, const size_t* path) + { + sync_descriptor(); + m_encoder.select_descriptor(levels, path); + m_pending_ds_instr = get_inst(); + return true; + } + + bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData) + { + sync_table(); + m_encoder.erase_group_level_table(table_ndx, num_tables + 1); + append_instruction(); + return true; + } + + bool erase_group_level_table(size_t table_ndx, size_t num_tables) + { + sync_table(); + m_encoder.insert_group_level_table(table_ndx, num_tables - 1, ""); + append_instruction(); + return true; + } + + bool rename_group_level_table(size_t, StringData) + { + sync_table(); + return true; + } + + bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) + { + sync_table(); + m_encoder.move_group_level_table(to_table_ndx, from_table_ndx); + append_instruction(); + return true; + } + + bool optimize_table() + { + return true; // No-op + } + + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered) + { + size_t num_rows_to_erase = num_rows_to_insert; + size_t prior_num_rows_2 = prior_num_rows + num_rows_to_insert; + m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows_2, unordered); // Throws + append_instruction(); + return true; + } + + bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered) + { + size_t num_rows_to_insert = num_rows_to_erase; + // Number of rows in table after removal, but before inverse insertion + size_t prior_num_rows_2 = prior_num_rows - num_rows_to_erase; + m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows_2, unordered); // Throws + append_instruction(); + return true; + } + + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) + { + m_encoder.swap_rows(row_ndx_1, row_ndx_2); + append_instruction(); + return true; + } + + bool merge_rows(size_t row_ndx, size_t new_row_ndx) + { + // There is no instruction we can generate here to change back. + // However, we do need to refresh accessors for any tables + // connected through backlinks, so we generate updates on each + // affected row by merging to itself. + m_encoder.merge_rows(row_ndx, row_ndx); + append_instruction(); + m_encoder.merge_rows(new_row_ndx, new_row_ndx); + append_instruction(); + return true; + } + + bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t value, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_int(col_ndx, row_ndx, value, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t value) + { + m_encoder.add_int(col_ndx, row_ndx, -value); + append_instruction(); + return true; + } + + bool set_bool(size_t col_ndx, size_t row_ndx, bool value, Instruction variant) + { + m_encoder.set_bool(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_float(size_t col_ndx, size_t row_ndx, float value, Instruction variant) + { + m_encoder.set_float(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_double(size_t col_ndx, size_t row_ndx, double value, Instruction variant) + { + m_encoder.set_double(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_string(size_t col_ndx, size_t row_ndx, StringData value, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_string(col_ndx, row_ndx, value, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant) + { + m_encoder.set_binary(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime value, Instruction variant) + { + m_encoder.set_olddatetime(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp value, Instruction variant) + { + m_encoder.set_timestamp(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_table(size_t col_ndx, size_t row_ndx, Instruction variant) + { + m_encoder.set_table(col_ndx, row_ndx, variant); + append_instruction(); + return true; + } + + bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed& value, Instruction variant) + { + m_encoder.set_mixed(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_null(size_t col_ndx, size_t row_ndx, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool set_link(size_t col_ndx, size_t row_ndx, size_t value, size_t target_group_level_ndx, Instruction variant) + { + m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx, variant); + append_instruction(); + return true; + } + + bool insert_substring(size_t, size_t, size_t, StringData) + { + return true; // No-op + } + + bool erase_substring(size_t, size_t, size_t, size_t) + { + return true; // No-op + } + + bool clear_table() + { + // FIXME: Add a comment on why we call insert_empty_rows() inside clear_table() + m_encoder.insert_empty_rows(0, 0, 0, true); + append_instruction(); + return true; + } + + bool add_search_index(size_t) + { + return true; // No-op + } + + bool remove_search_index(size_t) + { + return true; // No-op + } + + bool set_link_type(size_t, LinkType) + { + return true; // No-op + } + + bool insert_link_column(size_t col_idx, DataType, StringData, size_t target_table_idx, size_t backlink_col_ndx) + { + m_encoder.erase_link_column(col_idx, target_table_idx, backlink_col_ndx); + append_instruction(); + return true; + } + + bool erase_link_column(size_t col_idx, size_t target_table_idx, size_t backlink_col_idx) + { + DataType type = type_Link; // The real type of the column doesn't matter here, + // but the encoder asserts that it's actually a link type. + m_encoder.insert_link_column(col_idx, type, "", target_table_idx, backlink_col_idx); + append_instruction(); + return true; + } + + bool insert_column(size_t col_idx, DataType, StringData, bool) + { + m_encoder.erase_column(col_idx); + append_instruction(); + return true; + } + + bool erase_column(size_t col_idx) + { + m_encoder.insert_column(col_idx, DataType(), ""); + append_instruction(); + return true; + } + + bool rename_column(size_t, StringData) + { + return true; // No-op + } + + bool move_column(size_t col_ndx_1, size_t col_ndx_2) + { + m_encoder.move_column(col_ndx_2, col_ndx_1); + append_instruction(); + return true; + } + + bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx) + { + sync_linkview(); + m_encoder.select_link_list(col_ndx, row_ndx, link_target_group_level_ndx); + m_pending_lv_instr = get_inst(); + return true; + } + + bool link_list_set(size_t row, size_t value, size_t prior_size) + { + m_encoder.link_list_set(row, value, prior_size); + append_instruction(); + return true; + } + + bool link_list_insert(size_t link_ndx, size_t, size_t prior_size) + { + m_encoder.link_list_erase(link_ndx, prior_size + 1); + append_instruction(); + return true; + } + + bool link_list_move(size_t from_link_ndx, size_t to_link_ndx) + { + m_encoder.link_list_move(from_link_ndx, to_link_ndx); + append_instruction(); + return true; + } + + bool link_list_swap(size_t link1_ndx, size_t link2_ndx) + { + m_encoder.link_list_swap(link1_ndx, link2_ndx); + append_instruction(); + return true; + } + + bool link_list_erase(size_t link_ndx, size_t prior_size) + { + m_encoder.link_list_insert(link_ndx, 0, prior_size - 1); + append_instruction(); + return true; + } + + bool link_list_clear(size_t old_list_size) + { + // Append in reverse order because the reversed log is itself applied + // in reverse, and this way it generates all back-insertions rather than + // all front-insertions + for (size_t i = old_list_size; i > 0; --i) { + m_encoder.link_list_insert(i - 1, 0, old_list_size - i); + append_instruction(); + } + return true; + } + + bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx) + { + size_t value = 0; + // FIXME: Is zero this right value to pass here, or should + // TransactReverser::nullify_link() also have taken a + // `target_group_level_ndx` argument. + m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx); + append_instruction(); + return true; + } + + bool link_list_nullify(size_t link_ndx, size_t prior_size) + { + m_encoder.link_list_insert(link_ndx, 0, prior_size - 1); + append_instruction(); + return true; + } + +private: + _impl::TransactLogBufferStream m_buffer; + _impl::TransactLogEncoder m_encoder{m_buffer}; + struct Instr { + size_t begin; + size_t end; + }; + std::vector m_instructions; + size_t current_instr_start = 0; + Instr m_pending_ts_instr{0, 0}; + Instr m_pending_ds_instr{0, 0}; + Instr m_pending_lv_instr{0, 0}; + + Instr get_inst() + { + Instr instr; + instr.begin = current_instr_start; + current_instr_start = transact_log_size(); + instr.end = current_instr_start; + return instr; + } + + size_t transact_log_size() const + { + REALM_ASSERT_3(m_encoder.write_position(), >=, m_buffer.transact_log_data()); + return m_encoder.write_position() - m_buffer.transact_log_data(); + } + + void append_instruction() + { + m_instructions.push_back(get_inst()); + } + + void append_instruction(Instr instr) + { + m_instructions.push_back(instr); + } + + void sync_select(Instr& pending_instr) + { + if (pending_instr.begin != pending_instr.end) { + append_instruction(pending_instr); + pending_instr = {0, 0}; + } + } + + void sync_linkview() + { + sync_select(m_pending_lv_instr); + } + + void sync_descriptor() + { + sync_linkview(); + sync_select(m_pending_ds_instr); + } + + void sync_table() + { + sync_descriptor(); + sync_select(m_pending_ts_instr); + } + + friend class ReversedNoCopyInputStream; +}; + + +class ReversedNoCopyInputStream : public NoCopyInputStream { +public: + ReversedNoCopyInputStream(TransactReverser& reverser) + : m_instr_order(reverser.m_instructions) + { + // push any pending select_table or select_descriptor into the buffer + reverser.sync_table(); + + m_buffer = reverser.m_buffer.transact_log_data(); + m_current = m_instr_order.size(); + } + + bool next_block(const char*& begin, const char*& end) override + { + if (m_current != 0) { + m_current--; + begin = m_buffer + m_instr_order[m_current].begin; + end = m_buffer + m_instr_order[m_current].end; + return (end > begin); + } + return false; + } + +private: + const char* m_buffer; + std::vector& m_instr_order; + size_t m_current; +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_TRANSACT_LOG_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/importer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/importer.hpp new file mode 100644 index 0000000..9ef54dc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/importer.hpp @@ -0,0 +1,131 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_IMPORTER_HPP +#define REALM_IMPORTER_HPP + +/* +Main method: import_csv(). Arguments: +--------------------------------------------------------------------------------------------------------------------- +empty_as_string_flag: + Imports a column that has occurences of empty strings as String type column. Else fields arec onverted to + false/0/0.0 + +type_detection_rows: + tells how many rows to read before analyzing data types (to see if numeric rows are really + numeric everywhere, and not strings that happen to just mostly contain numeric characters + + +This library supports: +--------------------------------------------------------------------------------------------------------------------- + * Auto detection of float vs. double, depending on number of significant digits + * Bool types can be case insensitive "true, false, 0, 1, yes, no" + * Newline inside data fields, plus auto detection of non-conforming non-quoted newlines (as in some IBM sample + files) + * Realm types String, Integer, Bool, Float and Double + * Auto detection of header and naming of Realm columns accordingly + * double-quoted and non-quoted fields, and these can be mixed arbitrarely + * double-quotes inside data field + * *nix + MacOSv9 + Windows line feed + * Scientific notation of floats/doubles (+1.23e-10) + * Comma in floats - but ONLY if field is double-quoted + * FAST FAST FAST (200 MB/s). Uses state-machine instead of traditional char-by-char loop with state checks inside + + +Problems: +--------------------------------------------------------------------------------------------------------------------- + A csv file does not tell its sheme. So we auto-detect it, based on the first N rows. However if a given column + contains 'false, false, false, hello' and we detect and create Realm table scheme using the first 3 rows, we fail + when we meet 'hello' (this error is handled with a thorough error message) + + Does not support commas in floats unless field is double-quoted + + +Design: +--------------------------------------------------------------------------------------------------------------------- + +import_csv(csv file handle, realm table) + Calls tokenize(csv file handle): + reads payload chunk and returns std::vector> with the right dimensions filled with + rows and columns of the chunk payload + Calls parse_float(), parse_bool(), etc, which tests for type and returns converted values + Calls table.add_empty_row(), table.set_float(), table.set_bool() +*/ + +#include + +// Disk read chunk size. This MUST be large enough to contain at least TWO rows of csv plaintext! It's a good idea +// to set it as low as ever possible (like 32 K) even though it's counter-intuitive with respect to performance. It +// will make the operating system read 32 K from disk and return it, and then read-ahead 32-64 K more after fread() +// has returned. This read-ahead behaviour does NOT occur if we request megabyte-sized chunks (observed on Windows 7 / +// Ubuntu) +static const size_t chunk_size = 32 * 1024; + +// Number of rows to csv-parse + insert into realm in each iteration. +static const size_t record_chunks = 100; + +// Width of each column when printing them on screen (non-Quiet mode) +const size_t print_width = 25; + +#include +#include + +namespace realm { + +class Importer { +public: + Importer(); + size_t import_csv_auto(FILE* file, Table& table, size_t type_detection_rows = 1000, + size_t import_rows = static_cast(-1)); + + size_t import_csv_manual(FILE* file, Table& table, std::vector scheme, + std::vector column_names, size_t skip_first_rows = 0, + size_t import_rows = static_cast(-1)); + + bool Quiet; // Quiet mode, only print to screen upon errors + char Separator; // csv delimitor/separator + bool Empty_as_string; // Import columns that have occurences of empty strings as String type column + +private: + size_t import_csv(FILE* file, Table& table, std::vector* import_scheme, + std::vector* column_names, size_t type_detection_rows, size_t skip_first_rows, + size_t import_rows); + template + float parse_float(const char* col, bool* success = nullptr); + template + double parse_double(const char* col, bool* success = nullptr, size_t* significants = nullptr); + template + int64_t parse_integer(const char* col, bool* success = nullptr); + template + bool parse_bool(const char* col, bool* success = nullptr); + std::vector types(std::vector v); + size_t tokenize(std::vector>& payload, size_t records); + std::vector detect_scheme(std::vector> payload, size_t begin, size_t end); + std::vector lowest_common(std::vector types1, std::vector types2); + + char src[2 * chunk_size]; // .csv input buffer + size_t m_top; // points at top of buffer + size_t m_curpos; // points at next byte to parse + FILE* m_file; // handle to .csv file + size_t m_fields; // number of fields in each row + size_t m_row; // current row in .csv file, including field-embedded line breaks. Used for err msg only +}; + +} // namespace realm + +#endif // REALM_IMPORTER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/index_string.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/index_string.hpp new file mode 100644 index 0000000..d88c33f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/index_string.hpp @@ -0,0 +1,602 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_INDEX_STRING_HPP +#define REALM_INDEX_STRING_HPP + +#include +#include +#include + +#include +#include + +/* +The StringIndex class is used for both type_String and all integral types, such as type_Bool, type_OldDateTime and +type_Int. When used for integral types, the 64-bit integer is simply casted to a string of 8 bytes through a +pretty simple "wrapper layer" in all public methods. + +The StringIndex data structure is like an "inversed" B+ tree where the leafs contain row indexes and the non-leafs +contain 4-byte chunks of payload. Imagine a table with following strings: + + hello, kitty, kitten, foobar, kitty, foobar + +The topmost level of the index tree contains prefixes of the payload strings of length <= 4. The next level contains +prefixes of the remaining parts of the strings. Unnecessary levels of the tree are optimized away; the prefix "foob" +is shared only by rows that are identical ("foobar"), so "ar" is not needed to be stored in the tree. + + hell kitt foob + | /\ | + 0 en y {3, 5} + | \ + {1, 4} 2 + +Each non-leafs consists of two integer arrays of the same length, one containing payload and the other containing +references to the sublevel nodes. + +The leafs can be either a single value or a Column. If the reference in its parent node has its least significant +bit set, then the remaining upper bits specify the row index at which the string is stored. If the bit is clear, +it must be interpreted as a reference to a Column that stores the row indexes at which the string is stored. + +If a Column is used, then all row indexes are guaranteed to be sorted increasingly, which means you an search in it +using our binary search functions such as upper_bound() and lower_bound(). Each duplicate value will be stored in +the same Column, but Columns may contain more than just duplicates if the depth of the tree exceeds the value +`s_max_offset` This is to avoid stack overflow problems with many of our recursive functions if we have two very +long strings that have a long common prefix but differ in the last couple bytes. If a Column stores more than just +duplicates, then the list is kept sorted in ascending order by string value and within the groups of common +strings, the rows are sorted in ascending order. +*/ + +namespace realm { + +class Spec; +class Timestamp; + +class IndexArray : public Array { +public: + IndexArray(Allocator& allocator) + : Array(allocator) + { + } + + size_t index_string_find_first(StringData value, ColumnBase* column) const; + void index_string_find_all(IntegerColumn& result, StringData value, ColumnBase* column, bool case_insensitive = false) const; + FindRes index_string_find_all_no_copy(StringData value, ColumnBase* column, InternalFindResult& result) const; + size_t index_string_count(StringData value, ColumnBase* column) const; + +private: + template + size_t from_list(StringData value, InternalFindResult& result_ref, const IntegerColumn& rows, + ColumnBase* column) const; + + void from_list_all(StringData value, IntegerColumn& result, const IntegerColumn& rows, ColumnBase* column) const; + + void from_list_all_ins(StringData value, IntegerColumn& result, const IntegerColumn& rows, + ColumnBase* column) const; + + template + size_t index_string(StringData value, InternalFindResult& result_ref, ColumnBase* column) const; + + void index_string_all(StringData value, IntegerColumn& result, ColumnBase* column) const; + + void index_string_all_ins(StringData value, IntegerColumn& result, ColumnBase* column) const; +}; + + +class StringIndex { +public: + StringIndex(ColumnBase* target_column, Allocator&); + StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, ColumnBase* target_column, Allocator&); + ~StringIndex() noexcept + { + } + void set_target(ColumnBase* target_column) noexcept; + + // Accessor concept: + Allocator& get_alloc() const noexcept; + void destroy() noexcept; + void detach(); + bool is_attached() const noexcept; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t ndx_in_parent) noexcept; + void update_from_parent(size_t old_baseline) noexcept; + void refresh_accessor_tree(size_t, const Spec&); + ref_type get_ref() const noexcept; + + // StringIndex interface: + + // 12 is the biggest element size of any non-string/binary Realm type + static const size_t string_conversion_buffer_size = 12; + using StringConversionBuffer = std::array; + + bool is_empty() const; + + template + void insert(size_t row_ndx, T value, size_t num_rows, bool is_append); + template + void insert(size_t row_ndx, util::Optional value, size_t num_rows, bool is_append); + + template + void set(size_t row_ndx, T new_value); + template + void set(size_t row_ndx, util::Optional new_value); + + template + void erase(size_t row_ndx, bool is_last); + + template + size_t find_first(T value) const; + template + void find_all(IntegerColumn& result, T value, bool case_insensitive = false) const; + template + FindRes find_all_no_copy(T value, InternalFindResult& result) const; + template + size_t count(T value) const; + template + void update_ref(T value, size_t old_row_ndx, size_t new_row_ndx); + + void clear(); + + void distinct(IntegerColumn& result) const; + bool has_duplicate_values() const noexcept; + + void verify() const; +#ifdef REALM_DEBUG + void verify_entries(const StringColumn& column) const; + void do_dump_node_structure(std::ostream&, int) const; + void to_dot() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + + typedef int32_t key_type; + + // s_max_offset specifies the number of levels of recursive string indexes + // allowed before storing everything in lists. This is to avoid nesting + // to too deep of a level. Since every SubStringIndex stores 4 bytes, this + // means that a StringIndex is helpful for strings of a common prefix up to + // 4 times this limit (200 bytes shared). Lists are stored in sorted order, + // so strings sharing a common prefix of more than this limit will use a + // binary search of approximate complexity log2(n) from `std::lower_bound`. + static const size_t s_max_offset = 200; // max depth * s_index_key_length + static const size_t s_index_key_length = 4; + static key_type create_key(StringData) noexcept; + static key_type create_key(StringData, size_t) noexcept; + +private: + // m_array is a compact representation for storing the children of this StringIndex. + // Children can be: + // 1) a row number + // 2) a reference to a list which stores row numbers (for duplicate strings). + // 3) a reference to a sub-index + // m_array[0] is always a reference to a values array which stores the 4 byte chunk + // of payload data for quick string chunk comparisons. The array stored + // at m_array[0] lines up with the indices of values in m_array[1] so for example + // starting with an empty StringIndex: + // StringColumn::insert(target_row_ndx=42, value="test_string") would result with + // get_array_from_ref(m_array[0])[0] == create_key("test") and + // m_array[1] == 42 + // In this way, m_array which stores one child has a size of two. + // Children are type 1 (row number) if the LSB of the value is set. + // To get the actual row value, shift value down by one. + // If the LSB of the value is 0 then the value is a reference and can be either + // type 2, or type 3 (no shifting in either case). + // References point to a list if the context header flag is NOT set. + // If the header flag is set, references point to a sub-StringIndex (nesting). + std::unique_ptr m_array; + ColumnBase* m_target_column; + + struct inner_node_tag { + }; + StringIndex(inner_node_tag, Allocator&); + + static IndexArray* create_node(Allocator&, bool is_leaf); + + void insert_with_offset(size_t row_ndx, StringData value, size_t offset); + void insert_row_list(size_t ref, size_t offset, StringData value); + void insert_to_existing_list(size_t row, StringData value, IntegerColumn& list); + void insert_to_existing_list_at_lower(size_t row, StringData value, IntegerColumn& list, + const IntegerColumnIterator& lower); + key_type get_last_key() const; + + /// Add small signed \a diff to all elements that are greater than, or equal + /// to \a min_row_ndx. + void adjust_row_indexes(size_t min_row_ndx, int diff); + + struct NodeChange { + size_t ref1; + size_t ref2; + enum ChangeType { none, insert_before, insert_after, split } type; + NodeChange(ChangeType t, size_t r1 = 0, size_t r2 = 0) + : ref1(r1) + , ref2(r2) + , type(t) + { + } + NodeChange() + : ref1(0) + , ref2(0) + , type(none) + { + } + }; + + // B-Tree functions + void TreeInsert(size_t row_ndx, key_type, size_t offset, StringData value); + NodeChange do_insert(size_t ndx, key_type, size_t offset, StringData value); + /// Returns true if there is room or it can join existing entries + bool leaf_insert(size_t row_ndx, key_type, size_t offset, StringData value, bool noextend = false); + void node_insert_split(size_t ndx, size_t new_ref); + void node_insert(size_t ndx, size_t ref); + void do_delete(size_t ndx, StringData, size_t offset); + void do_update_ref(StringData value, size_t row_ndx, size_t new_row_ndx, size_t offset); + + StringData get(size_t ndx, StringConversionBuffer& buffer) const; + + void node_add_key(ref_type ref); + +#ifdef REALM_DEBUG + static void dump_node_structure(const Array& node, std::ostream&, int level); + void to_dot_2(std::ostream&, StringData title = StringData()) const; + static void array_to_dot(std::ostream&, const Array&); + static void keys_to_dot(std::ostream&, const Array&, StringData title = StringData()); +#endif +}; + + +class SortedListComparator { +public: + SortedListComparator(ColumnBase& column_values); + bool operator()(int64_t ndx, StringData needle); + bool operator()(StringData needle, int64_t ndx); + +private: + ColumnBase& values; +}; + + +// Implementation: + +template +struct GetIndexData; + +template <> +struct GetIndexData { + static StringData get_index_data(const int64_t& value, StringIndex::StringConversionBuffer& buffer) + { + const char* c = reinterpret_cast(&value); + realm::safe_copy_n(c, sizeof(int64_t), buffer.data()); + return StringData{buffer.data(), sizeof(int64_t)}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(StringData data, StringIndex::StringConversionBuffer&) + { + return data; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(null, StringIndex::StringConversionBuffer&) + { + return null{}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(const Timestamp&, StringIndex::StringConversionBuffer&); +}; + +template +struct GetIndexData> { + static StringData get_index_data(const util::Optional& value, StringIndex::StringConversionBuffer& buffer) + { + if (value) + return GetIndexData::get_index_data(*value, buffer); + return null{}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(float, StringIndex::StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(double, StringIndex::StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + } +}; + +template <> +struct GetIndexData : GetIndexData { +}; + +// to_str() is used by the integer index. The existing StringIndex is re-used for this +// by making IntegerColumn convert its integers to strings by calling to_str(). + +template +inline StringData to_str(T&& value, StringIndex::StringConversionBuffer& buffer) +{ + return GetIndexData::type>::get_index_data(value, buffer); +} + + +inline StringIndex::StringIndex(ColumnBase* target_column, Allocator& alloc) + : m_array(create_node(alloc, true)) // Throws + , m_target_column(target_column) +{ +} + +inline StringIndex::StringIndex(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, ColumnBase* target_column, + Allocator& alloc) + : m_array(new IndexArray(alloc)) + , m_target_column(target_column) +{ + REALM_ASSERT_EX(Array::get_context_flag_from_header(alloc.translate(ref)), ref, size_t(alloc.translate(ref))); + m_array->init_from_ref(ref); + set_parent(parent, ndx_in_parent); +} + +inline StringIndex::StringIndex(inner_node_tag, Allocator& alloc) + : m_array(create_node(alloc, false)) // Throws + , m_target_column(nullptr) +{ +} + +// Byte order of the key is *reversed*, so that for the integer index, the least significant +// byte comes first, so that it fits little-endian machines. That way we can perform fast +// range-lookups and iterate in order, etc, as future features. This, however, makes the same +// features slower for string indexes. Todo, we should reverse the order conditionally, depending +// on the column type. +inline StringIndex::key_type StringIndex::create_key(StringData str) noexcept +{ + key_type key = 0; + + if (str.size() >= 4) + goto four; + if (str.size() < 2) { + if (str.size() == 0) + goto none; + goto one; + } + if (str.size() == 2) + goto two; + goto three; + +// Create 4 byte index key +// (encoded like this to allow literal comparisons +// independently of endianness) +four: + key |= (key_type(static_cast(str[3])) << 0); +three: + key |= (key_type(static_cast(str[2])) << 8); +two: + key |= (key_type(static_cast(str[1])) << 16); +one: + key |= (key_type(static_cast(str[0])) << 24); +none: + return key; +} + +// Index works as follows: All non-NULL values are stored as if they had appended an 'X' character at the end. So +// "foo" is stored as if it was "fooX", and "" (empty string) is stored as "X". And NULLs are stored as empty strings. +inline StringIndex::key_type StringIndex::create_key(StringData str, size_t offset) noexcept +{ + if (str.is_null()) + return 0; + + if (offset > str.size()) + return 0; + + // for very short strings + size_t tail = str.size() - offset; + if (tail <= sizeof(key_type) - 1) { + char buf[sizeof(key_type)]; + memset(buf, 0, sizeof(key_type)); + buf[tail] = 'X'; + memcpy(buf, str.data() + offset, tail); + return create_key(StringData(buf, tail + 1)); + } + // else fallback + return create_key(str.substr(offset)); +} + +template +void StringIndex::insert(size_t row_ndx, T value, size_t num_rows, bool is_append) +{ + REALM_ASSERT_3(row_ndx, !=, npos); + + // If the new row is inserted after the last row in the table, we don't need + // to adjust any row indexes. + if (!is_append) { + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx + i; + adjust_row_indexes(row_ndx_2, 1); // Throws + } + } + + StringConversionBuffer buffer; + + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx + i; + size_t offset = 0; // First key from beginning of string + insert_with_offset(row_ndx_2, to_str(value, buffer), offset); // Throws + } +} + +template +void StringIndex::insert(size_t row_ndx, util::Optional value, size_t num_rows, bool is_append) +{ + if (value) { + insert(row_ndx, *value, num_rows, is_append); + } + else { + insert(row_ndx, null{}, num_rows, is_append); + } +} + +template +void StringIndex::set(size_t row_ndx, T new_value) +{ + StringConversionBuffer buffer; + StringConversionBuffer buffer2; + StringData old_value = get(row_ndx, buffer); + StringData new_value2 = to_str(new_value, buffer2); + + // Note that insert_with_offset() throws UniqueConstraintViolation. + + if (REALM_LIKELY(new_value2 != old_value)) { + // We must erase this row first because erase uses find_first which + // might find the duplicate if we insert before erasing. + bool is_last = true; // To avoid updating refs + erase(row_ndx, is_last); // Throws + + size_t offset = 0; // First key from beginning of string + insert_with_offset(row_ndx, new_value2, offset); // Throws + } +} + +template +void StringIndex::set(size_t row_ndx, util::Optional new_value) +{ + if (new_value) { + set(row_ndx, *new_value); + } + else { + set(row_ndx, null{}); + } +} + +template +void StringIndex::erase(size_t row_ndx, bool is_last) +{ + StringConversionBuffer buffer; + StringData value = get(row_ndx, buffer); + + do_delete(row_ndx, value, 0); + + // Collapse top nodes with single item + while (m_array->is_inner_bptree_node()) { + REALM_ASSERT(m_array->size() > 1); // node cannot be empty + if (m_array->size() > 2) + break; + + ref_type ref = m_array->get_as_ref(1); + m_array->set(1, 1); // avoid destruction of the extracted ref + m_array->destroy_deep(); + m_array->init_from_ref(ref); + m_array->update_parent(); + } + + // If it is last item in column, we don't have to update refs + if (!is_last) + adjust_row_indexes(row_ndx, -1); +} + +template +size_t StringIndex::find_first(T value) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_first(to_str(value, buffer), m_target_column); +} + +template +void StringIndex::find_all(IntegerColumn& result, T value, bool case_insensitive) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_all(result, to_str(value, buffer), m_target_column, case_insensitive); +} + +template +FindRes StringIndex::find_all_no_copy(T value, InternalFindResult& result) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_all_no_copy(to_str(value, buffer), m_target_column, result); +} + +template +size_t StringIndex::count(T value) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_count(to_str(value, buffer), m_target_column); +} + +template +void StringIndex::update_ref(T value, size_t old_row_ndx, size_t new_row_ndx) +{ + StringConversionBuffer buffer; + do_update_ref(to_str(value, buffer), old_row_ndx, new_row_ndx, 0); +} + +inline void StringIndex::destroy() noexcept +{ + return m_array->destroy_deep(); +} + +inline bool StringIndex::is_attached() const noexcept +{ + return m_array->is_attached(); +} + +inline void StringIndex::refresh_accessor_tree(size_t, const Spec&) +{ + m_array->init_from_parent(); +} + +inline ref_type StringIndex::get_ref() const noexcept +{ + return m_array->get_ref(); +} + +inline void StringIndex::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_array->set_parent(parent, ndx_in_parent); +} + +inline size_t StringIndex::get_ndx_in_parent() const noexcept +{ + return m_array->get_ndx_in_parent(); +} + +inline void StringIndex::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + m_array->set_ndx_in_parent(ndx_in_parent); +} + +inline void StringIndex::update_from_parent(size_t old_baseline) noexcept +{ + m_array->update_from_parent(old_baseline); +} + +} // namespace realm + +#endif // REALM_INDEX_STRING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp new file mode 100644 index 0000000..9e93c77 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp @@ -0,0 +1,380 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_LANG_BIND_HELPER_HPP +#define REALM_LANG_BIND_HELPER_HPP + +#include + +#include +#include +#include +#include +#include + +#include + +namespace realm { + + +/// These functions are only to be used by language bindings to gain +/// access to certain memebers that are othewise private. +/// +/// \note Applications are not supposed to call any of these functions +/// directly. +/// +/// All of the get_subtable_ptr() functions bind the table accessor pointer +/// before it is returned (bind_table_ptr()). The caller is then responsible for +/// making the corresponding call to unbind_table_ptr(). +class LangBindHelper { +public: + /// Increment the reference counter of the specified table accessor. This is + /// done automatically by all of the functions in this class that return + /// table accessor pointers, but if the binding/application makes a copy of + /// such a pointer, and the copy needs to have an "independent life", then + /// the binding/application must bind that copy using this function. + static void bind_table_ptr(const Table*) noexcept; + + /// Decrement the reference counter of the specified table accessor. The + /// binding/application must call this function for every bound table + /// accessor pointer object, when that pointer object ends its life. + static void unbind_table_ptr(const Table*) noexcept; + + /// Construct a new freestanding table. The table accessor pointer is bound + /// by the callee before it is returned (bind_table_ptr()). + static Table* new_table(); + + /// Construct a new freestanding table as a copy of the specified one. The + /// table accessor pointer is bound by the callee before it is returned + /// (bind_table_ptr()). + static Table* copy_table(const Table&); + + //@{ + + /// These functions are like their namesakes in Group, but these bypass the + /// construction of a smart-pointer object (TableRef). The table accessor + /// pointer is bound by the callee before it is returned (bind_table_ptr()). + + static Table* get_table(Group&, size_t index_in_group); + static const Table* get_table(const Group&, size_t index_in_group); + + static Table* get_table(Group&, StringData name); + static const Table* get_table(const Group&, StringData name); + + static Table* add_table(Group&, StringData name, bool require_unique_name = true); + static Table* get_or_add_table(Group&, StringData name, bool* was_added = nullptr); + + //@} + + static Table* get_subtable_ptr(Table*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const Table*, size_t column_ndx, size_t row_ndx); + + // FIXME: This is an 'oddball', do we really need it? If we do, + // please provide a comment that explains why it is needed! + static Table* get_subtable_ptr_during_insert(Table*, size_t col_ndx, size_t row_ndx); + + static Table* get_subtable_ptr(TableView*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const TableView*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const ConstTableView*, size_t column_ndx, size_t row_ndx); + + /// Calls parent.set_mixed_subtable(col_ndx, row_ndx, &source). Note + /// that the source table must have a descriptor that is + /// compatible with the target subtable column. + static void set_mixed_subtable(Table& parent, size_t col_ndx, size_t row_ndx, const Table& source); + + static const LinkViewRef& get_linklist_ptr(Row&, size_t col_ndx); + static void unbind_linklist_ptr(const LinkViewRef&); + + using VersionID = SharedGroup::VersionID; + + /// \defgroup lang_bind_helper_transactions Continuous Transactions + /// + /// advance_read() is equivalent to terminating the current read transaction + /// (SharedGroup::end_read()), and initiating a new one + /// (SharedGroup::begin_read()), except that all subordinate accessors + /// (Table, Row, Descriptor) will remain attached to the underlying objects, + /// unless those objects were removed in the target snapshot. By default, + /// the read transaction is advanced to the latest available snapshot, but + /// see SharedGroup::begin_read() for information about \a version. + /// + /// promote_to_write() is equivalent to terminating the current read + /// transaction (SharedGroup::end_read()), and initiating a new write + /// transaction (SharedGroup::begin_write()), except that all subordinate + /// accessors (Table, Row, Descriptor) will remain attached to the + /// underlying objects, unless those objects were removed in the target + /// snapshot. + /// + /// commit_and_continue_as_read() is equivalent to committing the current + /// write transaction (SharedGroup::commit()) and initiating a new read + /// transaction, which is bound to the snapshot produced by the write + /// transaction (SharedGroup::begin_read()), except that all subordinate + /// accessors (Table, Row, Descriptor) will remain attached to the + /// underlying objects. commit_and_continue_as_read() returns the version + /// produced by the committed transaction. + /// + /// rollback_and_continue_as_read() is equivalent to rolling back the + /// current write transaction (SharedGroup::rollback()) and initiating a new + /// read transaction, which is bound to the snapshot, that the write + /// transaction was based on (SharedGroup::begin_read()), except that all + /// subordinate accessors (Table, Row, Descriptor) will remain attached to + /// the underlying objects, unless they were attached to object that were + /// added during the rolled back transaction. + /// + /// If advance_read(), promote_to_write(), commit_and_continue_as_read(), or + /// rollback_and_continue_as_read() throws, the associated group accessor + /// and all of its subordinate accessors are left in a state that may not be + /// fully consistent. Only minimal consistency is guaranteed (see + /// AccessorConsistencyLevels). In this case, the application is required to + /// either destroy the SharedGroup object, forcing all associated accessors + /// to become detached, or take some other equivalent action that involves a + /// complete accessor detachment, such as terminating the transaction in + /// progress. Until then it is an error, and unsafe if the application + /// attempts to access any of those accessors. + /// + /// The application must use SharedGroup::end_read() if it wants to + /// terminate the transaction after advance_read() or promote_to_write() has + /// thrown an exception. Likewise, it must use SharedGroup::rollback() if it + /// wants to terminate the transaction after commit_and_continue_as_read() + /// or rollback_and_continue_as_read() has thrown an exception. + /// + /// \param observer An optional custom replication instruction handler. The + /// application may pass such a handler to observe the sequence of + /// modifications that advances (or rolls back) the state of the Realm. + /// + /// \throw SharedGroup::BadVersion Thrown by advance_read() if the specified + /// version does not correspond to a bound (or tethered) snapshot. + /// + //@{ + + static void advance_read(SharedGroup&, VersionID = VersionID()); + template + static void advance_read(SharedGroup&, O&& observer, VersionID = VersionID()); + static void promote_to_write(SharedGroup&); + template + static void promote_to_write(SharedGroup&, O&& observer); + static SharedGroup::version_type commit_and_continue_as_read(SharedGroup&); + static void rollback_and_continue_as_read(SharedGroup&); + template + static void rollback_and_continue_as_read(SharedGroup&, O&& observer); + + //@} + + /// Returns the name of the specified data type. Examples: + /// + ///
+    ///
+    ///   type_Int          ->  "int"
+    ///   type_Bool         ->  "bool"
+    ///   type_Float        ->  "float"
+    ///   ...
+    ///
+    /// 
+ static const char* get_data_type_name(DataType) noexcept; + + static SharedGroup::version_type get_version_of_latest_snapshot(SharedGroup&); +}; + + +// Implementation: + +inline Table* LangBindHelper::new_table() +{ + typedef _impl::TableFriend tf; + Allocator& alloc = Allocator::get_default(); + size_t ref = tf::create_empty_table(alloc); // Throws + Table::Parent* parent = nullptr; + size_t ndx_in_parent = 0; + Table* table = tf::create_accessor(alloc, ref, parent, ndx_in_parent); // Throws + bind_table_ptr(table); + return table; +} + +inline Table* LangBindHelper::copy_table(const Table& table) +{ + typedef _impl::TableFriend tf; + Allocator& alloc = Allocator::get_default(); + size_t ref = tf::clone(table, alloc); // Throws + Table::Parent* parent = nullptr; + size_t ndx_in_parent = 0; + Table* copy_of_table = tf::create_accessor(alloc, ref, parent, ndx_in_parent); // Throws + bind_table_ptr(copy_of_table); + return copy_of_table; +} + +inline Table* LangBindHelper::get_subtable_ptr(Table* t, size_t column_ndx, size_t row_ndx) +{ + Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws + subtab->bind_ptr(); + return subtab; +} + +inline const Table* LangBindHelper::get_subtable_ptr(const Table* t, size_t column_ndx, size_t row_ndx) +{ + const Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws + subtab->bind_ptr(); + return subtab; +} + +inline Table* LangBindHelper::get_subtable_ptr(TableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline const Table* LangBindHelper::get_subtable_ptr(const TableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline const Table* LangBindHelper::get_subtable_ptr(const ConstTableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline Table* LangBindHelper::get_table(Group& group, size_t index_in_group) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::get_table(group, index_in_group); // Throws + table->bind_ptr(); + return table; +} + +inline const Table* LangBindHelper::get_table(const Group& group, size_t index_in_group) +{ + typedef _impl::GroupFriend gf; + const Table* table = &gf::get_table(group, index_in_group); // Throws + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::get_table(Group& group, StringData name) +{ + typedef _impl::GroupFriend gf; + Table* table = gf::get_table(group, name); // Throws + if (table) + table->bind_ptr(); + return table; +} + +inline const Table* LangBindHelper::get_table(const Group& group, StringData name) +{ + typedef _impl::GroupFriend gf; + const Table* table = gf::get_table(group, name); // Throws + if (table) + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::add_table(Group& group, StringData name, bool require_unique_name) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::add_table(group, name, require_unique_name); // Throws + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::get_or_add_table(Group& group, StringData name, bool* was_added) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::get_or_add_table(group, name, was_added); // Throws + table->bind_ptr(); + return table; +} + +inline void LangBindHelper::unbind_table_ptr(const Table* t) noexcept +{ + t->unbind_ptr(); +} + +inline void LangBindHelper::bind_table_ptr(const Table* t) noexcept +{ + t->bind_ptr(); +} + +inline void LangBindHelper::set_mixed_subtable(Table& parent, size_t col_ndx, size_t row_ndx, const Table& source) +{ + parent.set_mixed_subtable(col_ndx, row_ndx, &source); +} + +inline const LinkViewRef& LangBindHelper::get_linklist_ptr(Row& row, size_t col_ndx) +{ + LinkViewRef* link_view = new LinkViewRef(row.get_linklist(col_ndx)); + return *link_view; +} + +inline void LangBindHelper::unbind_linklist_ptr(const LinkViewRef& link_view) +{ + delete (&link_view); +} + +inline void LangBindHelper::advance_read(SharedGroup& sg, VersionID version) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::advance_read(sg, observer, version); // Throws +} + +template +inline void LangBindHelper::advance_read(SharedGroup& sg, O&& observer, VersionID version) +{ + using sgf = _impl::SharedGroupFriend; + sgf::advance_read(sg, &observer, version); // Throws +} + +inline void LangBindHelper::promote_to_write(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::promote_to_write(sg, observer); // Throws +} + +template +inline void LangBindHelper::promote_to_write(SharedGroup& sg, O&& observer) +{ + using sgf = _impl::SharedGroupFriend; + sgf::promote_to_write(sg, &observer); // Throws +} + +inline SharedGroup::version_type LangBindHelper::commit_and_continue_as_read(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + return sgf::commit_and_continue_as_read(sg); // Throws +} + +inline void LangBindHelper::rollback_and_continue_as_read(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::rollback_and_continue_as_read(sg, observer); // Throws +} + +template +inline void LangBindHelper::rollback_and_continue_as_read(SharedGroup& sg, O&& observer) +{ + using sgf = _impl::SharedGroupFriend; + sgf::rollback_and_continue_as_read(sg, &observer); // Throws +} + +inline SharedGroup::version_type LangBindHelper::get_version_of_latest_snapshot(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_latest_snapshot(sg); // Throws +} + +} // namespace realm + +#endif // REALM_LANG_BIND_HELPER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view.hpp new file mode 100644 index 0000000..20c12a2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view.hpp @@ -0,0 +1,397 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_LINK_VIEW_HPP +#define REALM_LINK_VIEW_HPP + +#include +#include +#include +#include + +namespace realm { + +class LinkListColumn; + +namespace _impl { +class LinkListFriend; +class TransactLogConvenientEncoder; +} + + +/// The effect of calling most of the link list functions on a detached accessor +/// is unspecified and may lead to general corruption, or even a crash. The +/// exceptions are is_attached() and the destructor. +/// +/// FIXME: Rename this class to `LinkList`. +class LinkView : public RowIndexes, public std::enable_shared_from_this { +public: + ~LinkView() noexcept; + bool is_attached() const noexcept; + + /// This method will return true if the LinkView is detached (no assert). + bool is_empty() const noexcept; + + /// This method will return 0 if the LinkView is detached (no assert). + size_t size() const noexcept override; + + bool operator==(const LinkView&) const noexcept; + bool operator!=(const LinkView&) const noexcept; + + // Getting links + Table::ConstRowExpr operator[](size_t link_ndx) const noexcept; + Table::RowExpr operator[](size_t link_ndx) noexcept; + Table::ConstRowExpr get(size_t link_ndx) const noexcept; + Table::RowExpr get(size_t link_ndx) noexcept; + + // Modifiers + void add(size_t target_row_ndx); + void insert(size_t link_ndx, size_t target_row_ndx); + void set(size_t link_ndx, size_t target_row_ndx); + /// Move the link at \a from_ndx such that it ends up at \a to_ndx. Other + /// links are shifted as necessary in such a way that their order is + /// preserved. + /// + /// Note that \a to_ndx is the desired final index of the moved link, + /// therefore, `move(1,1)` is a no-op, while `move(1,2)` moves the link at + /// index 1 by one position, such that it ends up at index 2. A side-effect + /// of that, is that the link, that was originally at index 2, is moved to + /// index 1. + void move(size_t from_ndx, size_t to_ndx); + void swap(size_t link1_ndx, size_t link2_ndx); + void remove(size_t link_ndx); + void clear(); + + void sort(size_t column, bool ascending = true); + void sort(const SortDescriptor& order); + + TableView get_sorted_view(size_t column_index, bool ascending = true) const; + TableView get_sorted_view(SortDescriptor order) const; + + /// Remove the target row of the specified link from the target table. This + /// also removes the specified link from this link list, and any other link + /// pointing to that row. This is merely a shorthand for + /// `get_target_table.move_last_over(get(link_ndx))`. + void remove_target_row(size_t link_ndx); + + /// Remove all target rows pointed to by links in this link list, and clear + /// this link list. + void remove_all_target_rows(); + + /// Search this list for a link to the specified target table row (specified + /// by its index in the target table). If found, the index of the link to + /// that row within this list is returned, otherwise `realm::not_found` is + /// returned. + size_t find(size_t target_row_ndx, size_t start = 0) const noexcept; + + const ColumnBase& get_column_base(size_t index) + const override; // FIXME: `ColumnBase` is not part of the public API, so this function must be made private. + const Table& get_origin_table() const noexcept; + Table& get_origin_table() noexcept; + + size_t get_origin_row_index() const noexcept; + + const Table& get_target_table() const noexcept; + Table& get_target_table() noexcept; + + // No-op because LinkViews are always kept in sync. + uint_fast64_t sync_if_needed() const override; + bool is_in_sync() const override + { + return true; + } + +private: + struct ctor_cookie { + }; + + TableRef m_origin_table; + LinkListColumn* m_origin_column; + + using HandoverPatch = LinkViewHandoverPatch; + static void generate_patch(const ConstLinkViewRef& ref, std::unique_ptr& patch); + static LinkViewRef create_from_and_consume_patch(std::unique_ptr& patch, Group& group); + + void detach(); + void set_origin_row_index(size_t row_ndx) noexcept; + + void do_insert(size_t link_ndx, size_t target_row_ndx); + size_t do_set(size_t link_ndx, size_t target_row_ndx); + size_t do_remove(size_t link_ndx); + void do_clear(bool broken_reciprocal_backlinks); + + void do_nullify_link(size_t old_target_row_ndx); + void do_update_link(size_t old_target_row_ndx, size_t new_target_row_ndx); + void do_swap_link(size_t target_row_ndx_1, size_t target_row_ndx_2); + + void refresh_accessor_tree(size_t new_row_ndx) noexcept; + + void update_from_parent(size_t old_baseline) noexcept; + + Replication* get_repl() noexcept; + void repl_unselect() noexcept; + friend class _impl::TransactLogConvenientEncoder; + +#ifdef REALM_DEBUG + void verify(size_t row_ndx) const; +#endif + // allocate using make_shared: + static std::shared_ptr create(Table* origin_table, LinkListColumn&, size_t row_ndx); + static std::shared_ptr create_detached(); + + friend class _impl::LinkListFriend; + friend class LinkListColumn; + friend class LangBindHelper; + friend class SharedGroup; + friend class Query; + friend class TableViewBase; + + // must be public for use by make_shared, but cannot be called from outside, + // because ctor_cookie is private +public: + LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn&, size_t row_ndx); + LinkView(const ctor_cookie&); +}; + + +// Implementation + +inline LinkView::LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn& column, size_t row_ndx) + : RowIndexes(IntegerColumn::unattached_root_tag(), column.get_alloc()) // Throws + , m_origin_table(origin_table->get_table_ref()) + , m_origin_column(&column) +{ + m_row_indexes.set_parent(m_origin_column, row_ndx); + m_row_indexes.init_from_parent(); +} + +// create a detached LinkView. Only partially initialized, as it will never be used for +// anything, but indicating that it is detached. +inline LinkView::LinkView(const ctor_cookie&) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws + , m_origin_table(TableRef()) + , m_origin_column(nullptr) +{ +} + +inline std::shared_ptr LinkView::create(Table* origin_table, LinkListColumn& column, size_t row_ndx) +{ + return std::make_shared(ctor_cookie(), origin_table, column, row_ndx); +} + +inline std::shared_ptr LinkView::create_detached() +{ + return std::make_shared(ctor_cookie()); +} + +inline LinkView::~LinkView() noexcept +{ + if (is_attached()) { + repl_unselect(); + m_origin_column->unregister_linkview(); + } +} + +inline void LinkView::detach() +{ + REALM_ASSERT(is_attached()); + repl_unselect(); + m_origin_table.reset(); + m_row_indexes.detach(); +} + +inline bool LinkView::is_attached() const noexcept +{ + return static_cast(m_origin_table); +} + +inline bool LinkView::is_empty() const noexcept +{ + if (!is_attached()) + return true; + + if (!m_row_indexes.is_attached()) + return true; + + return m_row_indexes.is_empty(); +} + +inline size_t LinkView::size() const noexcept +{ + if (!is_attached()) + return 0; + + if (!m_row_indexes.is_attached()) + return 0; + + return m_row_indexes.size(); +} + +inline bool LinkView::operator==(const LinkView& link_list) const noexcept +{ + Table& target_table_1 = m_origin_column->get_target_table(); + Table& target_table_2 = link_list.m_origin_column->get_target_table(); + if (target_table_1.get_index_in_group() != target_table_2.get_index_in_group()) + return false; + if (!m_row_indexes.is_attached() || m_row_indexes.is_empty()) { + return !link_list.m_row_indexes.is_attached() || link_list.m_row_indexes.is_empty(); + } + return link_list.m_row_indexes.is_attached() && m_row_indexes.compare(link_list.m_row_indexes); +} + +inline bool LinkView::operator!=(const LinkView& link_list) const noexcept +{ + return !(*this == link_list); +} + +inline Table::ConstRowExpr LinkView::get(size_t link_ndx) const noexcept +{ + return const_cast(this)->get(link_ndx); +} + +inline Table::RowExpr LinkView::get(size_t link_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(m_row_indexes.is_attached()); + REALM_ASSERT_3(link_ndx, <, m_row_indexes.size()); + + Table& target_table = m_origin_column->get_target_table(); + size_t target_row_ndx = to_size_t(m_row_indexes.get(link_ndx)); + return target_table[target_row_ndx]; +} + +inline Table::ConstRowExpr LinkView::operator[](size_t link_ndx) const noexcept +{ + return get(link_ndx); +} + +inline Table::RowExpr LinkView::operator[](size_t link_ndx) noexcept +{ + return get(link_ndx); +} + +inline void LinkView::add(size_t target_row_ndx) +{ + REALM_ASSERT(is_attached()); + size_t ins_pos = (m_row_indexes.is_attached()) ? m_row_indexes.size() : 0; + insert(ins_pos, target_row_ndx); +} + +inline size_t LinkView::find(size_t target_row_ndx, size_t start) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT_3(target_row_ndx, <, m_origin_column->get_target_table().size()); + REALM_ASSERT_3(start, <=, size()); + + if (!m_row_indexes.is_attached()) + return not_found; + + return m_row_indexes.find_first(target_row_ndx, start); +} + +inline const ColumnBase& LinkView::get_column_base(size_t index) const +{ + return get_target_table().get_column_base(index); +} + +inline const Table& LinkView::get_origin_table() const noexcept +{ + return *m_origin_table; +} + +inline Table& LinkView::get_origin_table() noexcept +{ + return *m_origin_table; +} + +inline size_t LinkView::get_origin_row_index() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_row_indexes.get_root_array()->get_ndx_in_parent(); +} + +inline void LinkView::set_origin_row_index(size_t row_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + m_row_indexes.get_root_array()->set_ndx_in_parent(row_ndx); +} + +inline const Table& LinkView::get_target_table() const noexcept +{ + return m_origin_column->get_target_table(); +} + +inline Table& LinkView::get_target_table() noexcept +{ + return m_origin_column->get_target_table(); +} + +inline void LinkView::refresh_accessor_tree(size_t new_row_ndx) noexcept +{ + set_origin_row_index(new_row_ndx); + m_row_indexes.init_from_parent(); +} + +inline void LinkView::update_from_parent(size_t old_baseline) noexcept +{ + if (m_row_indexes.is_attached()) + m_row_indexes.update_from_parent(old_baseline); +} + +inline Replication* LinkView::get_repl() noexcept +{ + typedef _impl::TableFriend tf; + return tf::get_repl(*m_origin_table); +} + + +// The purpose of this class is to give internal access to some, but not all of +// the non-public parts of LinkView. +class _impl::LinkListFriend { +public: + static void do_set(LinkView& list, size_t link_ndx, size_t target_row_ndx) + { + list.do_set(link_ndx, target_row_ndx); + } + + static void do_remove(LinkView& list, size_t link_ndx) + { + list.do_remove(link_ndx); + } + + static void do_clear(LinkView& list) + { + bool broken_reciprocal_backlinks = false; + list.do_clear(broken_reciprocal_backlinks); + } + + static void do_insert(LinkView& list, size_t link_ndx, size_t target_row_ndx) + { + list.do_insert(link_ndx, target_row_ndx); + } + + static const LinkListColumn& get_origin_column(const LinkView& list) + { + REALM_ASSERT(list.is_attached()); + return *list.m_origin_column; + } +}; + +} // namespace realm + +#endif // REALM_LINK_VIEW_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp new file mode 100644 index 0000000..cba8801 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp @@ -0,0 +1,32 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_LINK_VIEW_FWD_HPP +#define REALM_LINK_VIEW_FWD_HPP + +#include + +namespace realm { + +class LinkView; +using LinkViewRef = std::shared_ptr; +using ConstLinkViewRef = std::shared_ptr; + +} // namespace realm + +#endif // REALM_LINK_VIEW_FWD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/mixed.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/mixed.hpp new file mode 100644 index 0000000..f50f4a0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/mixed.hpp @@ -0,0 +1,680 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_MIXED_HPP +#define REALM_MIXED_HPP + +#include // int64_t - not part of C++03, not even required by C++11 (see C++11 section 18.4.1) + +#include // size_t +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +/// This class represents a polymorphic Realm value. +/// +/// At any particular moment an instance of this class stores a +/// definite value of a definite type. If, for instance, that is an +/// integer value, you may call get_int() to extract that value. You +/// may call get_type() to discover what type of value is currently +/// stored. Calling get_int() on an instance that does not store an +/// integer, has undefined behavior, and likewise for all the other +/// types that can be stored. +/// +/// It is crucial to understand that the act of extracting a value of +/// a particular type requires definite knowledge about the stored +/// type. Calling a getter method for any particular type, that is not +/// the same type as the stored value, has undefined behavior. +/// +/// While values of numeric types are contained directly in a Mixed +/// instance, character and binary data are merely referenced. A Mixed +/// instance never owns the referenced data, nor does it in any other +/// way attempt to manage its lifetime. +/// +/// For compatibility with C style strings, when a string (character +/// data) is stored in a Realm database, it is always followed by a +/// terminating null character. This is also true when strings are +/// stored in a mixed type column. This means that in the following +/// code, if the 'mixed' value of the 8th row stores a string, then \c +/// c_str will always point to a null-terminated string: +/// +/// \code{.cpp} +/// +/// const char* c_str = my_table[7].mixed.data(); // Always null-terminated +/// +/// \endcode +/// +/// Note that this assumption does not hold in general for strings in +/// instances of Mixed. Indeed there is nothing stopping you from +/// constructing a new Mixed instance that refers to a string without +/// a terminating null character. +/// +/// At the present time no soultion has been found that would allow +/// for a Mixed instance to directly store a reference to a table. The +/// problem is roughly as follows: From most points of view, the +/// desirable thing to do, would be to store the table reference in a +/// Mixed instance as a plain pointer without any ownership +/// semantics. This would have no negative impact on the performance +/// of copying and destroying Mixed instances, and it would serve just +/// fine for passing a table as argument when setting the value of an +/// entry in a mixed column. In that case a copy of the referenced +/// table would be inserted into the mixed column. +/// +/// On the other hand, when retrieving a table reference from a mixed +/// column, storing it as a plain pointer in a Mixed instance is no +/// longer an acceptable option. The complex rules for managing the +/// lifetime of a Table instance, that represents a subtable, +/// necessitates the use of a "smart pointer" such as +/// TableRef. Enhancing the Mixed class to be able to act as a +/// TableRef would be possible, but would also lead to several new +/// problems. One problem is the risk of a Mixed instance outliving a +/// stack allocated Table instance that it references. This would be a +/// fatal error. Another problem is the impact that the nontrivial +/// table reference has on the performance of copying and destroying +/// Mixed instances. +/// +/// \sa StringData +class Mixed { +public: + Mixed() noexcept; + + Mixed(bool) noexcept; + Mixed(int64_t) noexcept; + Mixed(float) noexcept; + Mixed(double) noexcept; + Mixed(StringData) noexcept; + Mixed(BinaryData) noexcept; + Mixed(OldDateTime) noexcept; + Mixed(Timestamp) noexcept; + + // These are shortcuts for Mixed(StringData(c_str)), and are + // needed to avoid unwanted implicit conversion of char* to bool. + Mixed(char* c_str) noexcept + { + set_string(c_str); + } + Mixed(const char* c_str) noexcept + { + set_string(c_str); + } + + struct subtable_tag { + }; + Mixed(subtable_tag) noexcept + : m_type(type_Table) + { + } + + ~Mixed() noexcept + { + } + + DataType get_type() const noexcept + { + return m_type; + } + + int64_t get_int() const noexcept; + bool get_bool() const noexcept; + float get_float() const noexcept; + double get_double() const noexcept; + StringData get_string() const noexcept; + BinaryData get_binary() const noexcept; + OldDateTime get_olddatetime() const noexcept; + Timestamp get_timestamp() const noexcept; + + void set_int(int64_t) noexcept; + void set_bool(bool) noexcept; + void set_float(float) noexcept; + void set_double(double) noexcept; + void set_string(StringData) noexcept; + void set_binary(BinaryData) noexcept; + void set_binary(const char* data, size_t size) noexcept; + void set_olddatetime(OldDateTime) noexcept; + void set_timestamp(Timestamp) noexcept; + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const Mixed&); + +private: + DataType m_type; + union { + int64_t m_int; + bool m_bool; + float m_float; + double m_double; + const char* m_data; + int_fast64_t m_date; + Timestamp m_timestamp; + }; + size_t m_size = 0; +}; + +// Note: We cannot compare two mixed values, since when the type of +// both is type_Table, we would have to compare the two tables, but +// the mixed values do not provide access to those tables. + +// Note: The mixed values are specified as Wrap. If they were +// not, these operators would apply to simple comparisons, such as int +// vs int64_t, and cause ambiguity. This is because the constructors +// of Mixed are not explicit. + +// Compare mixed with integer +template +bool operator==(Wrap, const T&) noexcept; +template +bool operator!=(Wrap, const T&) noexcept; +template +bool operator==(const T&, Wrap) noexcept; +template +bool operator!=(const T&, Wrap) noexcept; + +// Compare mixed with boolean +bool operator==(Wrap, bool) noexcept; +bool operator!=(Wrap, bool) noexcept; +bool operator==(bool, Wrap) noexcept; +bool operator!=(bool, Wrap) noexcept; + +// Compare mixed with float +bool operator==(Wrap, float); +bool operator!=(Wrap, float); +bool operator==(float, Wrap); +bool operator!=(float, Wrap); + +// Compare mixed with double +bool operator==(Wrap, double); +bool operator!=(Wrap, double); +bool operator==(double, Wrap); +bool operator!=(double, Wrap); + +// Compare mixed with string +bool operator==(Wrap, StringData) noexcept; +bool operator!=(Wrap, StringData) noexcept; +bool operator==(StringData, Wrap) noexcept; +bool operator!=(StringData, Wrap) noexcept; +bool operator==(Wrap, const char* c_str) noexcept; +bool operator!=(Wrap, const char* c_str) noexcept; +bool operator==(const char* c_str, Wrap) noexcept; +bool operator!=(const char* c_str, Wrap) noexcept; +bool operator==(Wrap, char* c_str) noexcept; +bool operator!=(Wrap, char* c_str) noexcept; +bool operator==(char* c_str, Wrap) noexcept; +bool operator!=(char* c_str, Wrap) noexcept; + +// Compare mixed with binary data +bool operator==(Wrap, BinaryData) noexcept; +bool operator!=(Wrap, BinaryData) noexcept; +bool operator==(BinaryData, Wrap) noexcept; +bool operator!=(BinaryData, Wrap) noexcept; + +// Compare mixed with date +bool operator==(Wrap, OldDateTime) noexcept; +bool operator!=(Wrap, OldDateTime) noexcept; +bool operator==(OldDateTime, Wrap) noexcept; +bool operator!=(OldDateTime, Wrap) noexcept; + +// Compare mixed with Timestamp +bool operator==(Wrap, Timestamp) noexcept; +bool operator!=(Wrap, Timestamp) noexcept; +bool operator==(Timestamp, Wrap) noexcept; +bool operator!=(Timestamp, Wrap) noexcept; + +// Implementation: + +inline Mixed::Mixed() noexcept +{ + m_type = type_Int; + m_int = 0; +} + +inline Mixed::Mixed(int64_t v) noexcept +{ + m_type = type_Int; + m_int = v; +} + +inline Mixed::Mixed(bool v) noexcept +{ + m_type = type_Bool; + m_bool = v; +} + +inline Mixed::Mixed(float v) noexcept +{ + m_type = type_Float; + m_float = v; +} + +inline Mixed::Mixed(double v) noexcept +{ + m_type = type_Double; + m_double = v; +} + +inline Mixed::Mixed(StringData v) noexcept +{ + m_type = type_String; + m_data = v.data(); + m_size = v.size(); +} + +inline Mixed::Mixed(BinaryData v) noexcept +{ + m_type = type_Binary; + m_data = v.data(); + m_size = v.size(); +} + +inline Mixed::Mixed(OldDateTime v) noexcept +{ + m_type = type_OldDateTime; + m_date = v.get_olddatetime(); +} + +inline Mixed::Mixed(Timestamp v) noexcept +{ + m_type = type_Timestamp; + m_timestamp = v; +} + +inline int64_t Mixed::get_int() const noexcept +{ + REALM_ASSERT(m_type == type_Int); + return m_int; +} + +inline bool Mixed::get_bool() const noexcept +{ + REALM_ASSERT(m_type == type_Bool); + return m_bool; +} + +inline float Mixed::get_float() const noexcept +{ + REALM_ASSERT(m_type == type_Float); + return m_float; +} + +inline double Mixed::get_double() const noexcept +{ + REALM_ASSERT(m_type == type_Double); + return m_double; +} + +inline StringData Mixed::get_string() const noexcept +{ + REALM_ASSERT(m_type == type_String); + return StringData(m_data, m_size); +} + +inline BinaryData Mixed::get_binary() const noexcept +{ + REALM_ASSERT(m_type == type_Binary); + return BinaryData(m_data, m_size); +} + +inline OldDateTime Mixed::get_olddatetime() const noexcept +{ + REALM_ASSERT(m_type == type_OldDateTime); + return m_date; +} + +inline Timestamp Mixed::get_timestamp() const noexcept +{ + REALM_ASSERT(m_type == type_Timestamp); + return m_timestamp; +} + +inline void Mixed::set_int(int64_t v) noexcept +{ + m_type = type_Int; + m_int = v; +} + +inline void Mixed::set_bool(bool v) noexcept +{ + m_type = type_Bool; + m_bool = v; +} + +inline void Mixed::set_float(float v) noexcept +{ + m_type = type_Float; + m_float = v; +} + +inline void Mixed::set_double(double v) noexcept +{ + m_type = type_Double; + m_double = v; +} + +inline void Mixed::set_string(StringData v) noexcept +{ + m_type = type_String; + m_data = v.data(); + m_size = v.size(); +} + +inline void Mixed::set_binary(BinaryData v) noexcept +{ + set_binary(v.data(), v.size()); +} + +inline void Mixed::set_binary(const char* data, size_t size) noexcept +{ + m_type = type_Binary; + m_data = data; + m_size = size; +} + +inline void Mixed::set_olddatetime(OldDateTime v) noexcept +{ + m_type = type_OldDateTime; + m_date = v.get_olddatetime(); +} + +inline void Mixed::set_timestamp(Timestamp v) noexcept +{ + m_type = type_Timestamp; + m_timestamp = v; +} + +// LCOV_EXCL_START +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const Mixed& m) +{ + out << "Mixed("; + switch (m.m_type) { + case type_Int: + out << m.m_int; + break; + case type_Bool: + out << m.m_bool; + break; + case type_Float: + out << m.m_float; + break; + case type_Double: + out << m.m_double; + break; + case type_String: + out << StringData(m.m_data, m.m_size); + break; + case type_Binary: + out << BinaryData(m.m_data, m.m_size); + break; + case type_OldDateTime: + out << OldDateTime(m.m_date); + break; + case type_Timestamp: + out << Timestamp(m.m_timestamp); + break; + case type_Table: + out << "subtable"; + break; + case type_Mixed: + case type_Link: + case type_LinkList: + REALM_ASSERT(false); + } + out << ")"; + return out; +} +// LCOV_EXCL_STOP + + +// Compare mixed with integer + +template +inline bool operator==(Wrap a, const T& b) noexcept +{ + return Mixed(a).get_type() == type_Int && Mixed(a).get_int() == b; +} + +template +inline bool operator!=(Wrap a, const T& b) noexcept +{ + return Mixed(a).get_type() != type_Int || Mixed(a).get_int() != b; +} + +template +inline bool operator==(const T& a, Wrap b) noexcept +{ + return type_Int == Mixed(b).get_type() && a == Mixed(b).get_int(); +} + +template +inline bool operator!=(const T& a, Wrap b) noexcept +{ + return type_Int != Mixed(b).get_type() || a != Mixed(b).get_int(); +} + + +// Compare mixed with boolean + +inline bool operator==(Wrap a, bool b) noexcept +{ + return Mixed(a).get_type() == type_Bool && Mixed(a).get_bool() == b; +} + +inline bool operator!=(Wrap a, bool b) noexcept +{ + return Mixed(a).get_type() != type_Bool || Mixed(a).get_bool() != b; +} + +inline bool operator==(bool a, Wrap b) noexcept +{ + return type_Bool == Mixed(b).get_type() && a == Mixed(b).get_bool(); +} + +inline bool operator!=(bool a, Wrap b) noexcept +{ + return type_Bool != Mixed(b).get_type() || a != Mixed(b).get_bool(); +} + + +// Compare mixed with float + +inline bool operator==(Wrap a, float b) +{ + return Mixed(a).get_type() == type_Float && Mixed(a).get_float() == b; +} + +inline bool operator!=(Wrap a, float b) +{ + return Mixed(a).get_type() != type_Float || Mixed(a).get_float() != b; +} + +inline bool operator==(float a, Wrap b) +{ + return type_Float == Mixed(b).get_type() && a == Mixed(b).get_float(); +} + +inline bool operator!=(float a, Wrap b) +{ + return type_Float != Mixed(b).get_type() || a != Mixed(b).get_float(); +} + + +// Compare mixed with double + +inline bool operator==(Wrap a, double b) +{ + return Mixed(a).get_type() == type_Double && Mixed(a).get_double() == b; +} + +inline bool operator!=(Wrap a, double b) +{ + return Mixed(a).get_type() != type_Double || Mixed(a).get_double() != b; +} + +inline bool operator==(double a, Wrap b) +{ + return type_Double == Mixed(b).get_type() && a == Mixed(b).get_double(); +} + +inline bool operator!=(double a, Wrap b) +{ + return type_Double != Mixed(b).get_type() || a != Mixed(b).get_double(); +} + + +// Compare mixed with string + +inline bool operator==(Wrap a, StringData b) noexcept +{ + return Mixed(a).get_type() == type_String && Mixed(a).get_string() == b; +} + +inline bool operator!=(Wrap a, StringData b) noexcept +{ + return Mixed(a).get_type() != type_String || Mixed(a).get_string() != b; +} + +inline bool operator==(StringData a, Wrap b) noexcept +{ + return type_String == Mixed(b).get_type() && a == Mixed(b).get_string(); +} + +inline bool operator!=(StringData a, Wrap b) noexcept +{ + return type_String != Mixed(b).get_type() || a != Mixed(b).get_string(); +} + +inline bool operator==(Wrap a, const char* b) noexcept +{ + return a == StringData(b); +} + +inline bool operator!=(Wrap a, const char* b) noexcept +{ + return a != StringData(b); +} + +inline bool operator==(const char* a, Wrap b) noexcept +{ + return StringData(a) == b; +} + +inline bool operator!=(const char* a, Wrap b) noexcept +{ + return StringData(a) != b; +} + +inline bool operator==(Wrap a, char* b) noexcept +{ + return a == StringData(b); +} + +inline bool operator!=(Wrap a, char* b) noexcept +{ + return a != StringData(b); +} + +inline bool operator==(char* a, Wrap b) noexcept +{ + return StringData(a) == b; +} + +inline bool operator!=(char* a, Wrap b) noexcept +{ + return StringData(a) != b; +} + + +// Compare mixed with binary data + +inline bool operator==(Wrap a, BinaryData b) noexcept +{ + return Mixed(a).get_type() == type_Binary && Mixed(a).get_binary() == b; +} + +inline bool operator!=(Wrap a, BinaryData b) noexcept +{ + return Mixed(a).get_type() != type_Binary || Mixed(a).get_binary() != b; +} + +inline bool operator==(BinaryData a, Wrap b) noexcept +{ + return type_Binary == Mixed(b).get_type() && a == Mixed(b).get_binary(); +} + +inline bool operator!=(BinaryData a, Wrap b) noexcept +{ + return type_Binary != Mixed(b).get_type() || a != Mixed(b).get_binary(); +} + + +// Compare mixed with date + +inline bool operator==(Wrap a, OldDateTime b) noexcept +{ + return Mixed(a).get_type() == type_OldDateTime && OldDateTime(Mixed(a).get_olddatetime()) == b; +} + +inline bool operator!=(Wrap a, OldDateTime b) noexcept +{ + return Mixed(a).get_type() != type_OldDateTime || OldDateTime(Mixed(a).get_olddatetime()) != b; +} + +inline bool operator==(OldDateTime a, Wrap b) noexcept +{ + return type_OldDateTime == Mixed(b).get_type() && a == OldDateTime(Mixed(b).get_olddatetime()); +} + +inline bool operator!=(OldDateTime a, Wrap b) noexcept +{ + return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime()); +} + +// Compare mixed with Timestamp + +inline bool operator==(Wrap a, Timestamp b) noexcept +{ + return Mixed(a).get_type() == type_Timestamp && Timestamp(Mixed(a).get_timestamp()) == b; +} + +inline bool operator!=(Wrap a, Timestamp b) noexcept +{ + return Mixed(a).get_type() != type_Timestamp || Timestamp(Mixed(a).get_timestamp()) != b; +} + +inline bool operator==(Timestamp a, Wrap b) noexcept +{ + return type_Timestamp == Mixed(b).get_type() && a == Timestamp(Mixed(b).get_timestamp()); +} + +inline bool operator!=(Timestamp a, Wrap b) noexcept +{ + return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp()); +} + + +} // namespace realm + +#endif // REALM_MIXED_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/null.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/null.hpp new file mode 100644 index 0000000..6c2b713 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/null.hpp @@ -0,0 +1,165 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_NULL_HPP +#define REALM_NULL_HPP + +#include +#include + +#include +#include +#include +#include + +namespace realm { + +/* +Represents null in Query, find(), get(), set(), etc. + +Float/Double: Realm can both store user-given NaNs and null. Any user-given signaling NaN is converted to +0x7fa00000 (if float) or 0x7ff4000000000000 (if double). Any user-given quiet NaN is converted to +0x7fc00000 (if float) or 0x7ff8000000000000 (if double). So Realm does not preserve the optional bits in +user-given NaNs. + +However, since both clang and gcc on x64 and ARM, and also Java on x64, return these bit patterns when +requesting NaNs, these will actually seem to roundtrip bit-exact for the end-user in most cases. + +If set_null() is called, a null is stored in form of the bit pattern 0xffffffff (if float) or +0xffffffffffffffff (if double). These are quiet NaNs. + +Executing a query that involves a float/double column that contains NaNs gives an undefined result. If +it contains signaling NaNs, it may throw an exception. + +Notes on IEEE: + +A NaN float is any bit pattern `s 11111111 S xxxxxxxxxxxxxxxxxxxxxx` where `s` and `x` are arbitrary, but at +least 1 `x` must be 1. If `S` is 1, it's a quiet NaN, else it's a signaling NaN. + +A NaN doubule is the same as above, but for `s eeeeeeeeeee S xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` + +The `S` bit is at position 22 (float) or 51 (double). +*/ + +struct null { + null() + { + } + operator int64_t() + { + throw(LogicError::type_mismatch); + } + template + operator util::Optional() + { + return util::none; + } + + template + bool operator==(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator!=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator>(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator>=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator<=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator<(const T&) const + { + REALM_ASSERT(false); + return false; + } + + /// Returns whether `v` bitwise equals the null bit-pattern + template + static bool is_null_float(T v) + { + T i = null::get_null_float(); + return std::memcmp(&i, &v, sizeof(T)) == 0; + } + + /// Returns the quiet NaNs that represent null for floats/doubles in Realm in stored payload. + template + static T get_null_float() + { + typename std::conditional::value, uint32_t, uint64_t>::type i; + int64_t double_nan = 0x7ff80000000000aa; + i = std::is_same::value ? 0x7fc000aa : static_cast(double_nan); + T d = type_punning(i); + REALM_ASSERT_DEBUG(std::isnan(d)); + REALM_ASSERT_DEBUG(!is_signaling(d)); + return d; + } + + /// Takes a NaN as argument and returns whether or not it's signaling + template + static bool is_signaling(T v) + { + REALM_ASSERT(std::isnan(static_cast(v))); + typename std::conditional::value, uint32_t, uint64_t>::type i; + size_t signal_bit = std::is_same::value ? 22 : 51; // If this bit is set, it's quiet + i = type_punning(v); + return !(i & (1ull << signal_bit)); + } + + /// Converts any signaling or quiet NaN to their their respective bit patterns that are used on x64 gcc+clang, + /// ARM clang and x64 Java. + template + static T to_realm(T v) + { + if (std::isnan(static_cast(v))) { + typename std::conditional::value, uint32_t, uint64_t>::type i; + if (std::is_same::value) { + i = is_signaling(v) ? 0x7fa00000 : 0x7fc00000; + } + else { + i = static_cast(is_signaling(v) ? 0x7ff4000000000000 : 0x7ff8000000000000); + } + return type_punning(i); + } + else { + return v; + } + } +}; + +} // namespace realm + +#endif // REALM_NULL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/olddatetime.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/olddatetime.hpp new file mode 100644 index 0000000..b662899 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/olddatetime.hpp @@ -0,0 +1,157 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_DATETIME_HPP +#define REALM_DATETIME_HPP + +#include +#include + +namespace realm { + + +class OldDateTime { +public: + OldDateTime() noexcept + : m_time(0) + { + } + + /// Construct from the number of seconds since Jan 1 00:00:00 UTC + /// 1970. + /// FIXME: See if we can make this private again. Required by query_expression.hpp + OldDateTime(int_fast64_t d) noexcept + : m_time(d) + { + } + + /// Return the time as seconds since Jan 1 00:00:00 UTC 1970. + int_fast64_t get_olddatetime() const noexcept + { + return m_time; + } + + friend bool operator==(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator!=(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator<(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator<=(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator>(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator>=(const OldDateTime&, const OldDateTime&) noexcept; + + /// Construct from broken down local time. + /// + /// \note This constructor uses std::mktime() to convert the + /// specified local time to seconds since the Epoch, that is, the + /// result depends on the current globally specified time zone + /// setting. + /// + /// \param year The year (the minimum valid value is 1970). + /// + /// \param month The month in the range [1, 12]. + /// + /// \param day The day of the month in the range [1, 31]. + /// + /// \param hours Hours since midnight in the range [0, 23]. + /// + /// \param minutes Minutes after the hour in the range [0, 59]. + /// + /// \param seconds Seconds after the minute in the range [0, + /// 60]. Note that the range allows for leap seconds. + OldDateTime(int year, int month, int day, int hours = 0, int minutes = 0, int seconds = 0); + + template + friend std::basic_ostream& operator<<(std::basic_ostream& out, const OldDateTime&); + + // This is used by query_expression.hpp to generalize its templates and simplify the code *alot*; it is needed + // because OldDateTime is internally stored in an int64_t column. + operator int_fast64_t() noexcept; + +private: + int_fast64_t m_time; // Seconds since Jan 1 00:00:00 UTC 1970. + static std::time_t assemble(int year, int month, int day, int hours, int minutes, int seconds); + template + friend class Value; +}; + + +// Implementation: + +inline bool operator==(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time == b.m_time; +} + +inline bool operator!=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time != b.m_time; +} + +inline bool operator<(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time < b.m_time; +} + +inline bool operator<=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time <= b.m_time; +} + +inline bool operator>(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time > b.m_time; +} + +inline bool operator>=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time >= b.m_time; +} + +inline OldDateTime::operator int_fast64_t() noexcept +{ + return m_time; +} + +inline OldDateTime::OldDateTime(int year, int month, int day, int hours, int minutes, int seconds) + : m_time(assemble(year, month, day, hours, minutes, seconds)) +{ +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const OldDateTime& d) +{ + out << "OldDateTime(" << d.m_time << ")"; + return out; +} + +inline std::time_t OldDateTime::assemble(int year, int month, int day, int hours, int minutes, int seconds) +{ + std::tm local_time; + local_time.tm_year = year - 1900; + local_time.tm_mon = month - 1; + local_time.tm_mday = day; + local_time.tm_hour = hours; + local_time.tm_min = minutes; + local_time.tm_sec = seconds; + local_time.tm_isdst = -1; + return std::mktime(&local_time); +} + + +} // namespace realm + +#endif // REALM_DATETIME_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/owned_data.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/owned_data.hpp new file mode 100644 index 0000000..c707f9d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/owned_data.hpp @@ -0,0 +1,96 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_OWNED_DATA_HPP +#define REALM_OWNED_DATA_HPP + +#include + +#include +#include + +namespace realm { + +/// A chunk of owned data. +class OwnedData { +public: + /// Construct a null reference. + OwnedData() noexcept + { + } + + /// If \a data_to_copy is 'null', \a data_size must be zero. + OwnedData(const char* data_to_copy, size_t data_size) + : m_size(data_size) + { + REALM_ASSERT_DEBUG(data_to_copy || data_size == 0); + if (data_to_copy) { + m_data = std::unique_ptr(new char[data_size]); + memcpy(m_data.get(), data_to_copy, data_size); + } + } + + /// If \a unique_data is 'null', \a data_size must be zero. + OwnedData(std::unique_ptr unique_data, size_t data_size) noexcept + : m_data(std::move(unique_data)) + , m_size(data_size) + { + REALM_ASSERT_DEBUG(m_data || m_size == 0); + } + + OwnedData(const OwnedData& other) + : OwnedData(other.m_data.get(), other.m_size) + { + } + OwnedData& operator=(const OwnedData& other); + + OwnedData(OwnedData&&) = default; + OwnedData& operator=(OwnedData&&) = default; + + const char* data() const + { + return m_data.get(); + } + size_t size() const + { + return m_size; + } + +private: + std::unique_ptr m_data; + size_t m_size = 0; +}; + +inline OwnedData& OwnedData::operator=(const OwnedData& other) +{ + if (this != &other) { + if (other.m_data) { + m_data = std::unique_ptr(new char[other.m_size]); + memcpy(m_data.get(), other.m_data.get(), other.m_size); + } + else { + m_data = nullptr; + } + m_size = other.m_size; + } + return *this; +} + +} // namespace realm + +#endif // REALM_OWNED_DATA_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/query.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query.hpp new file mode 100644 index 0000000..611fd97 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query.hpp @@ -0,0 +1,468 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_QUERY_HPP +#define REALM_QUERY_HPP + +#include +#include +#include +#include +#include +#include + +#define REALM_MULTITHREAD_QUERY 0 + +#if REALM_MULTITHREAD_QUERY +// FIXME: Use our C++ thread abstraction API since it provides a much +// higher level of encapsulation and safety. +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +// Pre-declarations +class ParentNode; +class Table; +class TableView; +class TableViewBase; +class ConstTableView; +class Array; +class Expression; +class SequentialGetterBase; +class Group; + +struct QueryGroup { + enum class State { + Default, + OrCondition, + OrConditionChildren, + }; + + QueryGroup() = default; + + QueryGroup(const QueryGroup&); + QueryGroup& operator=(const QueryGroup&); + + QueryGroup(QueryGroup&&) = default; + QueryGroup& operator=(QueryGroup&&) = default; + + QueryGroup(const QueryGroup&, QueryNodeHandoverPatches&); + + std::unique_ptr m_root_node; + + bool m_pending_not = false; + size_t m_subtable_column = not_found; + State m_state = State::Default; +}; + +class Query final { +public: + Query(const Table& table, TableViewBase* tv = nullptr); + Query(const Table& table, std::unique_ptr); + Query(const Table& table, const LinkViewRef& lv); + Query(); + Query(std::unique_ptr); + ~Query() noexcept; + + Query(const Query& copy); + Query& operator=(const Query& source); + + Query(Query&&); + Query& operator=(Query&&); + + // Find links that point to a specific target row + Query& links_to(size_t column_ndx, const ConstRow& target_row); + + // Conditions: null + Query& equal(size_t column_ndx, null); + Query& not_equal(size_t column_ndx, null); + + // Conditions: int64_t + Query& equal(size_t column_ndx, int64_t value); + Query& not_equal(size_t column_ndx, int64_t value); + Query& greater(size_t column_ndx, int64_t value); + Query& greater_equal(size_t column_ndx, int64_t value); + Query& less(size_t column_ndx, int64_t value); + Query& less_equal(size_t column_ndx, int64_t value); + Query& between(size_t column_ndx, int64_t from, int64_t to); + + // Conditions: int (we need those because conversion from '1234' is ambiguous with float/double) + Query& equal(size_t column_ndx, int value); + Query& not_equal(size_t column_ndx, int value); + Query& greater(size_t column_ndx, int value); + Query& greater_equal(size_t column_ndx, int value); + Query& less(size_t column_ndx, int value); + Query& less_equal(size_t column_ndx, int value); + Query& between(size_t column_ndx, int from, int to); + + // Conditions: 2 int columns + Query& equal_int(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_int(size_t column_ndx1, size_t column_ndx2); + Query& greater_int(size_t column_ndx1, size_t column_ndx2); + Query& less_int(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_int(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_int(size_t column_ndx1, size_t column_ndx2); + + // Conditions: float + Query& equal(size_t column_ndx, float value); + Query& not_equal(size_t column_ndx, float value); + Query& greater(size_t column_ndx, float value); + Query& greater_equal(size_t column_ndx, float value); + Query& less(size_t column_ndx, float value); + Query& less_equal(size_t column_ndx, float value); + Query& between(size_t column_ndx, float from, float to); + + // Conditions: 2 float columns + Query& equal_float(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_float(size_t column_ndx1, size_t column_ndx2); + Query& greater_float(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_float(size_t column_ndx1, size_t column_ndx2); + Query& less_float(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_float(size_t column_ndx1, size_t column_ndx2); + + // Conditions: double + Query& equal(size_t column_ndx, double value); + Query& not_equal(size_t column_ndx, double value); + Query& greater(size_t column_ndx, double value); + Query& greater_equal(size_t column_ndx, double value); + Query& less(size_t column_ndx, double value); + Query& less_equal(size_t column_ndx, double value); + Query& between(size_t column_ndx, double from, double to); + + // Conditions: 2 double columns + Query& equal_double(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_double(size_t column_ndx1, size_t column_ndx2); + Query& greater_double(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_double(size_t column_ndx1, size_t column_ndx2); + Query& less_double(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_double(size_t column_ndx1, size_t column_ndx2); + + // Conditions: timestamp + Query& equal(size_t column_ndx, Timestamp value); + Query& not_equal(size_t column_ndx, Timestamp value); + Query& greater(size_t column_ndx, Timestamp value); + Query& greater_equal(size_t column_ndx, Timestamp value); + Query& less_equal(size_t column_ndx, Timestamp value); + Query& less(size_t column_ndx, Timestamp value); + + // Conditions: size + Query& size_equal(size_t column_ndx, int64_t value); + Query& size_not_equal(size_t column_ndx, int64_t value); + Query& size_greater(size_t column_ndx, int64_t value); + Query& size_greater_equal(size_t column_ndx, int64_t value); + Query& size_less_equal(size_t column_ndx, int64_t value); + Query& size_less(size_t column_ndx, int64_t value); + Query& size_between(size_t column_ndx, int64_t from, int64_t to); + + // Conditions: bool + Query& equal(size_t column_ndx, bool value); + + // Conditions: date + Query& equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& not_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return not_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& greater_olddatetime(size_t column_ndx, OldDateTime value) + { + return greater(column_ndx, int64_t(value.get_olddatetime())); + } + Query& greater_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return greater_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& less_olddatetime(size_t column_ndx, OldDateTime value) + { + return less(column_ndx, int64_t(value.get_olddatetime())); + } + Query& less_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return less_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& between_olddatetime(size_t column_ndx, OldDateTime from, OldDateTime to) + { + return between(column_ndx, int64_t(from.get_olddatetime()), int64_t(to.get_olddatetime())); + } + + // Conditions: strings + Query& equal(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& not_equal(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& begins_with(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& ends_with(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& contains(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& like(size_t column_ndx, StringData value, bool case_sensitive = true); + + // These are shortcuts for equal(StringData(c_str)) and + // not_equal(StringData(c_str)), and are needed to avoid unwanted + // implicit conversion of char* to bool. + Query& equal(size_t column_ndx, const char* c_str, bool case_sensitive = true); + Query& not_equal(size_t column_ndx, const char* c_str, bool case_sensitive = true); + + // Conditions: binary data + Query& equal(size_t column_ndx, BinaryData value); + Query& not_equal(size_t column_ndx, BinaryData value); + Query& begins_with(size_t column_ndx, BinaryData value); + Query& ends_with(size_t column_ndx, BinaryData value); + Query& contains(size_t column_ndx, BinaryData value); + + // Negation + Query& Not(); + + // Grouping + Query& group(); + Query& end_group(); + Query& subtable(size_t column); + Query& end_subtable(); + Query& Or(); + + Query& and_query(const Query& q); + Query& and_query(Query&& q); + Query operator||(const Query& q); + Query operator&&(const Query& q); + Query operator!(); + + + // Searching + size_t find(size_t begin_at_table_row = size_t(0)); + TableView find_all(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)); + ConstTableView find_all(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + + // Aggregates + size_t count(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + + int64_t sum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + int64_t maximum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + int64_t minimum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double sum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + float maximum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + float minimum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double sum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double maximum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double minimum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + OldDateTime maximum_olddatetime(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, + size_t end = size_t(-1), size_t limit = size_t(-1), + size_t* return_ndx = nullptr) const; + + OldDateTime minimum_olddatetime(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, + size_t end = size_t(-1), size_t limit = size_t(-1), + size_t* return_ndx = nullptr) const; + + Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)); + + Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)); + + // Deletion + size_t remove(); + +#if REALM_MULTITHREAD_QUERY + // Multi-threading + TableView find_all_multi(size_t start = 0, size_t end = size_t(-1)); + ConstTableView find_all_multi(size_t start = 0, size_t end = size_t(-1)) const; + int set_threads(unsigned int threadcount); +#endif + + const TableRef& get_table() + { + return m_table; + } + + // True if matching rows are guaranteed to be returned in table order. + bool produces_results_in_table_order() const + { + return !m_view; + } + + // Calls sync_if_needed on the restricting view, if present. + // Returns the current version of the table(s) this query depends on, + // or util::none if the query is not associated with a table. + util::Optional sync_view_if_needed() const; + + std::string validate(); + +private: + Query(Table& table, TableViewBase* tv = nullptr); + void create(); + + void init() const; + size_t find_internal(size_t start = 0, size_t end = size_t(-1)) const; + size_t peek_tablerow(size_t row) const; + void handle_pending_not(); + void set_table(TableRef tr); + +public: + using HandoverPatch = QueryHandoverPatch; + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, ConstSourcePayload mode) const + { + patch.reset(new HandoverPatch); + return std::make_unique(*this, *patch, mode); + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, MutableSourcePayload mode) + { + patch.reset(new HandoverPatch); + return std::make_unique(*this, *patch, mode); + } + + void apply_and_consume_patch(std::unique_ptr& patch, Group& dest_group) + { + apply_patch(*patch, dest_group); + patch.reset(); + } + + void apply_patch(HandoverPatch& patch, Group& dest_group); + Query(const Query& source, HandoverPatch& patch, ConstSourcePayload mode); + Query(Query& source, HandoverPatch& patch, MutableSourcePayload mode); + +private: + void fetch_descriptor(); + + void add_expression_node(std::unique_ptr); + + template + Query& equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& less(size_t column_ndx1, size_t column_ndx2); + + template + Query& less_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& greater(size_t column_ndx1, size_t column_ndx2); + + template + Query& greater_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& not_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& add_condition(size_t column_ndx, T value); + + template + Query& add_size_condition(size_t column_ndx, int64_t value); + + template + double average(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + template + R aggregate(R (ColClass::*method)(size_t, size_t, size_t, size_t*) const, size_t column_ndx, size_t* resultcount, + size_t start, size_t end, size_t limit, size_t* return_ndx = nullptr) const; + + void aggregate_internal(Action TAction, DataType TSourceColumn, bool nullable, ParentNode* pn, QueryStateBase* st, + size_t start, size_t end, SequentialGetterBase* source_column) const; + + void find_all(TableViewBase& tv, size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + void delete_nodes() noexcept; + + bool has_conditions() const + { + return m_groups.size() > 0 && m_groups[0].m_root_node; + } + ParentNode* root_node() const + { + REALM_ASSERT(m_groups.size()); + return m_groups[0].m_root_node.get(); + } + + void add_node(std::unique_ptr); + + friend class Table; + friend class TableViewBase; + + std::string error_code; + + std::vector m_groups; + + // Used to access schema while building query: + std::vector m_subtable_path; + + ConstDescriptorRef m_current_descriptor; + TableRef m_table; + + // points to the base class of the restricting view. If the restricting + // view is a link view, m_source_link_view is non-zero. If it is a table view, + // m_source_table_view is non-zero. + RowIndexes* m_view = nullptr; + + // At most one of these can be non-zero, and if so the non-zero one indicates the restricting view. + LinkViewRef m_source_link_view; // link views are refcounted and shared. + TableViewBase* m_source_table_view = nullptr; // table views are not refcounted, and not owned by the query. + std::unique_ptr m_owned_source_table_view; // <--- except when indicated here +}; + +// Implementation: + +inline Query& Query::equal(size_t column_ndx, const char* c_str, bool case_sensitive) +{ + return equal(column_ndx, StringData(c_str), case_sensitive); +} + +inline Query& Query::not_equal(size_t column_ndx, const char* c_str, bool case_sensitive) +{ + return not_equal(column_ndx, StringData(c_str), case_sensitive); +} + +} // namespace realm + +#endif // REALM_QUERY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_conditions.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_conditions.hpp new file mode 100644 index 0000000..d7d571c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_conditions.hpp @@ -0,0 +1,715 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_QUERY_CONDITIONS_HPP +#define REALM_QUERY_CONDITIONS_HPP + +#include +#include + +#include +#include +#include + +namespace realm { + +// Array::VTable only uses the first 4 conditions (enums) in an array of function pointers +enum { cond_Equal, cond_NotEqual, cond_Greater, cond_Less, cond_VTABLE_FINDER_COUNT, cond_None, cond_LeftNotNull }; + +// Quick hack to make "Queries with Integer null columns" able to compile in Visual Studio 2015 which doesn't full +// support sfinae +// (real cause hasn't been investigated yet, cannot exclude that we don't obey c++11 standard) +struct HackClass { + template + bool can_match(A, B, C) + { + REALM_ASSERT(false); + return false; + } + template + bool will_match(A, B, C) + { + REALM_ASSERT(false); + return false; + } +}; + +// Does v2 contain v1? +struct Contains : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + bool operator()(StringData v1, const std::array &charmap, StringData v2) const + { + return v2.contains(v1, charmap); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 contain something like v1 (wildcard matching)? +struct Like : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.like(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.like(v1); + } + bool operator()(BinaryData, BinaryData, bool = false, bool = false) const + { + REALM_ASSERT(false); + return false; + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 begin with v1? +struct BeginsWith : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 end with v1? +struct EndsWith : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct Equal { + static const int avx = 0x00; // _CMP_EQ_OQ + // bool operator()(const bool v1, const bool v2, bool v1null = false, bool v2null = false) const { return v1 == + // v2; } + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v1 == v2; + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v1 == v2; + } + + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + return (v1null && v2null) || (!v1null && !v2null && v1 == v2); + } + static const int condition = cond_Equal; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v >= lbound && v <= ubound); + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v == 0 && ubound == 0 && lbound == 0); + } +}; + +struct NotEqual { + static const int avx = 0x0B; // _CMP_FALSE_OQ + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v1 != v2; + } + // bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const { return v1 != v2; } + + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (!v1null && !v2null) + return v1 != v2; + + if (v1null && v2null) + return false; + + return true; + } + + static const int condition = cond_NotEqual; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + return !(v == 0 && ubound == 0 && lbound == 0); + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v > ubound || v < lbound); + } + + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } +}; + +// Does v2 contain v1? +struct ContainsIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() == 0 && !v2.is_null()) + return true; + + return search_case_fold(v2, v1_upper, v1_lower, v1.size()) != v2.size(); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() == 0 && !v2.is_null()) + return true; + + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return search_case_fold(v2, v1_upper.c_str(), v1_lower.c_str(), v1.size()) != v2.size(); + } + + // Case insensitive Boyer-Moore version + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, const std::array &charmap, StringData v2) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() == 0 && !v2.is_null()) + return true; + + return contains_ins(v2, v1_upper, v1_lower, v1.size(), charmap); + } + + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 contain something like v1 (wildcard matching)? +struct LikeIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() || v1.is_null()) { + return (v2.is_null() && v1.is_null()); + } + + return string_like_ins(v2, v1_lower, v1_upper); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() || v1.is_null()) { + return (v2.is_null() && v1.is_null()); + } + + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return string_like_ins(v2, v1_lower, v1_upper); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 begin with v1? +struct BeginsWithIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + return v1.size() <= v2.size() && equal_case_fold(v2.prefix(v1.size()), v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() > v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2.prefix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 end with v1? +struct EndsWithIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + return v1.size() <= v2.size() && equal_case_fold(v2.suffix(v1.size()), v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() > v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2.suffix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct EqualIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v1.is_null() != v2.is_null()) + return false; + + return v1.size() == v2.size() && equal_case_fold(v2, v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v1.is_null() != v2.is_null()) + return false; + + if (v1.size() != v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct NotEqualIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v1.is_null() != v2.is_null()) + return true; + return v1.size() != v2.size() || !equal_case_fold(v2, v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v1.is_null() != v2.is_null()) + return true; + + if (v1.size() != v2.size()) + return true; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return !equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct Greater { + static const int avx = 0x1E; // _CMP_GT_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null || v2null) + return false; + + return v1 > v2; + } + static const int condition = cond_Greater; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + return ubound > v; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(ubound); + return lbound > v; + } +}; + +struct None { + template + bool operator()(const T&, const T&, bool = false, bool = false) const + { + return true; + } + static const int condition = cond_None; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } +}; + +struct NotNull { + template + bool operator()(const T&, const T&, bool v = false, bool = false) const + { + return !v; + } + static const int condition = cond_LeftNotNull; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } +}; + + +struct Less { + static const int avx = 0x11; // _CMP_LT_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null || v2null) + return false; + + return v1 < v2; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = cond_Less; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(ubound); + return lbound < v; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + return ubound < v; + } +}; + +struct LessEqual : public HackClass { + static const int avx = 0x12; // _CMP_LE_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null && v2null) + return true; + + return (!v1null && !v2null && v1 <= v2); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = -1; +}; + +struct GreaterEqual : public HackClass { + static const int avx = 0x1D; // _CMP_GE_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null && v2null) + return true; + + return (!v1null && !v2null && v1 >= v2); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = -1; +}; + + +// CompareLess is a temporary hack to have a generalized way to compare any realm types. Todo, enable correct < +// operator of StringData (currently gives circular header dependency with utf8.hpp) +template +struct CompareLess { + static bool compare(T v1, T v2, bool = false, bool = false) + { + return v1 < v2; + } +}; +template <> +struct CompareLess { + static bool compare(StringData v1, StringData v2, bool = false, bool = false) + { + bool ret = utf8_compare(v1.data(), v2.data()); + return ret; + } +}; + +} // namespace realm + +#endif // REALM_QUERY_CONDITIONS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_engine.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_engine.hpp new file mode 100644 index 0000000..3ca7f0a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_engine.hpp @@ -0,0 +1,1962 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +/* +A query consists of node objects, one for each query condition. Each node contains pointers to all other nodes: + +node1 node2 node3 +------ ----- ----- +node2* node1* node1* +node3* node3* node2* + +The construction of all this takes part in query.cpp. Each node has two important functions: + + aggregate(start, end) + aggregate_local(start, end) + +The aggregate() function executes the aggregate of a query. You can call the method on any of the nodes +(except children nodes of OrNode and SubtableNode) - it has the same behaviour. The function contains +scheduling that calls aggregate_local(start, end) on different nodes with different start/end ranges, +depending on what it finds is most optimal. + +The aggregate_local() function contains a tight loop that tests the condition of its own node, and upon match +it tests all other conditions at that index to report a full match or not. It will remain in the tight loop +after a full match. + +So a call stack with 2 and 9 being local matches of a node could look like this: + +aggregate(0, 10) + node1->aggregate_local(0, 3) + node2->find_first_local(2, 3) + node3->find_first_local(2, 3) + node3->aggregate_local(3, 10) + node1->find_first_local(4, 5) + node2->find_first_local(4, 5) + node1->find_first_local(7, 8) + node2->find_first_local(7, 8) + +find_first_local(n, n + 1) is a function that can be used to test a single row of another condition. Note that +this is very simplified. There are other statistical arguments to the methods, and also, find_first_local() can be +called from a callback function called by an integer Array. + + +Template arguments in methods: +---------------------------------------------------------------------------------------------------- + +TConditionFunction: Each node has a condition from query_conditions.c such as Equal, GreaterEqual, etc + +TConditionValue: Type of values in condition column. That is, int64_t, float, int, bool, etc + +TAction: What to do with each search result, from the enums act_ReturnFirst, act_Count, act_Sum, etc + +TResult: Type of result of actions - float, double, int64_t, etc. Special notes: For act_Count it's + int64_t, for RLM_FIND_ALL it's int64_t which points at destination array. + +TSourceColumn: Type of source column used in actions, or *ignored* if no source column is used (like for + act_Count, act_ReturnFirst) + + +There are two important classes used in queries: +---------------------------------------------------------------------------------------------------- +SequentialGetter Column iterator used to get successive values with leaf caching. Used both for condition columns + and aggregate source column + +AggregateState State of the aggregate - contains a state variable that stores intermediate sum, max, min, + etc, etc. + +*/ + +#ifndef REALM_QUERY_ENGINE_HPP +#define REALM_QUERY_ENGINE_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if REALM_X86_OR_X64_TRUE && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 +#include +#endif + +namespace realm { + +// Number of matches to find in best condition loop before breaking out to probe other conditions. Too low value gives +// too many constant time overheads everywhere in the query engine. Too high value makes it adapt less rapidly to +// changes in match frequencies. +const size_t findlocals = 64; + +// Average match distance in linear searches where further increase in distance no longer increases query speed +// (because time spent on handling each match becomes insignificant compared to time spent on the search). +const size_t bestdist = 512; + +// Minimum number of matches required in a certain condition before it can be used to compute statistics. Too high +// value can spent too much time in a bad node (with high match frequency). Too low value gives inaccurate statistics. +const size_t probe_matches = 4; + +const size_t bitwidth_time_unit = 64; + +typedef bool (*CallbackDummy)(int64_t); + + +class ParentNode { + typedef ParentNode ThisType; + +public: + ParentNode() = default; + virtual ~ParentNode() = default; + + void gather_children(std::vector& v) + { + m_children.clear(); + size_t i = v.size(); + v.push_back(this); + + if (m_child) + m_child->gather_children(v); + + m_children = v; + m_children.erase(m_children.begin() + i); + m_children.insert(m_children.begin(), this); + } + + double cost() const + { + return 8 * bitwidth_time_unit / m_dD + + m_dT; // dt = 1/64 to 1. Match dist is 8 times more important than bitwidth + } + + size_t find_first(size_t start, size_t end); + + virtual void init() + { + // Verify that the cached column accessor is still valid + verify_column(); // throws + + if (m_child) + m_child->init(); + + m_column_action_specializer = nullptr; + } + + void set_table(const Table& table) + { + if (&table == m_table) + return; + + m_table.reset(&table); + if (m_child) + m_child->set_table(table); + table_changed(); + } + + virtual size_t find_first_local(size_t start, size_t end) = 0; + + virtual void aggregate_local_prepare(Action TAction, DataType col_id, bool nullable); + + template + bool column_action_specialization(QueryStateBase* st, SequentialGetterBase* source_column, size_t r) + { + // TResult: type of query result + // TSourceValue: type of aggregate source + using TSourceValue = typename TSourceColumn::value_type; + using TResult = typename ColumnTypeTraitsSum::sum_type; + + // Sum of float column must accumulate in double + static_assert(!(TAction == act_Sum && + (std::is_same::value && !std::is_same::value)), + ""); + + TSourceValue av{}; + // uses_val test because compiler cannot see that IntegerColumn::get has no side effect and result is + // discarded + if (static_cast*>(st)->template uses_val() && source_column != nullptr) { + REALM_ASSERT_DEBUG(dynamic_cast*>(source_column) != nullptr); + av = static_cast*>(source_column)->get_next(r); + } + REALM_ASSERT_DEBUG(dynamic_cast*>(st) != nullptr); + bool cont = static_cast*>(st)->template match(r, 0, av); + return cont; + } + + virtual size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column); + + + virtual std::string validate() + { + if (error_code != "") + return error_code; + if (m_child == nullptr) + return ""; + else + return m_child->validate(); + } + + ParentNode(const ParentNode& from) + : ParentNode(from, nullptr) + { + } + + ParentNode(const ParentNode& from, QueryNodeHandoverPatches* patches) + : m_child(from.m_child ? from.m_child->clone(patches) : nullptr) + , m_condition_column_idx(from.m_condition_column_idx) + , m_dD(from.m_dD) + , m_dT(from.m_dT) + , m_probes(from.m_probes) + , m_matches(from.m_matches) + , m_table(patches ? ConstTableRef{} : from.m_table) + { + } + + void add_child(std::unique_ptr child) + { + if (m_child) + m_child->add_child(std::move(child)); + else + m_child = std::move(child); + } + + virtual std::unique_ptr clone(QueryNodeHandoverPatches* = nullptr) const = 0; + + virtual void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) + { + if (m_child) + m_child->apply_handover_patch(patches, group); + } + + virtual void verify_column() const = 0; + + std::unique_ptr m_child; + std::vector m_children; + size_t m_condition_column_idx = npos; // Column of search criteria + + double m_dD; // Average row distance between each local match at current position + double m_dT = 0.0; // Time overhead of testing index i + 1 if we have just tested index i. > 1 for linear scans, 0 + // for index/tableview + + size_t m_probes = 0; + size_t m_matches = 0; + +protected: + typedef bool (ParentNode::*Column_action_specialized)(QueryStateBase*, SequentialGetterBase*, size_t); + Column_action_specialized m_column_action_specializer; + ConstTableRef m_table; + std::string error_code; + + const ColumnBase& get_column_base(size_t ndx) + { + return m_table->get_column_base(ndx); + } + + template + const ColType& get_column(size_t ndx) + { + auto& col = m_table->get_column_base(ndx); + REALM_ASSERT_DEBUG(dynamic_cast(&col)); + return static_cast(col); + } + + ColumnType get_real_column_type(size_t ndx) + { + return m_table->get_real_column_type(ndx); + } + + template + void copy_getter(SequentialGetter& dst, size_t& dst_idx, const SequentialGetter& src, + const QueryNodeHandoverPatches* patches) + { + if (src.m_column) { + if (patches) + dst_idx = src.m_column->get_column_index(); + else + dst.init(src.m_column); + } + } + + void do_verify_column(const ColumnBase* col, size_t col_ndx = npos) const + { + if (col_ndx == npos) + col_ndx = m_condition_column_idx; + if (m_table && col_ndx != npos) { + m_table->verify_column(col_ndx, col); + } + } + +private: + virtual void table_changed() = 0; +}; + +// For conditions on a subtable (encapsulated in subtable()...end_subtable()). These return the parent row as match if +// and only if one or more subtable rows match the condition. +class SubtableNode : public ParentNode { +public: + SubtableNode(size_t column, std::unique_ptr condition) + : m_condition(std::move(condition)) + { + m_dT = 100.0; + m_condition_column_idx = column; + } + + void init() override + { + ParentNode::init(); + + m_dD = 10.0; + + // m_condition is first node in condition of subtable query. + if (m_condition) { + // Can't call init() here as usual since the subtable can be degenerate + // m_condition->init(table); + std::vector v; + m_condition->gather_children(v); + } + } + + void table_changed() override + { + m_col_type = m_table->get_real_column_type(m_condition_column_idx); + REALM_ASSERT(m_col_type == col_type_Table || m_col_type == col_type_Mixed); + if (m_col_type == col_type_Table) + m_column = &m_table->get_column_table(m_condition_column_idx); + else // Mixed + m_column = &m_table->get_column_mixed(m_condition_column_idx); + } + + void verify_column() const override + { + if (m_table) + m_table->verify_column(m_condition_column_idx, m_column); + } + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_condition == nullptr) + return "Unbalanced subtable/end_subtable block"; + else + return m_condition->validate(); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(m_table); + REALM_ASSERT(m_condition); + + for (size_t s = start; s < end; ++s) { + const Table* subtable; + if (m_col_type == col_type_Table) + subtable = static_cast(m_column)->get_subtable_ptr(s); + else { + subtable = static_cast(m_column)->get_subtable_ptr(s); + if (!subtable) + continue; + } + + if (subtable->is_degenerate()) + return not_found; + + m_condition->set_table(*subtable); + m_condition->init(); + const size_t subsize = subtable->size(); + const size_t sub = m_condition->find_first(0, subsize); + + if (sub != not_found) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SubtableNode(*this, patches)); + } + + SubtableNode(const SubtableNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_condition(from.m_condition ? from.m_condition->clone(patches) : nullptr) + , m_column(from.m_column) + , m_col_type(from.m_col_type) + { + if (m_column && patches) + m_condition_column_idx = m_column->get_column_index(); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_condition->apply_handover_patch(patches, group); + ParentNode::apply_handover_patch(patches, group); + } + + std::unique_ptr m_condition; + const ColumnBase* m_column = nullptr; + ColumnType m_col_type; +}; + +namespace _impl { + +template +struct CostHeuristic; + +template <> +struct CostHeuristic { + static constexpr double dD() + { + return 100.0; + } + static constexpr double dT() + { + return 1.0 / 4.0; + } +}; + +template <> +struct CostHeuristic { + static constexpr double dD() + { + return 100.0; + } + static constexpr double dT() + { + return 1.0 / 4.0; + } +}; + +// FIXME: Add AdaptiveStringColumn, BasicColumn, etc. +} + +class ColumnNodeBase : public ParentNode { +protected: + ColumnNodeBase(size_t column_idx) + { + m_condition_column_idx = column_idx; + } + + ColumnNodeBase(const ColumnNodeBase& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_last_local_match(from.m_last_local_match) + , m_local_matches(from.m_local_matches) + , m_local_limit(from.m_local_limit) + , m_fastmode_disabled(from.m_fastmode_disabled) + , m_action(from.m_action) + , m_state(from.m_state) + , m_source_column(from.m_source_column) + { + } + + template + bool match_callback(int64_t v) + { + using TSourceValue = typename ColType::value_type; + using QueryStateType = typename ColumnTypeTraitsSum::sum_type; + + size_t i = to_size_t(v); + m_last_local_match = i; + m_local_matches++; + + auto state = static_cast*>(m_state); + auto source_column = static_cast*>(m_source_column); + + // Test remaining sub conditions of this node. m_children[0] is the node that called match_callback(), so skip + // it + for (size_t c = 1; c < m_children.size(); c++) { + m_children[c]->m_probes++; + size_t m = m_children[c]->find_first_local(i, i + 1); + if (m != i) + return true; + } + + bool b; + if (state->template uses_val()) { // Compiler cannot see that IntegerColumn::Get has no side effect + // and result is discarded + TSourceValue av = source_column->get_next(i); + b = state->template match(i, 0, av); + } + else { + b = state->template match(i, 0, TSourceValue{}); + } + + return b; + } + + // Aggregate bookkeeping + size_t m_last_local_match = npos; + size_t m_local_matches = 0; + size_t m_local_limit = 0; + bool m_fastmode_disabled = false; + Action m_action; + QueryStateBase* m_state = nullptr; + SequentialGetterBase* m_source_column = + nullptr; // Column of values used in aggregate (act_FindAll, actReturnFirst, act_Sum, etc) +}; + +template +class IntegerNodeBase : public ColumnNodeBase { + using ThisType = IntegerNodeBase; + +public: + using TConditionValue = typename ColType::value_type; + static const bool nullable = ColType::nullable; + + template + bool find_callback_specialization(size_t s, size_t end_in_leaf) + { + using AggregateColumnType = typename GetColumnType::type; + bool cont; + size_t start_in_leaf = s - this->m_leaf_start; + cont = this->m_leaf_ptr->template find( + m_value, start_in_leaf, end_in_leaf, this->m_leaf_start, nullptr, + std::bind(std::mem_fn(&ThisType::template match_callback), this, + std::placeholders::_1)); + return cont; + } + +protected: + using LeafType = typename ColType::LeafType; + using LeafInfo = typename ColType::LeafInfo; + + size_t aggregate_local_impl(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column, int c) + { + REALM_ASSERT(m_children.size() > 0); + m_local_matches = 0; + m_local_limit = local_limit; + m_last_local_match = start - 1; + m_state = st; + + // If there are no other nodes than us (m_children.size() == 1) AND the column used for our condition is + // the same as the column used for the aggregate action, then the entire query can run within scope of that + // column only, with no references to other columns: + bool fastmode = should_run_in_fastmode(source_column); + for (size_t s = start; s < end;) { + cache_leaf(s); + + size_t end_in_leaf; + if (end > m_leaf_end) + end_in_leaf = m_leaf_end - m_leaf_start; + else + end_in_leaf = end - m_leaf_start; + + if (fastmode) { + bool cont; + size_t start_in_leaf = s - m_leaf_start; + cont = m_leaf_ptr->find(c, m_action, m_value, start_in_leaf, end_in_leaf, m_leaf_start, + static_cast*>(st)); + if (!cont) + return not_found; + } + // Else, for each match in this node, call our IntegerNodeBase::match_callback to test remaining nodes + // and/or extract + // aggregate payload from aggregate column: + else { + m_source_column = source_column; + bool cont = (this->*m_find_callback_specialized)(s, end_in_leaf); + if (!cont) + return not_found; + } + + if (m_local_matches == m_local_limit) + break; + + s = end_in_leaf + m_leaf_start; + } + + if (m_local_matches == m_local_limit) { + m_dD = (m_last_local_match + 1 - start) / (m_local_matches + 1.0); + return m_last_local_match + 1; + } + else { + m_dD = (end - start) / (m_local_matches + 1.0); + return end; + } + } + + IntegerNodeBase(TConditionValue value, size_t column_idx) + : ColumnNodeBase(column_idx) + , m_value(std::move(value)) + { + } + + IntegerNodeBase(const ThisType& from, QueryNodeHandoverPatches* patches) + : ColumnNodeBase(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_find_callback_specialized(from.m_find_callback_specialized) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ColumnNodeBase::init(); + + m_dT = _impl::CostHeuristic::dT(); + m_dD = _impl::CostHeuristic::dD(); + + // Clear leaf cache + m_leaf_end = 0; + m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr.reset(new (&m_leaf_cache_storage) LeafType(m_table->get_alloc())); + } + + void get_leaf(const ColType& col, size_t ndx) + { + size_t ndx_in_leaf; + LeafInfo leaf_info{&m_leaf_ptr, m_array_ptr.get()}; + col.get_leaf(ndx, ndx_in_leaf, leaf_info); + m_leaf_start = ndx - ndx_in_leaf; + m_leaf_end = m_leaf_start + m_leaf_ptr->size(); + } + + void cache_leaf(size_t s) + { + if (s >= m_leaf_end || s < m_leaf_start) { + get_leaf(*m_condition_column, s); + size_t w = m_leaf_ptr->get_width(); + m_dT = (w == 0 ? 1.0 / REALM_MAX_BPNODE_SIZE : w / float(bitwidth_time_unit)); + } + } + + bool should_run_in_fastmode(SequentialGetterBase* source_column) const + { + return (m_children.size() == 1 && + (source_column == nullptr || + (!m_fastmode_disabled && + static_cast*>(source_column)->m_column == m_condition_column))); + } + + // Search value: + TConditionValue m_value; + + // Column on which search criteria are applied + const ColType* m_condition_column = nullptr; + + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + LeafCacheStorage m_leaf_cache_storage; + std::unique_ptr m_array_ptr; + const LeafType* m_leaf_ptr = nullptr; + size_t m_leaf_start = npos; + size_t m_leaf_end = 0; + size_t m_local_end; + + // Aggregate optimization + using TFind_callback_specialized = bool (ThisType::*)(size_t, size_t); + TFind_callback_specialized m_find_callback_specialized = nullptr; +}; + +// FIXME: Add specialization that uses index for TConditionFunction = Equal +template +class IntegerNode : public IntegerNodeBase { + using BaseType = IntegerNodeBase; + using ThisType = IntegerNode; + +public: + static const bool special_null_node = false; + using TConditionValue = typename BaseType::TConditionValue; + + IntegerNode(TConditionValue value, size_t column_ndx) + : BaseType(value, column_ndx) + { + } + IntegerNode(const IntegerNode& from, QueryNodeHandoverPatches* patches) + : BaseType(from, patches) + { + } + + void aggregate_local_prepare(Action action, DataType col_id, bool nullable) override + { + this->m_fastmode_disabled = (col_id == type_Float || col_id == type_Double); + this->m_action = action; + this->m_find_callback_specialized = get_specialized_callback(action, col_id, nullable); + } + + size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column) override + { + constexpr int cond = TConditionFunction::condition; + return this->aggregate_local_impl(st, start, end, local_limit, source_column, cond); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(this->m_table); + + while (start < end) { + + // Cache internal leaves + if (start >= this->m_leaf_end || start < this->m_leaf_start) { + this->get_leaf(*this->m_condition_column, start); + } + + // FIXME: Create a fast bypass when you just need to check 1 row, which is used alot from within core. + // It should just call array::get and save the initial overhead of find_first() which has become quite + // big. Do this when we have cleaned up core a bit more. + + size_t end2; + if (end > this->m_leaf_end) + end2 = this->m_leaf_end - this->m_leaf_start; + else + end2 = end - this->m_leaf_start; + + size_t s; + s = this->m_leaf_ptr->template find_first(this->m_value, start - this->m_leaf_start, + end2); + + if (s == not_found) { + start = this->m_leaf_end; + continue; + } + else + return s + this->m_leaf_start; + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new IntegerNode(*this, patches)); + } + +protected: + using TFind_callback_specialized = typename BaseType::TFind_callback_specialized; + + static TFind_callback_specialized get_specialized_callback(Action action, DataType col_id, bool nullable) + { + switch (action) { + case act_Count: + return get_specialized_callback_2_int(col_id, nullable); + case act_Sum: + return get_specialized_callback_2(col_id, nullable); + case act_Max: + return get_specialized_callback_2(col_id, nullable); + case act_Min: + return get_specialized_callback_2(col_id, nullable); + case act_FindAll: + return get_specialized_callback_2_int(col_id, nullable); + case act_CallbackIdx: + return get_specialized_callback_2_int(col_id, nullable); + default: + break; + } + REALM_ASSERT(false); // Invalid aggregate function + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_2(DataType col_id, bool nullable) + { + switch (col_id) { + case type_Int: + return get_specialized_callback_3(nullable); + case type_Float: + return get_specialized_callback_3(nullable); + case type_Double: + return get_specialized_callback_3(nullable); + default: + break; + } + REALM_ASSERT(false); // Invalid aggregate source column + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_2_int(DataType col_id, bool nullable) + { + if (col_id == type_Int) { + return get_specialized_callback_3(nullable); + } + REALM_ASSERT(false); // Invalid aggregate source column + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_3(bool nullable) + { + if (nullable) { + return &BaseType::template find_callback_specialization; + } + else { + return &BaseType::template find_callback_specialization; + } + } +}; + +// This node is currently used for floats and doubles only +template +class FloatDoubleNode : public ParentNode { +public: + using TConditionValue = typename ColType::value_type; + static const bool special_null_node = false; + + FloatDoubleNode(TConditionValue v, size_t column_ndx) + : m_value(v) + { + m_condition_column_idx = column_ndx; + m_dT = 1.0; + } + FloatDoubleNode(null, size_t column_ndx) + : m_value(null::get_null_float()) + { + m_condition_column_idx = column_ndx; + m_dT = 1.0; + } + + void table_changed() override + { + m_condition_column.init(&get_column(m_condition_column_idx)); + } + + void verify_column() const override + { + do_verify_column(m_condition_column.m_column); + } + + void init() override + { + ParentNode::init(); + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction cond; + + auto find = [&](bool nullability) { + bool m_value_nan = nullability ? null::is_null_float(m_value) : false; + for (size_t s = start; s < end; ++s) { + TConditionValue v = m_condition_column.get_next(s); + REALM_ASSERT(!(null::is_null_float(v) && !nullability)); + if (cond(v, m_value, nullability ? null::is_null_float(v) : false, m_value_nan)) + return s; + } + return not_found; + }; + + // This will inline the second case but no the first. Todo, use templated lambda when switching to c++14 + if (m_table->is_nullable(m_condition_column_idx)) + return find(true); + else + return find(false); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new FloatDoubleNode(*this, patches)); + } + + FloatDoubleNode(const FloatDoubleNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + { + copy_getter(m_condition_column, m_condition_column_idx, from.m_condition_column, patches); + } + +protected: + TConditionValue m_value; + SequentialGetter m_condition_column; +}; + +template +class SizeNode : public ParentNode { +public: + SizeNode(int64_t v, size_t column) + : m_value(v) + { + m_condition_column_idx = column; + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ParentNode::init(); + m_dD = 10.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + for (size_t s = start; s < end; ++s) { + TConditionValue v = m_condition_column->get(s); + int64_t sz = m_size_operator(v); + if (TConditionFunction()(sz, m_value, !bool(v))) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SizeNode(*this, patches)); + } + + SizeNode(const SizeNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + using TConditionValue = typename ColType::value_type; + + int64_t m_value; + const ColType* m_condition_column = nullptr; + Size m_size_operator; +}; + + +template +class BinaryNode : public ParentNode { +public: + using TConditionValue = BinaryData; + static const bool special_null_node = false; + + BinaryNode(BinaryData v, size_t column) + : m_value(v) + { + m_dT = 100.0; + m_condition_column_idx = column; + } + + BinaryNode(null, size_t column) + : BinaryNode(BinaryData{}, column) + { + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ParentNode::init(); + + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction condition; + for (size_t s = start; s < end; ++s) { + BinaryData value = m_condition_column->get(s); + if (condition(m_value.get(), value)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new BinaryNode(*this, patches)); + } + + BinaryNode(const BinaryNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + OwnedBinaryData m_value; + const BinaryColumn* m_condition_column; +}; + + +template +class TimestampNode : public ParentNode { +public: + using TConditionValue = Timestamp; + static const bool special_null_node = false; + + TimestampNode(Timestamp v, size_t column) + : m_value(v) + { + m_condition_column_idx = column; + } + + TimestampNode(null, size_t column) + : TimestampNode(Timestamp{}, column) + { + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ParentNode::init(); + + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + size_t ret = m_condition_column->find(m_value, start, end); + return ret; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new TimestampNode(*this, patches)); + } + + TimestampNode(const TimestampNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + Timestamp m_value; + const TimestampColumn* m_condition_column; +}; + +class StringNodeBase : public ParentNode { +public: + using TConditionValue = StringData; + static const bool special_null_node = true; + + StringNodeBase(StringData v, size_t column) + : m_value(v.is_null() ? util::none : util::make_optional(std::string(v))) + { + m_condition_column_idx = column; + } + + void table_changed() override + { + m_condition_column = &get_column_base(m_condition_column_idx); + m_column_type = get_real_column_type(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ParentNode::init(); + + m_dT = 10.0; + m_probes = 0; + m_matches = 0; + m_end_s = 0; + m_leaf_start = 0; + m_leaf_end = 0; + } + + void clear_leaf_state() + { + m_leaf.reset(nullptr); + } + + StringNodeBase(const StringNodeBase& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_column_type(from.m_column_type) + , m_leaf_type(from.m_leaf_type) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +protected: + util::Optional m_value; + + const ColumnBase* m_condition_column = nullptr; + ColumnType m_column_type; + + // Used for linear scan through short/long-string + std::unique_ptr m_leaf; + StringColumn::LeafType m_leaf_type; + size_t m_end_s = 0; + size_t m_leaf_start = 0; + size_t m_leaf_end = 0; + + inline StringData get_string(size_t s) + { + StringData t; + + if (m_column_type == col_type_StringEnum) { + // enum + t = static_cast(m_condition_column)->get(s); + } + else { + // short or long + const StringColumn* asc = static_cast(m_condition_column); + REALM_ASSERT_3(s, <, asc->size()); + if (s >= m_end_s || s < m_leaf_start) { + // we exceeded current leaf's range + clear_leaf_state(); + size_t ndx_in_leaf; + m_leaf = asc->get_leaf(s, ndx_in_leaf, m_leaf_type); + m_leaf_start = s - ndx_in_leaf; + + if (m_leaf_type == StringColumn::leaf_type_Small) + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + else + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + } + + if (m_leaf_type == StringColumn::leaf_type_Small) + t = static_cast(*m_leaf).get(s - m_leaf_start); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + t = static_cast(*m_leaf).get(s - m_leaf_start); + else + t = static_cast(*m_leaf).get_string(s - m_leaf_start); + } + return t; + } +}; + +// Conditions for strings. Note that Equal is specialized later in this file! +template +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, size_t column) + : StringNodeBase(v, column) + { + auto upper = case_map(v, true); + auto lower = case_map(v, false); + if (!upper || !lower) { + error_code = "Malformed UTF-8: " + std::string(v); + } + else { + m_ucase = std::move(*upper); + m_lcase = std::move(*lower); + } + } + + void init() override + { + clear_leaf_state(); + + m_dD = 100.0; + + StringNodeBase::init(); + } + + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction cond; + + for (size_t s = start; s < end; ++s) { + StringData t = get_string(s); + + if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), t)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) + { + } + +protected: + std::string m_ucase; + std::string m_lcase; +}; + +// Specialization for Contains condition on Strings - we specialize because we can utilize Boyer-Moore +template <> +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, size_t column) + : StringNodeBase(v, column), m_charmap() + { + if (v.size() == 0) + return; + + // Build a dictionary of char-to-last distances in the search string + // (zero indicates that the char is not in needle) + size_t last_char_pos = v.size()-1; + for (size_t i = 0; i < last_char_pos; ++i) { + // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte) + uint8_t jump = last_char_pos-i < 255 ? static_cast(last_char_pos-i) : 255; + + unsigned char c = v[i]; + m_charmap[c] = jump; + } + } + + void init() override + { + clear_leaf_state(); + + m_dD = 100.0; + + StringNodeBase::init(); + } + + + size_t find_first_local(size_t start, size_t end) override + { + Contains cond; + + for (size_t s = start; s < end; ++s) { + StringData t = get_string(s); + + if (cond(StringData(m_value), m_charmap, t)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + , m_charmap(from.m_charmap) + { + } + +protected: + std::array m_charmap; +}; + +// Specialization for ContainsIns condition on Strings - we specialize because we can utilize Boyer-Moore +template <> +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, size_t column) + : StringNodeBase(v, column), m_charmap() + { + auto upper = case_map(v, true); + auto lower = case_map(v, false); + if (!upper || !lower) { + error_code = "Malformed UTF-8: " + std::string(v); + } + else { + m_ucase = std::move(*upper); + m_lcase = std::move(*lower); + } + + if (v.size() == 0) + return; + + // Build a dictionary of char-to-last distances in the search string + // (zero indicates that the char is not in needle) + size_t last_char_pos = m_ucase.size()-1; + for (size_t i = 0; i < last_char_pos; ++i) { + // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte) + uint8_t jump = last_char_pos-i < 255 ? static_cast(last_char_pos-i) : 255; + + unsigned char uc = m_ucase[i]; + unsigned char lc = m_lcase[i]; + m_charmap[uc] = jump; + m_charmap[lc] = jump; + } + + } + + void init() override + { + clear_leaf_state(); + + m_dD = 100.0; + + StringNodeBase::init(); + } + + + size_t find_first_local(size_t start, size_t end) override + { + ContainsIns cond; + + for (size_t s = start; s < end; ++s) { + StringData t = get_string(s); + + if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), m_charmap, t)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + , m_charmap(from.m_charmap) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) + { + } + +protected: + std::array m_charmap; + std::string m_ucase; + std::string m_lcase; +}; + +class StringNodeEqualBase : public StringNodeBase { +public: + StringNodeEqualBase(StringData v, size_t column) + : StringNodeBase(v, column) + { + } + StringNodeEqualBase(const StringNodeEqualBase& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + { + } + ~StringNodeEqualBase() noexcept override + { + deallocate(); + } + + void deallocate() noexcept; + void init() override; + size_t find_first_local(size_t start, size_t end) override; + + +protected: + inline BinaryData str_to_bin(const StringData& s) noexcept + { + return BinaryData(s.data(), s.size()); + } + + virtual void _search_index_init() = 0; + virtual size_t _find_first_local(size_t start, size_t end) = 0; + + size_t m_key_ndx = not_found; + size_t m_last_indexed; + + // Used for linear scan through enum-string + SequentialGetter m_cse; + + // Used for index lookup + std::unique_ptr m_index_matches; + bool m_index_matches_destroy = false; + std::unique_ptr> m_index_getter; + size_t m_results_start; + size_t m_results_end; + size_t m_last_start; +}; + + +// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for +// Equal. +// Future optimization: make specialization for greater, notequal, etc +template <> +class StringNode : public StringNodeEqualBase { +public: + using StringNodeEqualBase::StringNodeEqualBase; + + void _search_index_init() override; + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + +private: + size_t _find_first_local(size_t start, size_t end) override; +}; + + +// Specialization for EqualIns condition on Strings - we specialize because we can utilize indexes (if they exist) for +// EqualIns. +template <> +class StringNode : public StringNodeEqualBase { +public: + StringNode(StringData v, size_t column) + : StringNodeEqualBase(v, column) + { + auto upper = case_map(v, true); + auto lower = case_map(v, false); + if (!upper || !lower) { + error_code = "Malformed UTF-8: " + std::string(v); + } + else { + m_ucase = std::move(*upper); + m_lcase = std::move(*lower); + } + } + + void _search_index_init() override; + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeEqualBase(from, patches) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) + { + } + +private: + std::string m_ucase; + std::string m_lcase; + + size_t _find_first_local(size_t start, size_t end) override; +}; + + +// OR node contains at least two node pointers: Two or more conditions to OR +// together in m_conditions, and the next AND condition (if any) in m_child. +// +// For 'second.equal(23).begin_group().first.equal(111).Or().first.equal(222).end_group().third().equal(555)', this +// will first set m_conditions[0] = left-hand-side through constructor, and then later, when .first.equal(222) is +// invoked, +// invocation will set m_conditions[1] = right-hand-side through Query& Query::Or() (see query.cpp). In there, m_child +// is +// also set to next AND condition (if any exists) following the OR. +class OrNode : public ParentNode { +public: + OrNode(std::unique_ptr condition) + { + m_dT = 50.0; + if (condition) + m_conditions.emplace_back(std::move(condition)); + } + + OrNode(const OrNode& other, QueryNodeHandoverPatches* patches) + : ParentNode(other, patches) + { + for (const auto& condition : other.m_conditions) { + m_conditions.emplace_back(condition->clone(patches)); + } + } + + void table_changed() override + { + for (auto& condition : m_conditions) { + condition->set_table(*m_table); + } + } + + void verify_column() const override + { + for (auto& condition : m_conditions) { + condition->verify_column(); + } + } + + void init() override + { + ParentNode::init(); + + m_dD = 10.0; + + m_start.clear(); + m_start.resize(m_conditions.size(), 0); + + m_last.clear(); + m_last.resize(m_conditions.size(), 0); + + m_was_match.clear(); + m_was_match.resize(m_conditions.size(), false); + + std::vector v; + for (auto& condition : m_conditions) { + condition->init(); + v.clear(); + condition->gather_children(v); + } + } + + size_t find_first_local(size_t start, size_t end) override + { + if (start >= end) + return not_found; + + size_t index = not_found; + + for (size_t c = 0; c < m_conditions.size(); ++c) { + // out of order search; have to discard cached results + if (start < m_start[c]) { + m_last[c] = 0; + m_was_match[c] = false; + } + // already searched this range and didn't match + else if (m_last[c] >= end) + continue; + // already search this range and *did* match + else if (m_was_match[c] && m_last[c] >= start) { + if (index > m_last[c]) + index = m_last[c]; + continue; + } + + m_start[c] = start; + size_t fmax = std::max(m_last[c], start); + size_t f = m_conditions[c]->find_first(fmax, end); + m_was_match[c] = f != not_found; + m_last[c] = f == not_found ? end : f; + if (f != not_found && index > m_last[c]) + index = m_last[c]; + } + + return index; + } + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_conditions.size() == 0) + return "Missing left-hand side of OR"; + if (m_conditions.size() == 1) + return "Missing right-hand side of OR"; + std::string s; + if (m_child != 0) + s = m_child->validate(); + if (s != "") + return s; + for (size_t i = 0; i < m_conditions.size(); ++i) { + s = m_conditions[i]->validate(); + if (s != "") + return s; + } + return ""; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new OrNode(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + for (auto it = m_conditions.rbegin(); it != m_conditions.rend(); ++it) + (*it)->apply_handover_patch(patches, group); + + ParentNode::apply_handover_patch(patches, group); + } + + std::vector> m_conditions; + +private: + // start index of the last find for each cond + std::vector m_start; + // last looked at index of the lasft find for each cond + // is a matching index if m_was_match is true + std::vector m_last; + std::vector m_was_match; +}; + + +class NotNode : public ParentNode { +public: + NotNode(std::unique_ptr condition) + : m_condition(std::move(condition)) + { + m_dT = 50.0; + } + + void table_changed() override + { + m_condition->set_table(*m_table); + } + + void verify_column() const override + { + m_condition->verify_column(); + } + + void init() override + { + ParentNode::init(); + + m_dD = 10.0; + + std::vector v; + + m_condition->init(); + v.clear(); + m_condition->gather_children(v); + + // Heuristics bookkeeping: + m_known_range_start = 0; + m_known_range_end = 0; + m_first_in_known_range = not_found; + } + + size_t find_first_local(size_t start, size_t end) override; + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_condition == 0) + return "Missing argument to Not"; + std::string s; + if (m_child != 0) + s = m_child->validate(); + if (s != "") + return s; + s = m_condition->validate(); + if (s != "") + return s; + return ""; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new NotNode(*this, patches)); + } + + NotNode(const NotNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_condition(from.m_condition ? from.m_condition->clone(patches) : nullptr) + , m_known_range_start(from.m_known_range_start) + , m_known_range_end(from.m_known_range_end) + , m_first_in_known_range(from.m_first_in_known_range) + { + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_condition->apply_handover_patch(patches, group); + ParentNode::apply_handover_patch(patches, group); + } + + std::unique_ptr m_condition; + +private: + // FIXME This heuristic might as well be reused for all condition nodes. + size_t m_known_range_start; + size_t m_known_range_end; + size_t m_first_in_known_range; + + bool evaluate_at(size_t rowndx); + void update_known(size_t start, size_t end, size_t first); + size_t find_first_loop(size_t start, size_t end); + size_t find_first_covers_known(size_t start, size_t end); + size_t find_first_covered_by_known(size_t start, size_t end); + size_t find_first_overlap_lower(size_t start, size_t end); + size_t find_first_overlap_upper(size_t start, size_t end); + size_t find_first_no_overlap(size_t start, size_t end); +}; + + +// Compare two columns with eachother row-by-row +template +class TwoColumnsNode : public ParentNode { +public: + using TConditionValue = typename ColType::value_type; + + TwoColumnsNode(size_t column1, size_t column2) + { + m_dT = 100.0; + m_condition_column_idx1 = column1; + m_condition_column_idx2 = column2; + } + + ~TwoColumnsNode() noexcept override + { + delete[] m_value.data(); + } + + void table_changed() override + { + m_getter1.init(&get_column(m_condition_column_idx1)); + m_getter2.init(&get_column(m_condition_column_idx2)); + } + + void verify_column() const override + { + do_verify_column(m_getter1.m_column, m_condition_column_idx1); + do_verify_column(m_getter2.m_column, m_condition_column_idx2); + } + + void init() override + { + ParentNode::init(); + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + size_t s = start; + + while (s < end) { + if (std::is_same::value) { + // For int64_t we've created an array intrinsics named compare_leafs which template expands bitwidths + // of boths arrays to make Get faster. + m_getter1.cache_next(s); + m_getter2.cache_next(s); + + QueryState qs; + bool resume = m_getter1.m_leaf_ptr->template compare_leafs( + m_getter2.m_leaf_ptr, s - m_getter1.m_leaf_start, m_getter1.local_end(end), 0, &qs, + CallbackDummy()); + + if (resume) + s = m_getter1.m_leaf_end; + else + return to_size_t(qs.m_state) + m_getter1.m_leaf_start; + } + else { +// This is for float and double. + +#if 0 && defined(REALM_COMPILER_AVX) +// AVX has been disabled because of array alignment (see https://app.asana.com/0/search/8836174089724/5763107052506) +// +// For AVX you can call things like if (sseavx<1>()) to test for AVX, and then utilize _mm256_movemask_ps (VC) +// or movemask_cmp_ps (gcc/clang) +// +// See https://github.com/rrrlasse/realm/tree/AVX for an example of utilizing AVX for a two-column search which has +// been benchmarked to: floats: 288 ms vs 552 by using AVX compared to 2-level-unrolled FPU loop. doubles: 415 ms vs +// 475 (more bandwidth bound). Tests against SSE have not been performed; AVX may not pay off. Please benchmark +#endif + + TConditionValue v1 = m_getter1.get_next(s); + TConditionValue v2 = m_getter2.get_next(s); + TConditionFunction C; + + if (C(v1, v2)) + return s; + else + s++; + } + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new TwoColumnsNode(*this, patches)); + } + + TwoColumnsNode(const TwoColumnsNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_column_type(from.m_column_type) + , m_condition_column_idx1(from.m_condition_column_idx1) + , m_condition_column_idx2(from.m_condition_column_idx2) + { + if (m_condition_column) + m_condition_column_idx = m_condition_column->get_column_index(); + copy_getter(m_getter1, m_condition_column_idx1, from.m_getter1, patches); + copy_getter(m_getter2, m_condition_column_idx2, from.m_getter2, patches); + } + +private: + BinaryData m_value; + const BinaryColumn* m_condition_column = nullptr; + ColumnType m_column_type; + + size_t m_condition_column_idx1 = not_found; + size_t m_condition_column_idx2 = not_found; + + SequentialGetter m_getter1; + SequentialGetter m_getter2; +}; + + +// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100. +class ExpressionNode : public ParentNode { + +public: + ExpressionNode(std::unique_ptr); + + size_t find_first_local(size_t start, size_t end) override; + + void table_changed() override; + void verify_column() const override; + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override; + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override; + +private: + ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatches* patches); + + std::unique_ptr m_expression; +}; + + +struct LinksToNodeHandoverPatch : public QueryNodeHandoverPatch { + std::unique_ptr m_target_row; + size_t m_origin_column; +}; + +class LinksToNode : public ParentNode { +public: + LinksToNode(size_t origin_column_index, const ConstRow& target_row) + : m_origin_column(origin_column_index) + , m_target_row(target_row) + { + m_dD = 10.0; + m_dT = 50.0; + } + + void table_changed() override + { + m_column_type = m_table->get_column_type(m_origin_column); + m_column = &const_cast(m_table.get())->get_column_link_base(m_origin_column); + REALM_ASSERT(m_column_type == type_Link || m_column_type == type_LinkList); + } + + void verify_column() const override + { + do_verify_column(m_column, m_origin_column); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(m_column); + if (!m_target_row.is_attached()) + return not_found; + + if (m_column_type == type_Link) { + LinkColumn& cl = static_cast(*m_column); + return cl.find_first(m_target_row.get_index() + 1, start, + end); // LinkColumn stores link to row N as the integer N + 1 + } + else if (m_column_type == type_LinkList) { + LinkListColumn& cll = static_cast(*m_column); + + for (size_t i = start; i < end; i++) { + LinkViewRef lv = cll.get(i); + if (lv->find(m_target_row.get_index()) != not_found) + return i; + } + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(patches ? new LinksToNode(*this, patches) : new LinksToNode(*this)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_origin_column = patch->m_origin_column; + m_target_row.apply_and_consume_patch(patch->m_target_row, group); + + ParentNode::apply_handover_patch(patches, group); + } + +private: + size_t m_origin_column = npos; + ConstRow m_target_row; + LinkColumnBase* m_column = nullptr; + DataType m_column_type; + + LinksToNode(const LinksToNode& source, QueryNodeHandoverPatches* patches) + : ParentNode(source, patches) + { + auto patch = std::make_unique(); + patch->m_origin_column = source.m_column->get_column_index(); + ConstRow::generate_patch(source.m_target_row, patch->m_target_row); + patches->push_back(std::move(patch)); + } +}; + +} // namespace realm + +#endif // REALM_QUERY_ENGINE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_expression.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_expression.hpp new file mode 100644 index 0000000..b99065f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_expression.hpp @@ -0,0 +1,3333 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +/* +This file lets you write queries in C++ syntax like: Expression* e = (first + 1 / second >= third + 12.3); + +Type conversion/promotion semantics is the same as in the C++ expressions, e.g float + int > double == float + +(float)int > double. + + +Grammar: +----------------------------------------------------------------------------------------------------------------------- + Expression: Subexpr2 Compare Subexpr2 + operator! Expression + + Subexpr2: Value + Columns + Subexpr2 Operator Subexpr2 + power(Subexpr2) // power(x) = x * x, as example of unary operator + + Value: T + + Operator>: +, -, *, / + + Compare: ==, !=, >=, <=, >, < + + T: bool, int, int64_t, float, double, StringData + + +Class diagram +----------------------------------------------------------------------------------------------------------------------- +Subexpr2 + void evaluate(size_t i, ValueBase* destination) + +Compare: public Subexpr2 + size_t find_first(size_t start, size_t end) // main method that executes query + + unique_ptr m_left; // left expression subtree + unique_ptr m_right; // right expression subtree + +Operator: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + unique_ptr m_left; // left expression subtree + unique_ptr m_right; // right expression subtree + +Value: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + T m_v[8]; + +Columns: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + SequentialGetter sg; // class bound to a column, lets you read values in a fast way + Table* m_table; + +class ColumnAccessor<>: public Columns + + +Call diagram: +----------------------------------------------------------------------------------------------------------------------- +Example of 'table.first > 34.6 + table.second': + +size_t Compare::find_first()-------------+ + | | + | | + | | + +--> Columns::evaluate() +--------> Operator::evaluate() + | | + Value::evaluate() Columns::evaluate() + +Operator, Value and Columns have an evaluate(size_t i, ValueBase* destination) method which returns a Value +containing 8 values representing table rows i...i + 7. + +So Value contains 8 concecutive values and all operations are based on these chunks. This is +to save overhead by virtual calls needed for evaluating a query that has been dynamically constructed at runtime. + + +Memory allocation: +----------------------------------------------------------------------------------------------------------------------- +Subexpressions created by the end-user are stack allocated. They are cloned to the heap when passed to UnaryOperator, +Operator, and Compare. Those types own the clones and deallocate them when destroyed. + + +Caveats, notes and todos +----------------------------------------------------------------------------------------------------------------------- + * Perhaps disallow columns from two different tables in same expression + * The name Columns (with s) an be confusing because we also have Column (without s) + * We have Columns::m_table, Query::m_table and ColumnAccessorBase::m_table that point at the same thing, even with + ColumnAccessor<> extending Columns. So m_table is redundant, but this is in order to keep class dependencies and + entanglement low so that the design is flexible (if you perhaps later want a Columns class that is not dependent + on ColumnAccessor) + +Nulls +----------------------------------------------------------------------------------------------------------------------- +First note that at array level, nulls are distinguished between non-null in different ways: +String: + m_data == 0 && m_size == 0 + +Integer, Bool, OldDateTime stored in ArrayIntNull: + value == get(0) (entry 0 determins a magic value that represents nulls) + +Float/double: + null::is_null(value) which tests if value bit-matches one specific bit pattern reserved for null + +The Columns class encapsulates all this into a simple class that, for any type T has + evaluate(size_t index) that reads values from a column, taking nulls in count + get(index) + set(index) + is_null(index) + set_null(index) +*/ + +#ifndef REALM_QUERY_EXPRESSION_HPP +#define REALM_QUERY_EXPRESSION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Normally, if a next-generation-syntax condition is supported by the old query_engine.hpp, a query_engine node is +// created because it's faster (by a factor of 5 - 10). Because many of our existing next-generation-syntax unit +// unit tests are indeed simple enough to fallback to old query_engine, query_expression gets low test coverage. Undef +// flag to get higher query_expression test coverage. This is a good idea to try out each time you develop on/modify +// query_expression. + +#define REALM_OLDQUERY_FALLBACK + +namespace realm { + +template +T minimum(T a, T b) +{ + return a < b ? a : b; +} + +#ifdef REALM_OLDQUERY_FALLBACK +// Hack to avoid template instantiation errors. See create(). Todo, see if we can simplify only_numeric somehow +namespace { +template +T only_numeric(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +int only_numeric(const StringData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +int only_numeric(const BinaryData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +StringData only_string(T in) +{ + REALM_ASSERT(false); + static_cast(in); + return StringData(); +} + +StringData only_string(StringData in) +{ + return in; +} + +template +T no_timestamp(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +int no_timestamp(const Timestamp&) +{ + REALM_ASSERT(false); + return 0; +} + +} // anonymous namespace + +#endif // REALM_OLDQUERY_FALLBACK + +template +struct Plus { + T operator()(T v1, T v2) const + { + return v1 + v2; + } + typedef T type; +}; + +template +struct Minus { + T operator()(T v1, T v2) const + { + return v1 - v2; + } + typedef T type; +}; + +template +struct Div { + T operator()(T v1, T v2) const + { + return v1 / v2; + } + typedef T type; +}; + +template +struct Mul { + T operator()(T v1, T v2) const + { + return v1 * v2; + } + typedef T type; +}; + +// Unary operator +template +struct Pow { + T operator()(T v) const + { + return v * v; + } + typedef T type; +}; + +// Finds a common type for T1 and T2 according to C++ conversion/promotion in arithmetic (float + int => float, etc) +template ::is_integer || std::is_same::value, + bool T2_is_int = std::numeric_limits::is_integer || std::is_same::value, + bool T1_is_widest = (sizeof(T1) > sizeof(T2) || std::is_same::value)> +struct Common; +template +struct Common { + typedef T1 type; +}; +template +struct Common { + typedef T2 type; +}; +template +struct Common { + typedef T1 type; +}; +template +struct Common { + typedef T2 type; +}; + + +struct RowIndex { + enum DetachedTag { + Detached, + }; + + explicit RowIndex() + : m_row_index(npos) + { + } + explicit RowIndex(size_t row_index) + : m_row_index(row_index) + { + } + RowIndex(DetachedTag) + : m_row_index() + { + } + + bool is_attached() const + { + return bool(m_row_index); + } + bool is_null() const + { + return is_attached() && *m_row_index == npos; + } + + bool operator==(const RowIndex& other) const + { + // Row indexes that are detached are never equal to any other row index. + if (!is_attached() || !other.is_attached()) + return false; + return m_row_index == other.m_row_index; + } + bool operator!=(const RowIndex& other) const + { + return !(*this == other); + } + +private: + util::Optional m_row_index; +}; + + +struct ValueBase { + static const size_t default_size = 8; + virtual void export_bool(ValueBase& destination) const = 0; + virtual void export_Timestamp(ValueBase& destination) const = 0; + virtual void export_int(ValueBase& destination) const = 0; + virtual void export_float(ValueBase& destination) const = 0; + virtual void export_int64_t(ValueBase& destination) const = 0; + virtual void export_double(ValueBase& destination) const = 0; + virtual void export_StringData(ValueBase& destination) const = 0; + virtual void export_BinaryData(ValueBase& destination) const = 0; + virtual void export_RowIndex(ValueBase& destination) const = 0; + virtual void export_null(ValueBase& destination) const = 0; + virtual void import(const ValueBase& destination) = 0; + + // If true, all values in the class come from a link list of a single field in the parent table (m_table). If + // false, then values come from successive rows of m_table (query operations are operated on in bulks for speed) + bool m_from_link_list; + + // Number of values stored in the class. + size_t m_values; +}; + +class Expression { +public: + Expression() + { + } + virtual ~Expression() + { + } + + virtual size_t find_first(size_t start, size_t end) const = 0; + virtual void set_base_table(const Table* table) = 0; + virtual void verify_column() const = 0; + virtual const Table* get_base_table() const = 0; + + virtual std::unique_ptr clone(QueryNodeHandoverPatches*) const = 0; + virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&) + { + } +}; + +template +std::unique_ptr make_expression(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +class Subexpr { +public: + virtual ~Subexpr() + { + } + + virtual std::unique_ptr clone(QueryNodeHandoverPatches* = nullptr) const = 0; + virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&) + { + } + + // When the user constructs a query, it always "belongs" to one single base/parent table (regardless of + // any links or not and regardless of any queries assembled with || or &&). When you do a Query::find(), + // then Query::m_table is set to this table, and set_base_table() is called on all Columns and LinkMaps in + // the query expression tree so that they can set/update their internals as required. + // + // During thread-handover of a Query, set_base_table() is also called to make objects point at the new table + // instead of the old one from the old thread. + virtual void set_base_table(const Table*) + { + } + + virtual void verify_column() const = 0; + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + virtual const Table* get_base_table() const + { + return nullptr; + } + + virtual void evaluate(size_t index, ValueBase& destination) = 0; +}; + +template +std::unique_ptr make_subexpr(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +class Columns; +template +class Value; +class ConstantStringValue; +template +class Subexpr2; +template +class Operator; +template +class UnaryOperator; +template +class SizeOperator; +template +class Compare; +template +class UnaryLinkCompare; +class ColumnAccessorBase; + + +// Handle cases where left side is a constant (int, float, int64_t, double, StringData) +template +Query create(L left, const Subexpr2& right) +{ +// Purpose of below code is to intercept the creation of a condition and test if it's supported by the old +// query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a +// query_expression.hpp node. +// +// This method intercepts only Value Subexpr2. Interception of Subexpr2 Subexpr is elsewhere. + +#ifdef REALM_OLDQUERY_FALLBACK // if not defined, then never fallback to query_engine.hpp; always use query_expression + const Columns* column = dynamic_cast*>(&right); + // TODO: recognize size operator expressions + // auto size_operator = dynamic_cast, Subexpr>*>(&right); + + if (column && ((std::numeric_limits::is_integer && std::numeric_limits::is_integer) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value)) && + !column->links_exist()) { + const Table* t = column->get_base_table(); + Query q = Query(*t); + + if (std::is_same::value) + q.greater(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.less(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_ndx(), left); + else if (std::is_same::value) + q.not_equal(column->column_ndx(), left); + else if (std::is_same::value) + q.greater_equal(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.less_equal(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.not_equal(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.begins_with(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.begins_with(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.ends_with(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.ends_with(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.contains(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.contains(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.like(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.like(column->column_ndx(), only_string(left), false); + else { + // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp or + // fallback to using use 'return new Compare<>' instead. + REALM_ASSERT(false); + } + // Return query_engine.hpp node + return q; + } + else +#endif + { + // Return query_expression.hpp node + using CommonType = typename Common::type; + using ValueType = + typename std::conditional::value, ConstantStringValue, Value>::type; + return make_expression>(make_subexpr(left), right.clone()); + } +} + + +// All overloads where left-hand-side is Subexpr2: +// +// left-hand-side operator right-hand-side +// Subexpr2 +, -, *, /, <, >, ==, !=, <=, >= R, Subexpr2 +// +// For L = R = {int, int64_t, float, double, StringData, Timestamp}: +template +class Overloads { + typedef typename Common::type CommonType; + + std::unique_ptr clone_subexpr() const + { + return static_cast&>(*this).clone(); + } + +public: + // Arithmetic, right side constant + Operator> operator+(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator-(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator*(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator/(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + + // Arithmetic, right side subexpression + Operator> operator+(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator-(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator*(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator/(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + + // Compare, right side constant + Query operator>(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator<(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator>=(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator<=(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator==(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator!=(R right) + { + return create(right, static_cast&>(*this)); + } + + // Purpose of this method is to intercept the creation of a condition and test if it's supported by the old + // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a + // query_expression.hpp node. + // + // This method intercepts Subexpr2 Subexpr2 only. Value Subexpr2 is intercepted elsewhere. + template + Query create2(const Subexpr2& right) + { +#ifdef REALM_OLDQUERY_FALLBACK // if not defined, never fallback query_engine; always use query_expression + // Test if expressions are of type Columns. Other possibilities are Value and Operator. + const Columns* left_col = dynamic_cast*>(static_cast*>(this)); + const Columns* right_col = dynamic_cast*>(&right); + + // query_engine supports 'T-column ' for T = {int64_t, float, double}, op = {<, >, ==, !=, <=, + // >=}, + // but only if both columns are non-nullable, and aren't in linked tables. + if (left_col && right_col && std::is_same::value && !left_col->is_nullable() && + !right_col->is_nullable() && !left_col->links_exist() && !right_col->links_exist() && + !std::is_same::value) { + const Table* t = left_col->get_base_table(); + Query q = Query(*t); + + if (std::numeric_limits::is_integer || std::is_same::value) { + if (std::is_same::value) + q.less_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else { + REALM_ASSERT(false); + } + // Return query_engine.hpp node + return q; + } + else +#endif + { + // Return query_expression.hpp node + return make_expression::type>>(clone_subexpr(), right.clone()); + } + } + + // Compare, right side subexpression + Query operator==(const Subexpr2& right) + { + return create2(right); + } + Query operator!=(const Subexpr2& right) + { + return create2(right); + } + Query operator>(const Subexpr2& right) + { + return create2(right); + } + Query operator<(const Subexpr2& right) + { + return create2(right); + } + Query operator>=(const Subexpr2& right) + { + return create2(right); + } + Query operator<=(const Subexpr2& right) + { + return create2(right); + } +}; + +// With this wrapper class we can define just 20 overloads inside Overloads instead of 5 * 20 = 100. Todo: We +// can +// consider if it's simpler/better to remove this class completely and just list all 100 overloads manually anyway. +template +class Subexpr2 : public Subexpr, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads { +public: + virtual ~Subexpr2() + { + } + +#define RLM_U2(t, o) using Overloads::operator o; +#define RLM_U(o) \ + RLM_U2(int, o) \ + RLM_U2(float, o) \ + RLM_U2(double, o) \ + RLM_U2(int64_t, o) \ + RLM_U2(StringData, o) RLM_U2(bool, o) RLM_U2(OldDateTime, o) RLM_U2(Timestamp, o) RLM_U2(null, o) + RLM_U(+) RLM_U(-) RLM_U(*) RLM_U(/) RLM_U(>) RLM_U(<) RLM_U(==) RLM_U(!=) RLM_U(>=) RLM_U(<=) +}; + +// Subexpr2 only provides equality comparisons. Their implementations can be found later in this file. +template <> +class Subexpr2 : public Subexpr { +}; + + +/* +This class is used to store N values of type T = {int64_t, bool, OldDateTime or StringData}, and allows an entry +to be null too. It's used by the Value class for internal storage. + +To indicate nulls, we could have chosen a separate bool vector or some other bitmask construction. But for +performance, we customize indication of nulls to match the same indication that is used in the persisted database +file + +Queries in query_expression.hpp execute by processing chunks of 8 rows at a time. Assume you have a column: + + price (int) = {1, 2, 3, null, 1, 6, 6, 9, 5, 2, null} + +And perform a query: + + Query q = (price + 2 == 5); + +query_expression.hpp will then create a NullableVector = {5, 5, 5, 5, 5, 5, 5, 5} and then read values +NullableVector = {1, 2, 3, null, 1, 6, 6, 9} from the column, and then perform `+` and `==` on these chunks. + +See the top of this file for more information on all this. + +Assume the user specifies the null constant in a query: + +Query q = (price == null) + +The query system will then construct a NullableVector of type `null` (NullableVector). This allows compile +time optimizations for these cases. +*/ + +template +struct NullableVector { + using Underlying = typename util::RemoveOptional::type; + using t_storage = + typename std::conditional::value || std::is_same::value, + int64_t, Underlying>::type; + + NullableVector() + { + } + + NullableVector& operator=(const NullableVector& other) + { + if (this != &other) { + init(other.m_size); + realm::safe_copy_n(other.m_first, other.m_size, m_first); + m_null = other.m_null; + } + return *this; + } + + NullableVector(const NullableVector& other) + { + init(other.m_size); + realm::safe_copy_n(other.m_first, other.m_size, m_first); + m_null = other.m_null; + } + + ~NullableVector() + { + dealloc(); + } + + T operator[](size_t index) const + { + REALM_ASSERT_3(index, <, m_size); + return static_cast(m_first[index]); + } + + inline bool is_null(size_t index) const + { + REALM_ASSERT((std::is_same::value)); + return m_first[index] == m_null; + } + + inline void set_null(size_t index) + { + REALM_ASSERT((std::is_same::value)); + m_first[index] = m_null; + } + + template + typename std::enable_if::value, void>::type set(size_t index, t_storage value) + { + REALM_ASSERT((std::is_same::value)); + + // If value collides with magic null value, then switch to a new unique representation for null + if (REALM_UNLIKELY(value == m_null)) { + // adding a prime will generate 2^64 unique values. Todo: Only works on 2's complement architecture + uint64_t candidate = static_cast(m_null) + 0xfffffffbULL; + while (std::find(m_first, m_first + m_size, static_cast(candidate)) != m_first + m_size) + candidate += 0xfffffffbULL; + std::replace(m_first, m_first + m_size, m_null, static_cast(candidate)); + } + m_first[index] = value; + } + + template + typename std::enable_if::value, + void>::type + set(size_t index, t_storage value) + { + m_first[index] = value; + } + + inline util::Optional get(size_t index) const + { + if (is_null(index)) + return util::none; + + return util::make_optional((*this)[index]); + } + + inline void set(size_t index, util::Optional value) + { + if (value) { + Underlying v = *value; + set(index, v); + } + else { + set_null(index); + } + } + + void fill(T value) + { + for (size_t t = 0; t < m_size; t++) { + if (std::is_same::value) + set_null(t); + else + set(t, value); + } + } + + void init(size_t size) + { + if (size == m_size) + return; + + dealloc(); + m_size = size; + if (m_size > 0) { + if (m_size > prealloc) + m_first = reinterpret_cast(new t_storage[m_size]); + else + m_first = m_cache; + } + } + + void init(size_t size, T values) + { + init(size); + fill(values); + } + + void dealloc() + { + if (m_first) { + if (m_size > prealloc) + delete[] m_first; + m_first = nullptr; + } + } + + t_storage m_cache[prealloc]; + t_storage* m_first = &m_cache[0]; + size_t m_size = 0; + + int64_t m_null = reinterpret_cast(&m_null); // choose magic value to represent nulls +}; + +// Double +// NOTE: fails in gcc 4.8 without `inline`. Do not remove. Same applies for all methods below. +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return null::is_null_float(m_first[index]); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = null::get_null_float(); +} + +// Float +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return null::is_null_float(m_first[index]); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = null::get_null_float(); +} + + +// Null +template <> +inline void NullableVector::set_null(size_t) +{ + return; +} +template <> +inline bool NullableVector::is_null(size_t) const +{ + return true; +} + + +// OldDateTime +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].get_olddatetime() == m_null; +} + + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = m_null; +} + +// StringData + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = StringData(); +} + +// BinaryData + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = BinaryData(); +} + +// RowIndex +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = RowIndex(); +} + + +// Timestamp + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = Timestamp{}; +} + +// ConstTableRef +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return !bool(m_first[index]); +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index].reset(); +} + +template +struct OperatorOptionalAdapter { + template + util::Optional operator()(const util::Optional& left, const util::Optional& right) + { + if (!left || !right) + return util::none; + return Operator()(*left, *right); + } + + template + util::Optional operator()(const util::Optional& arg) + { + if (!arg) + return util::none; + return Operator()(*arg); + } +}; + +// Stores N values of type T. Can also exchange data with other ValueBase of different types +template +class Value : public ValueBase, public Subexpr2 { +public: + Value() + { + init(false, ValueBase::default_size, T()); + } + Value(T v) + { + init(false, ValueBase::default_size, v); + } + + Value(bool from_link_list, size_t values) + { + init(from_link_list, values, T()); + } + + Value(bool from_link_list, size_t values, T v) + { + init(from_link_list, values, v); + } + + Value(const Value&) = default; + Value& operator=(const Value&) = default; + + void init(bool from_link_list, size_t values, T v) + { + m_storage.init(values, v); + ValueBase::m_from_link_list = from_link_list; + ValueBase::m_values = values; + } + + void init(bool from_link_list, size_t values) + { + m_storage.init(values); + ValueBase::m_from_link_list = from_link_list; + ValueBase::m_values = values; + } + + void verify_column() const override + { + } + + void evaluate(size_t, ValueBase& destination) override + { + destination.import(*this); + } + + + template + REALM_FORCEINLINE void fun(const Value* left, const Value* right) + { + OperatorOptionalAdapter o; + + if (!left->m_from_link_list && !right->m_from_link_list) { + // Operate on values one-by-one (one value is one row; no links) + size_t min = std::min(left->m_values, right->m_values); + init(false, min); + + for (size_t i = 0; i < min; i++) { + m_storage.set(i, o(left->m_storage.get(i), right->m_storage.get(i))); + } + } + else if (left->m_from_link_list && right->m_from_link_list) { + // FIXME: Many-to-many links not supported yet. Need to specify behaviour + REALM_ASSERT_DEBUG(false); + } + else if (!left->m_from_link_list && right->m_from_link_list) { + // Right values come from link. Left must come from single row. + REALM_ASSERT_DEBUG(left->m_values > 0); + init(true, right->m_values); + + auto left_value = left->m_storage.get(0); + for (size_t i = 0; i < right->m_values; i++) { + m_storage.set(i, o(left_value, right->m_storage.get(i))); + } + } + else if (left->m_from_link_list && !right->m_from_link_list) { + // Same as above, but with left values coming from links + REALM_ASSERT_DEBUG(right->m_values > 0); + init(true, left->m_values); + + auto right_value = right->m_storage.get(0); + for (size_t i = 0; i < left->m_values; i++) { + m_storage.set(i, o(left->m_storage.get(i), right_value)); + } + } + } + + template + REALM_FORCEINLINE void fun(const Value* value) + { + init(value->m_from_link_list, value->m_values); + + OperatorOptionalAdapter o; + for (size_t i = 0; i < value->m_values; i++) { + m_storage.set(i, o(value->m_storage.get(i))); + } + } + + + // Below import and export methods are for type conversion between float, double, int64_t, etc. + template + typename std::enable_if::value>::type REALM_FORCEINLINE + export2(ValueBase& destination) const + { + Value& d = static_cast&>(destination); + d.init(ValueBase::m_from_link_list, ValueBase::m_values, D()); + for (size_t t = 0; t < ValueBase::m_values; t++) { + if (m_storage.is_null(t)) + d.m_storage.set_null(t); + else { + d.m_storage.set(t, static_cast(m_storage[t])); + } + } + } + + template + typename std::enable_if::value>::type REALM_FORCEINLINE export2(ValueBase&) const + { + // export2 is instantiated for impossible conversions like T=StringData, D=int64_t. These are never + // performed at runtime but would result in a compiler error if we did not provide this implementation. + REALM_ASSERT_DEBUG(false); + } + + REALM_FORCEINLINE void export_Timestamp(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_bool(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_int64_t(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_float(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_int(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_double(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_StringData(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_BinaryData(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_RowIndex(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_null(ValueBase& destination) const override + { + Value& d = static_cast&>(destination); + d.init(m_from_link_list, m_values); + } + + REALM_FORCEINLINE void import(const ValueBase& source) override + { + if (std::is_same::value) + source.export_int(*this); + else if (std::is_same::value) + source.export_Timestamp(*this); + else if (std::is_same::value) + source.export_bool(*this); + else if (std::is_same::value) + source.export_float(*this); + else if (std::is_same::value) + source.export_double(*this); + else if (std::is_same::value || std::is_same::value || + std::is_same::value) + source.export_int64_t(*this); + else if (std::is_same::value) + source.export_StringData(*this); + else if (std::is_same::value) + source.export_BinaryData(*this); + else if (std::is_same::value) + source.export_RowIndex(*this); + else if (std::is_same::value) + source.export_null(*this); + else + REALM_ASSERT_DEBUG(false); + } + + // Given a TCond (==, !=, >, <, >=, <=) and two Value, return index of first match + template + REALM_FORCEINLINE static size_t compare(Value* left, Value* right) + { + TCond c; + + if (!left->m_from_link_list && !right->m_from_link_list) { + // Compare values one-by-one (one value is one row; no link lists) + size_t min = minimum(left->ValueBase::m_values, right->ValueBase::m_values); + for (size_t m = 0; m < min; m++) { + + if (c(left->m_storage[m], right->m_storage[m], left->m_storage.is_null(m), + right->m_storage.is_null(m))) + return m; + } + } + else if (left->m_from_link_list && right->m_from_link_list) { + // FIXME: Many-to-many links not supported yet. Need to specify behaviour + REALM_ASSERT_DEBUG(false); + } + else if (!left->m_from_link_list && right->m_from_link_list) { + // Right values come from link list. Left must come from single row. Semantics: Match if at least 1 + // linked-to-value fulfills the condition + REALM_ASSERT_DEBUG(left->m_values > 0); + for (size_t r = 0; r < right->m_values; r++) { + if (c(left->m_storage[0], right->m_storage[r], left->m_storage.is_null(0), + right->m_storage.is_null(r))) + return 0; + } + } + else if (left->m_from_link_list && !right->m_from_link_list) { + // Same as above, but with left values coming from link list. + REALM_ASSERT_DEBUG(right->m_values > 0); + for (size_t l = 0; l < left->m_values; l++) { + if (c(left->m_storage[l], right->m_storage[0], left->m_storage.is_null(l), + right->m_storage.is_null(0))) + return 0; + } + } + + return not_found; // no match + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return make_subexpr>(*this); + } + + NullableVector m_storage; +}; + +class ConstantStringValue : public Value { +public: + ConstantStringValue(const StringData& string) + : Value() + , m_string(string.is_null() ? util::none : util::make_optional(std::string(string))) + { + init(false, ValueBase::default_size, m_string); + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new ConstantStringValue(*this)); + } + +private: + ConstantStringValue(const ConstantStringValue& other) + : Value() + , m_string(other.m_string) + { + init(other.m_from_link_list, other.m_values, m_string); + } + + util::Optional m_string; +}; + +// All overloads where left-hand-side is L: +// +// left-hand-side operator right-hand-side +// L +, -, *, /, <, >, ==, !=, <=, >= Subexpr2 +// +// For L = R = {int, int64_t, float, double, Timestamp}: +// Compare numeric values +template +Query operator>(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} + +template +Query operator<(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} + +// Arithmetic +template +Operator::type>> operator+(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} + +// Unary operators +template +UnaryOperator> power(const Subexpr2& left) +{ + return {left.clone()}; +} + +// Classes used for LinkMap (see below). +struct LinkMapFunction { + // Your consume() method is given row index of the linked-to table as argument, and you must return whether or + // not you want the LinkMapFunction to exit (return false) or continue (return true) harvesting the link tree + // for the current main table row index (it will be a link tree if you have multiple type_LinkList columns + // in a link()->link() query. + virtual bool consume(size_t row_index) = 0; +}; + +struct FindNullLinks : public LinkMapFunction { + bool consume(size_t row_index) override + { + static_cast(row_index); + m_has_link = true; + return false; // we've found a row index, so this can't be a null-link, so exit link harvesting + } + + bool m_has_link = false; +}; + +struct MakeLinkVector : public LinkMapFunction { + MakeLinkVector(std::vector& result) + : m_links(result) + { + } + + bool consume(size_t row_index) override + { + m_links.push_back(row_index); + return true; // continue evaluation + } + std::vector& m_links; +}; + +struct CountLinks : public LinkMapFunction { + bool consume(size_t) override + { + m_link_count++; + return true; + } + + size_t result() const + { + return m_link_count; + } + + size_t m_link_count = 0; +}; + + +/* +The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on +the value payload they point at). + +MapLink::map_links() takes a row index of the link column as argument and follows any link chain stated in the query +(through the link()->link() methods) until the final payload table is reached, and then applies LinkMapFunction on +the linked-to row index(es). + +If all link columns are type_Link, then LinkMapFunction is only invoked for a single row index. If one or more +columns are type_LinkList, then it may result in multiple row indexes. + +The reason we use this map pattern is that we can exit the link-tree-traversal as early as possible, e.g. when we've +found the first link that points to row '5'. Other solutions could be a std::vector harvest_all_links(), or an +iterator pattern. First solution can't exit, second solution requires internal state. +*/ +class LinkMap { +public: + LinkMap() = default; + LinkMap(const Table* table, std::vector columns) + : m_link_column_indexes(std::move(columns)) + { + set_base_table(table); + } + + LinkMap(LinkMap const& other, QueryNodeHandoverPatches* patches) + : LinkMap(other) + { + if (!patches) + return; + + m_link_column_indexes.clear(); + const Table* table = base_table(); + m_tables.clear(); + for (auto column : m_link_columns) { + m_link_column_indexes.push_back(column->get_column_index()); + if (table->get_real_column_type(m_link_column_indexes.back()) == col_type_BackLink) + table = &static_cast(column)->get_origin_table(); + else + table = &static_cast(column)->get_target_table(); + } + } + + void set_base_table(const Table* table) + { + if (table == base_table()) + return; + + m_tables.clear(); + m_tables.push_back(table); + m_link_columns.clear(); + m_link_types.clear(); + m_only_unary_links = true; + + for (size_t link_column_index : m_link_column_indexes) { + // Link column can be either LinkList or single Link + const Table* t = m_tables.back(); + ColumnType type = t->get_real_column_type(link_column_index); + REALM_ASSERT(Table::is_link_type(type) || type == col_type_BackLink); + m_link_types.push_back(type); + + if (type == col_type_LinkList) { + const LinkListColumn& cll = t->get_column_link_list(link_column_index); + m_link_columns.push_back(&cll); + m_only_unary_links = false; + m_tables.push_back(&cll.get_target_table()); + } + else if (type == col_type_Link) { + const LinkColumn& cl = t->get_column_link(link_column_index); + m_link_columns.push_back(&cl); + m_tables.push_back(&cl.get_target_table()); + } + else if (type == col_type_BackLink) { + const BacklinkColumn& bl = t->get_column_backlink(link_column_index); + m_link_columns.push_back(&bl); + m_only_unary_links = false; + m_tables.push_back(&bl.get_origin_table()); + } + } + } + + void verify_columns() const + { + for (size_t i = 0; i < m_link_column_indexes.size(); i++) { + m_tables[i]->verify_column(m_link_column_indexes[i], m_link_columns[i]); + } + } + + std::vector get_links(size_t index) + { + std::vector res; + get_links(index, res); + return res; + } + + size_t count_links(size_t row) + { + CountLinks counter; + map_links(row, counter); + return counter.result(); + } + + void map_links(size_t row, LinkMapFunction& lm) + { + map_links(0, row, lm); + } + + bool only_unary_links() const + { + return m_only_unary_links; + } + + const Table* base_table() const + { + return m_tables.empty() ? nullptr : m_tables[0]; + } + + const Table* target_table() const + { + REALM_ASSERT(!m_tables.empty()); + return m_tables.back(); + } + + std::vector m_link_columns; + +private: + void map_links(size_t column, size_t row, LinkMapFunction& lm) + { + bool last = (column + 1 == m_link_columns.size()); + ColumnType type = m_link_types[column]; + if (type == col_type_Link) { + const LinkColumn& cl = *static_cast(m_link_columns[column]); + size_t r = to_size_t(cl.get(row)); + if (r == 0) + return; + r--; // LinkColumn stores link to row N as N + 1 + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + else if (type == col_type_LinkList) { + const LinkListColumn& cll = *static_cast(m_link_columns[column]); + ConstLinkViewRef lvr = cll.get(row); + for (size_t t = 0; t < lvr->size(); t++) { + size_t r = lvr->get(t).get_index(); + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + } + else if (type == col_type_BackLink) { + const BacklinkColumn& bl = *static_cast(m_link_columns[column]); + size_t count = bl.get_backlink_count(row); + for (size_t i = 0; i < count; ++i) { + size_t r = bl.get_backlink(row, i); + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + } + } + + + void get_links(size_t row, std::vector& result) + { + MakeLinkVector mlv = MakeLinkVector(result); + map_links(row, mlv); + } + + std::vector m_link_column_indexes; + std::vector m_link_types; + std::vector m_tables; + bool m_only_unary_links = true; + + template + friend Query compare(const Subexpr2&, const ConstRow&); +}; + +template +Query string_compare(const Columns& left, T right, bool case_insensitive); +template +Query string_compare(const Columns& left, const Columns& right, bool case_insensitive); + +template +Value make_value_for_link(bool only_unary_links, size_t size) +{ + Value value; + if (only_unary_links) { + REALM_ASSERT(size <= 1); + value.init(false, 1); + value.m_storage.set_null(0); + } + else { + value.init(true, size); + } + return value; +} + + +// If we add a new Realm type T and quickly want Query support for it, then simply inherit from it like +// `template <> class Columns : public SimpleQuerySupport` and you're done. Any operators of the set +// { ==, >=, <=, !=, >, < } that are supported by T will be supported by the "query expression syntax" +// automatically. NOTE: This method of Query support will be slow because it goes through Table::get. +// To get faster Query support, either add SequentialGetter support (faster) or create a query_engine.hpp +// node for it (super fast). + +template +class SimpleQuerySupport : public Subexpr2 { +public: + SimpleQuerySupport(size_t column, const Table* table, std::vector links = {}) + : m_column_ndx(column) + , m_link_map(table, std::move(links)) + { + m_column = &m_link_map.target_table()->get_column_base(m_column_ndx); + } + + bool is_nullable() const noexcept + { + return m_link_map.base_table()->is_nullable(m_column->get_column_index()); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + if (table != get_base_table()) { + m_link_map.set_base_table(table); + m_column = &m_link_map.target_table()->get_column_base(m_column_ndx); + } + } + + void verify_column() const override + { + // verify links + m_link_map.verify_columns(); + // verify target table + const Table* target_table = m_link_map.target_table(); + if (target_table && m_column_ndx != npos) { + target_table->verify_column(m_column_ndx, m_column); + } + } + + void evaluate(size_t index, ValueBase& destination) override + { + Value& d = static_cast&>(destination); + size_t col = column_ndx(); + + if (links_exist()) { + std::vector links = m_link_map.get_links(index); + Value v = make_value_for_link(m_link_map.only_unary_links(), links.size()); + + for (size_t t = 0; t < links.size(); t++) { + size_t link_to = links[t]; + v.m_storage.set(t, m_link_map.target_table()->template get(col, link_to)); + } + destination.import(v); + } + else { + // Not a link column + const Table* target_table = m_link_map.target_table(); + for (size_t t = 0; t < destination.m_values && index + t < target_table->size(); t++) { + d.m_storage.set(t, target_table->get(col, index + t)); + } + } + } + + bool links_exist() const + { + return m_link_map.m_link_columns.size() > 0; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches = nullptr) const override + { + return make_subexpr>(static_cast&>(*this), patches); + } + + SimpleQuerySupport(SimpleQuerySupport const& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_column_ndx(other.m_column_ndx) + , m_column(other.m_column) + , m_link_map(other.m_link_map, patches) + { + if (patches && m_column) { + m_column_ndx = column_ndx(); + m_column = nullptr; + } + } + + size_t column_ndx() const + { + return m_column->get_column_index(); + } + + SizeOperator> size() + { + return SizeOperator>(this->clone(nullptr)); + } + +private: + // Column index of payload column of m_table + mutable size_t m_column_ndx; + const ColumnBase* m_column; + LinkMap m_link_map; +}; + + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + + +template <> +class Columns : public SimpleQuerySupport { +public: + using SimpleQuerySupport::SimpleQuerySupport; + + Query equal(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query equal(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query not_equal(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query not_equal(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query begins_with(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query begins_with(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query ends_with(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query ends_with(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query contains(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query contains(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query like(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query like(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } +}; + +template +Query string_compare(const Columns& left, T right, bool case_sensitive) +{ + StringData sd(right); + if (case_sensitive) + return create(sd, left); + else + return create(sd, left); +} + +template +Query string_compare(const Columns& left, const Columns& right, bool case_sensitive) +{ + if (case_sensitive) + return make_expression>(right.clone(), left.clone()); + else + return make_expression>(right.clone(), left.clone()); +} + +// Columns == Columns +inline Query operator==(const Columns& left, const Columns& right) +{ + return string_compare(left, right, true); +} + +// Columns != Columns +inline Query operator!=(const Columns& left, const Columns& right) +{ + return string_compare(left, right, true); +} + +// String == Columns +template +Query operator==(T left, const Columns& right) +{ + return operator==(right, left); +} + +// String != Columns +template +Query operator!=(T left, const Columns& right) +{ + return operator!=(right, left); +} + +// Columns == String +template +Query operator==(const Columns& left, T right) +{ + return string_compare(left, right, true); +} + +// Columns != String +template +Query operator!=(const Columns& left, T right) +{ + return string_compare(left, right, true); +} + + +inline Query operator==(const Columns& left, BinaryData right) +{ + return create(right, left); +} + +inline Query operator==(BinaryData left, const Columns& right) +{ + return create(left, right); +} + +inline Query operator!=(const Columns& left, BinaryData right) +{ + return create(right, left); +} + +inline Query operator!=(BinaryData left, const Columns& right) +{ + return create(left, right); +} + + +// This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload* +// in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently +// only "find first null link" and "find first non-null link" is supported. More will be added later. When we add +// more, I propose to remove the template argument from this class and instead template it by +// a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion. +template +class UnaryLinkCompare : public Expression { +public: + UnaryLinkCompare(LinkMap lm) + : m_link_map(std::move(lm)) + { + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + } + + // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as + // any linked-to payload tables + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + size_t find_first(size_t start, size_t end) const override + { + for (; start < end;) { + std::vector l = m_link_map.get_links(start); + // We have found a Link which is NULL, or LinkList with 0 entries. Return it as match. + + FindNullLinks fnl; + m_link_map.map_links(start, fnl); + if (fnl.m_has_link == has_links) + return start; + + start++; + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new UnaryLinkCompare(*this, patches)); + } + +private: + UnaryLinkCompare(const UnaryLinkCompare& other, QueryNodeHandoverPatches* patches = nullptr) + : Expression(other) + , m_link_map(other.m_link_map, patches) + { + } + + mutable LinkMap m_link_map; +}; + +class LinkCount : public Subexpr2 { +public: + LinkCount(LinkMap link_map) + : m_link_map(std::move(link_map)) + { + } + LinkCount(LinkCount const& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + size_t count = m_link_map.count_links(index); + destination.import(Value(false, 1, count)); + } + +private: + LinkMap m_link_map; +}; + +template +class SizeOperator : public Subexpr2 { +public: + SizeOperator(std::unique_ptr left) + : m_expr(std::move(left)) + { + } + + // See comment in base class + void set_base_table(const Table* table) override + { + m_expr->set_base_table(table); + } + + void verify_column() const override + { + m_expr->verify_column(); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_expr->get_base_table(); + } + + // destination = operator(left) + void evaluate(size_t index, ValueBase& destination) override + { + REALM_ASSERT_DEBUG(dynamic_cast*>(&destination) != nullptr); + Value* d = static_cast*>(&destination); + REALM_ASSERT(d); + + Value v; + m_expr->evaluate(index, v); + + size_t sz = v.m_values; + d->init(v.m_from_link_list, sz); + + for (size_t i = 0; i < sz; i++) { + auto elem = v.m_storage.get(i); + if (!elem) { + d->m_storage.set_null(i); + } + else { + d->m_storage.set(i, oper()(*elem)); + } + } + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SizeOperator(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_expr->apply_handover_patch(patches, group); + } + +private: + SizeOperator(const SizeOperator& other, QueryNodeHandoverPatches* patches) + : m_expr(other.m_expr->clone(patches)) + { + } + + typedef typename oper::type T; + std::unique_ptr m_expr; +}; + +struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch { + std::unique_ptr row_patch; +}; + +class ConstantRowValue : public Subexpr2 { +public: + ConstantRowValue(const ConstRow& row) + : m_row(row) + { + } + + void set_base_table(const Table*) override + { + } + + void verify_column() const override + { + } + + const Table* get_base_table() const override + { + return nullptr; + } + + void evaluate(size_t, ValueBase& destination) override + { + if (m_row.is_attached()) { + Value v(RowIndex(m_row.get_index())); + destination.import(v); + } + else { + Value v(RowIndex::Detached); + destination.import(v); + } + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new ConstantRowValue(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_row.apply_and_consume_patch(patch->row_patch, group); + } + +private: + ConstantRowValue(const ConstantRowValue& source, QueryNodeHandoverPatches* patches) + : m_row(patches ? ConstRow() : source.m_row) + { + if (!patches) + return; + + std::unique_ptr patch(new ConstantRowValueHandoverPatch); + ConstRow::generate_patch(source.m_row, patch->row_patch); + patches->emplace_back(patch.release()); + } + + ConstRow m_row; +}; + +template +class SubColumns; + +// This is for LinkList and BackLink too since they're declared as typedefs of Link. +template <> +class Columns : public Subexpr2 { +public: + Query is_null() + { + if (m_link_map.m_link_columns.size() > 1) + throw std::runtime_error("Combining link() and is_null() is currently not supported"); + // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour + return make_expression>(m_link_map); + } + + Query is_not_null() + { + if (m_link_map.m_link_columns.size() > 1) + throw std::runtime_error("Combining link() and is_not_null() is currently not supported"); + // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour + return make_expression>(m_link_map); + } + + LinkCount count() const + { + return LinkCount(m_link_map); + } + + LinkCount size() const + { + return LinkCount(m_link_map); + } + + template + SubColumns column(size_t column_ndx) const + { + return SubColumns(Columns(column_ndx, m_link_map.target_table()), m_link_map); + } + + const LinkMap& link_map() const + { + return m_link_map; + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Columns(*this, patches)); + } + + void evaluate(size_t index, ValueBase& destination) override; + + +private: + LinkMap m_link_map; + friend class Table; + + Columns(size_t column_ndx, const Table* table, const std::vector& links = {}) + : m_link_map(table, links) + { + static_cast(column_ndx); + } + Columns(const Columns& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + { + } +}; + +template <> +class Columns : public Subexpr2 { +public: + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column = &m_link_map.target_table()->get_column_table(m_column_ndx); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + m_link_map.target_table()->verify_column(m_column_ndx, m_column); + } + + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Columns(*this, patches)); + } + + void evaluate(size_t index, ValueBase& destination) override; + + SizeOperator> size() + { + return SizeOperator>(this->clone(nullptr)); + } + +private: + LinkMap m_link_map; + size_t m_column_ndx; + const SubtableColumn* m_column = nullptr; + friend class Table; + + Columns(size_t column_ndx, const Table* table, const std::vector& links = {}) + : m_link_map(table, links) + , m_column_ndx(column_ndx) + , m_column(&m_link_map.target_table()->get_column_table(column_ndx)) + { + } + + Columns(const Columns& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + , m_column_ndx(other.m_column_ndx) + , m_column(other.m_column) + { + if (m_column && patches) + m_column_ndx = m_column->get_column_index(); + } +}; + +template +Query compare(const Subexpr2& left, const ConstRow& row) +{ + static_assert(std::is_same::value || std::is_same::value, + "Links can only be compared for equality."); + const Columns* column = dynamic_cast*>(&left); + if (column) { + const LinkMap& link_map = column->link_map(); + REALM_ASSERT(link_map.target_table() == row.get_table() || !row.is_attached()); +#ifdef REALM_OLDQUERY_FALLBACK + if (link_map.m_link_columns.size() == 1) { + // We can fall back to Query::links_to for != and == operations on links, but only + // for == on link lists. This is because negating query.links_to() is equivalent to + // to "ALL linklist != row" rather than the "ANY linklist != row" semantics we're after. + if (link_map.m_link_types[0] == col_type_Link || + (link_map.m_link_types[0] == col_type_LinkList && std::is_same::value)) { + const Table* t = column->get_base_table(); + Query query(*t); + + if (std::is_same::value) { + // Negate the following `links_to`. + query.Not(); + } + query.links_to(link_map.m_link_column_indexes[0], row); + return query; + } + } +#endif + } + return make_expression>(left.clone(), make_subexpr(row)); +} + +inline Query operator==(const Subexpr2& left, const ConstRow& row) +{ + return compare(left, row); +} +inline Query operator!=(const Subexpr2& left, const ConstRow& row) +{ + return compare(left, row); +} +inline Query operator==(const ConstRow& row, const Subexpr2& right) +{ + return compare(right, row); +} +inline Query operator!=(const ConstRow& row, const Subexpr2& right) +{ + return compare(right, row); +} + +template +Query compare(const Subexpr2& left, null) +{ + static_assert(std::is_same::value || std::is_same::value, + "Links can only be compared for equality."); + return make_expression>(left.clone(), make_subexpr>()); +} + +inline Query operator==(const Subexpr2& left, null) +{ + return compare(left, null()); +} +inline Query operator!=(const Subexpr2& left, null) +{ + return compare(left, null()); +} +inline Query operator==(null, const Subexpr2& right) +{ + return compare(right, null()); +} +inline Query operator!=(null, const Subexpr2& right) +{ + return compare(right, null()); +} + + +template +class Columns : public Subexpr2 { +public: + using ColType = typename ColumnTypeTraits::column_type; + + Columns(size_t column, const Table* table, std::vector links = {}) + : m_link_map(table, std::move(links)) + , m_column_ndx(column) + , m_nullable(m_link_map.target_table()->is_nullable(m_column_ndx)) + { + } + + Columns(const Columns& other, QueryNodeHandoverPatches* patches = nullptr) + : m_link_map(other.m_link_map, patches) + , m_column_ndx(other.m_column_ndx) + , m_nullable(other.m_nullable) + { + if (!other.m_sg) + return; + + if (patches) { + m_column_ndx = other.get_column_base().get_column_index(); + } + else { + if (m_nullable && std::is_same::value) { + init(&other.get_column_base()); + } + else { + init(&other.get_column_base()); + } + } + } + + Columns& operator=(const Columns& other) + { + if (this != &other) { + m_link_map = other.m_link_map; + m_sg.reset(); + m_column_ndx = other.m_column_ndx; + m_nullable = other.m_nullable; + } + return *this; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr>(*this, patches); + } + + // See comment in base class + void set_base_table(const Table* table) override + { + if (m_sg && table == get_base_table()) + return; + + m_link_map.set_base_table(table); + m_nullable = m_link_map.target_table()->is_nullable(m_column_ndx); + + const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column_ndx); + if (m_nullable && std::is_same::value) { + init(c); + } + else { + init(c); + } + } + + void verify_column() const override + { + // verify links + m_link_map.verify_columns(); + // verify target table + const Table* target_table = m_link_map.target_table(); + if (target_table && m_column_ndx != npos) { + target_table->verify_column(m_column_ndx, &get_column_base()); + } + } + + template + void init(const ColumnBase* c) + { + REALM_ASSERT_DEBUG(dynamic_cast(c)); + if (m_sg == nullptr) { + m_sg.reset(new SequentialGetter()); + } + static_cast&>(*m_sg).init(static_cast(c)); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + template + void evaluate_internal(size_t index, ValueBase& destination) + { + REALM_ASSERT_DEBUG(m_sg.get()); + REALM_ASSERT_DEBUG(dynamic_cast*>(m_sg.get())); + + using U = typename ColType2::value_type; + auto sgc = static_cast*>(m_sg.get()); + REALM_ASSERT_DEBUG(sgc->m_column); + + if (links_exist()) { + // LinkList with more than 0 values. Create Value with payload for all fields + + std::vector links = m_link_map.get_links(index); + auto v = make_value_for_link::type>(m_link_map.only_unary_links(), + links.size()); + + for (size_t t = 0; t < links.size(); t++) { + size_t link_to = links[t]; + sgc->cache_next(link_to); + + if (sgc->m_column->is_null(link_to)) + v.m_storage.set_null(t); + else + v.m_storage.set(t, sgc->get_next(link_to)); + } + destination.import(v); + } + else { + // Not a Link column + // make sequential getter load the respective leaf to access data at column row 'index' + sgc->cache_next(index); + size_t colsize = sgc->m_column->size(); + + // Now load `ValueBase::default_size` rows from from the leaf into m_storage. If it's an integer + // leaf, then it contains the method get_chunk() which copies these values in a super fast way (first + // case of the `if` below. Otherwise, copy the values one by one in a for-loop (the `else` case). + if (std::is_same::value && index + ValueBase::default_size <= sgc->m_leaf_end) { + Value v; + + // If you want to modify 'default_size' then update Array::get_chunk() + REALM_ASSERT_3(ValueBase::default_size, ==, 8); + + auto sgc_2 = static_cast*>(m_sg.get()); + sgc_2->m_leaf_ptr->get_chunk( + index - sgc->m_leaf_start, + static_cast*>(static_cast(&v))->m_storage.m_first); + + destination.import(v); + } + else { + size_t rows = colsize - index; + if (rows > ValueBase::default_size) + rows = ValueBase::default_size; + Value::type> v(false, rows); + + for (size_t t = 0; t < rows; t++) + v.m_storage.set(t, sgc->get_next(index + t)); + + destination.import(v); + } + } + } + + // Load values from Column into destination + void evaluate(size_t index, ValueBase& destination) override + { + if (m_nullable && std::is_same::value) { + evaluate_internal(index, destination); + } + else { + evaluate_internal(index, destination); + } + } + + bool links_exist() const + { + return m_link_map.m_link_columns.size() > 0; + } + + bool is_nullable() const + { + return m_nullable; + } + + size_t column_ndx() const noexcept + { + return m_sg ? get_column_base().get_column_index() : m_column_ndx; + } + +private: + LinkMap m_link_map; + + // Fast (leaf caching) value getter for payload column (column in table on which query condition is executed) + std::unique_ptr m_sg; + + // Column index of payload column of m_table + size_t m_column_ndx; + + // set to false by default for stand-alone Columns declaration that are not yet associated with any table + // or oclumn. Call init() to update it or use a constructor that takes table + column index as argument. + bool m_nullable = false; + + const ColumnBase& get_column_base() const noexcept + { + if (m_nullable && std::is_same::value) + return *static_cast&>(*m_sg).m_column; + else + return *static_cast&>(*m_sg).m_column; + } +}; + +template +class SubColumnAggregate; +namespace aggregate_operations { +template +class Minimum; +template +class Maximum; +template +class Sum; +template +class Average; +} + +template +class SubColumns : public Subexpr { +public: + SubColumns(Columns column, LinkMap link_map) + : m_column(std::move(column)) + , m_link_map(std::move(link_map)) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return make_subexpr>(*this); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.target_table()); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + m_column.verify_column(); + } + + void evaluate(size_t, ValueBase&) override + { + // SubColumns can only be used in an expression in conjunction with its aggregate methods. + REALM_ASSERT(false); + } + + SubColumnAggregate> min() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> max() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> sum() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> average() const + { + return {m_column, m_link_map}; + } + +private: + Columns m_column; + LinkMap m_link_map; +}; + +template +class SubColumnAggregate : public Subexpr2 { +public: + SubColumnAggregate(Columns column, LinkMap link_map) + : m_column(std::move(column)) + , m_link_map(std::move(link_map)) + { + } + SubColumnAggregate(SubColumnAggregate const& other, QueryNodeHandoverPatches* patches) + : m_column(other.m_column, patches) + , m_link_map(other.m_link_map, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.target_table()); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + m_column.verify_column(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + std::sort(links.begin(), links.end()); + + Operation op; + for (size_t link_index = 0; link_index < links.size();) { + Value value; + size_t link = links[link_index]; + m_column.evaluate(link, value); + + // Columns::evaluate fetches values in chunks of ValueBase::default_size. Process all values + // within the chunk that came from rows that we link to. + const auto& value_storage = value.m_storage; + for (size_t value_index = 0; value_index < value.m_values;) { + if (!value_storage.is_null(value_index)) { + op.accumulate(value_storage[value_index]); + } + if (++link_index >= links.size()) { + break; + } + + size_t previous_link = link; + link = links[link_index]; + value_index += link - previous_link; + } + } + if (op.is_null()) { + destination.import(Value(false, 1, null())); + } + else { + destination.import(Value(false, 1, op.result())); + } + } + +private: + Columns m_column; + LinkMap m_link_map; +}; + +struct SubQueryCountHandoverPatch : QueryNodeHandoverPatch { + QueryHandoverPatch m_query; +}; + +class SubQueryCount : public Subexpr2 { +public: + SubQueryCount(Query q, LinkMap link_map) + : m_query(std::move(q)) + , m_link_map(std::move(link_map)) + { + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + std::sort(links.begin(), links.end()); + + size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, size_t link) { + return running_count + m_query.count(link, link + 1, 1); + }); + + destination.import(Value(false, 1, size_t(count))); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + if (patches) + return std::unique_ptr(new SubQueryCount(*this, patches)); + + return make_subexpr(*this); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_query.apply_patch(patch->m_query, group); + } + +private: + SubQueryCount(const SubQueryCount& other, QueryNodeHandoverPatches* patches) + : m_link_map(other.m_link_map, patches) + { + std::unique_ptr patch(new SubQueryCountHandoverPatch); + m_query = Query(other.m_query, patch->m_query, ConstSourcePayload::Copy); + patches->emplace_back(patch.release()); + } + + Query m_query; + LinkMap m_link_map; +}; + +// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp. +template +class SubQuery { +public: + SubQuery(Columns link_column, Query query) + : m_query(std::move(query)) + , m_link_map(link_column.link_map()) + { + REALM_ASSERT(m_link_map.target_table() == m_query.get_table()); + } + + SubQueryCount count() const + { + return SubQueryCount(m_query, m_link_map); + } + +private: + Query m_query; + LinkMap m_link_map; +}; + +namespace aggregate_operations { +template +class BaseAggregateOperation { + static_assert(std::is_same::value || std::is_same::value || std::is_same::value, + "Numeric aggregates can only be used with subcolumns of numeric types"); + +public: + using ResultType = R; + + void accumulate(T value) + { + m_count++; + m_result = Derived::apply(m_result, value); + } + + bool is_null() const + { + return m_count == 0; + } + ResultType result() const + { + return m_result; + } + +protected: + size_t m_count = 0; + ResultType m_result = Derived::initial_value(); +}; + +template +class Minimum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return std::numeric_limits::max(); + } + static T apply(T a, T b) + { + return std::min(a, b); + } +}; + +template +class Maximum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return std::numeric_limits::min(); + } + static T apply(T a, T b) + { + return std::max(a, b); + } +}; + +template +class Sum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return T(); + } + static T apply(T a, T b) + { + return a + b; + } + bool is_null() const + { + return false; + } +}; + +template +class Average : public BaseAggregateOperation, double> { + using Base = BaseAggregateOperation, double>; + +public: + static double initial_value() + { + return 0; + } + static double apply(double a, T b) + { + return a + b; + } + double result() const + { + return Base::m_result / Base::m_count; + } +}; +} + +template +class UnaryOperator : public Subexpr2 { +public: + UnaryOperator(std::unique_ptr left) + : m_left(std::move(left)) + { + } + + UnaryOperator(const UnaryOperator& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + { + } + + UnaryOperator& operator=(const UnaryOperator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + } + return *this; + } + + UnaryOperator(UnaryOperator&&) = default; + UnaryOperator& operator=(UnaryOperator&&) = default; + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + } + + void verify_column() const override + { + m_left->verify_column(); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_left->get_base_table(); + } + + // destination = operator(left) + void evaluate(size_t index, ValueBase& destination) override + { + Value result; + Value left; + m_left->evaluate(index, left); + result.template fun(&left); + destination.import(result); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_left->apply_handover_patch(patches, group); + } + +private: + typedef typename oper::type T; + std::unique_ptr m_left; +}; + + +template +class Operator : public Subexpr2 { +public: + Operator(std::unique_ptr left, std::unique_ptr right) + : m_left(std::move(left)) + , m_right(std::move(right)) + { + } + + Operator(const Operator& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + , m_right(other.m_right->clone(patches)) + { + } + + Operator& operator=(const Operator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + m_right = other.m_right->clone(); + } + return *this; + } + + Operator(Operator&&) = default; + Operator& operator=(Operator&&) = default; + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + void verify_column() const override + { + m_left->verify_column(); + m_right->verify_column(); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + const Table* get_base_table() const override + { + const Table* l = m_left->get_base_table(); + const Table* r = m_right->get_base_table(); + + // Queries do not support multiple different tables; all tables must be the same. + REALM_ASSERT(l == nullptr || r == nullptr || l == r); + + // nullptr pointer means expression which isn't yet associated with any table, or is a Value + return l ? l : r; + } + + // destination = operator(left, right) + void evaluate(size_t index, ValueBase& destination) override + { + Value result; + Value left; + Value right; + m_left->evaluate(index, left); + m_right->evaluate(index, right); + result.template fun(&left, &right); + destination.import(result); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_right->apply_handover_patch(patches, group); + m_left->apply_handover_patch(patches, group); + } + +private: + typedef typename oper::type T; + std::unique_ptr m_left; + std::unique_ptr m_right; +}; + + +template +class Compare : public Expression { +public: + Compare(std::unique_ptr left, std::unique_ptr right) + : m_left(std::move(left)) + , m_right(std::move(right)) + { + } + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + void verify_column() const override + { + m_left->verify_column(); + m_right->verify_column(); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + const Table* get_base_table() const override + { + const Table* l = m_left->get_base_table(); + const Table* r = m_right->get_base_table(); + + // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same. + REALM_ASSERT(l == nullptr || r == nullptr || l == r); + + // nullptr pointer means expression which isn't yet associated with any table, or is a Value + return l ? l : r; + } + + size_t find_first(size_t start, size_t end) const override + { + size_t match; + Value right; + Value left; + + for (; start < end;) { + m_left->evaluate(start, left); + m_right->evaluate(start, right); + match = Value::template compare(&left, &right); + + if (match != not_found && match + start < end) + return start + match; + + size_t rows = + (left.m_from_link_list || right.m_from_link_list) ? 1 : minimum(right.m_values, left.m_values); + start += rows; + } + + return not_found; // no match + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Compare(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_right->apply_handover_patch(patches, group); + m_left->apply_handover_patch(patches, group); + } + +private: + Compare(const Compare& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + , m_right(other.m_right->clone(patches)) + { + } + + std::unique_ptr m_left; + std::unique_ptr m_right; +}; +} +#endif // REALM_QUERY_EXPRESSION_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_operators.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_operators.hpp new file mode 100644 index 0000000..203af84 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/query_operators.hpp @@ -0,0 +1,71 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_QUERY_OPERATORS_HPP +#define REALM_QUERY_OPERATORS_HPP + +#include +#include +#include +#include + +namespace realm { + +// This is not supported in the general case +template +struct Size; + +template <> +struct Size { + int64_t operator()(StringData v) const + { + return v.size(); + } + typedef StringData type; +}; + +template <> +struct Size { + int64_t operator()(BinaryData v) const + { + return v.size(); + } + typedef BinaryData type; +}; + +template <> +struct Size { + int64_t operator()(ConstTableRef v) const + { + return v->size(); + } + typedef ConstTableRef type; +}; + +template <> +struct Size { + int64_t operator()(ConstLinkViewRef v) const + { + return v->size(); + } + typedef ConstLinkViewRef type; +}; + +} // namespace realm + +#endif // REALM_QUERY_OPERATORS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h b/Desafio Mobile iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h new file mode 100644 index 0000000..8144da6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h @@ -0,0 +1,182 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_NMMINTRIN_H +#define REALM_NMMINTRIN_H + +/* + We must support runtime detection of CPU support of SSE when distributing Realm as a closed source library. + + This is a problem on gcc and llvm: To use SSE intrinsics we need to pass -msse on the command line (to get offered + __builtin_ accessors used by intrinsics functions). However, the -msse flag allows gcc to emit SSE instructions + in its code generation/optimization. This is unwanted because the binary would crash on non-SSE CPUs. + + Since there exists no flag in gcc that enables intrinsics but probits SSE in code generation, we define our + own intrinsics to be assembled by the back end assembler and omit passing -msse to gcc. +*/ + +#ifndef _MSC_VER + +#ifdef REALM_COMPILER_SSE +#include // SSE2 (using __m128i) +#endif + +namespace realm { + +#if 0 +#ifdef REALM_COMPILER_AVX +typedef float __m256 __attribute__((__vector_size__(32), __may_alias__)); +typedef double __m256d __attribute__((__vector_size__(32), __may_alias__)); + +const int _CMP_EQ_OQ = 0x00; // Equal (ordered, non-signaling) +const int _CMP_NEQ_OQ = 0x0c; // Not-equal (ordered, non-signaling) +const int _CMP_LT_OQ = 0x11; // Less-than (ordered, non-signaling) +const int _CMP_LE_OQ = 0x12; // Less-than-or-equal (ordered, non-signaling) +const int _CMP_GE_OQ = 0x1d; // Greater-than-or-equal (ordered, non-signaling) +const int _CMP_GT_OQ = 0x1e; // Greater-than (ordered, non-signaling) + + +template +static int movemask_cmp_ps(__m256* y1, __m256* y2) +{ + int ret; + __asm__("vmovaps %0, %%ymm0" : : "m"(*y1) : "%xmm0" ); + __asm__("vmovaps %0, %%ymm1" : : "m"(*y2) : "%xmm1" ); + __asm__("vcmpps %0, %%ymm0, %%ymm1, %%ymm0" : : "I"(op) : "%xmm0" ); + __asm__("vmovmskps %%ymm0, %0" : "=r"(ret) : : ); + return ret; +} + +template +static inline int movemask_cmp_pd(__m256d* y1, __m256d* y2) +{ + int ret; + __asm__("vmovapd %0, %%ymm0" : : "m"(*y1) : "%xmm0" ); + __asm__("vmovapd %0, %%ymm1" : : "m"(*y2) : "%xmm1" ); + __asm__("vcmppd %0, %%ymm0, %%ymm1, %%ymm0" : : "I"(op) : "%xmm0" ); + __asm__("vmovmskpd %%ymm0, %0" : "=r"(ret) : : ); + return ret; +} + + + +static inline int movemask_cmp_ps(__m256* y1, __m256* y2, int op) +{ + // todo, use constexpr; + if (op == _CMP_EQ_OQ) + return movemask_cmp_ps<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_NEQ_OQ) + return movemask_cmp_ps<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_LT_OQ) + return movemask_cmp_ps<_CMP_LT_OQ>(y1, y2); + else if (op == _CMP_LE_OQ) + return movemask_cmp_ps<_CMP_LE_OQ>(y1, y2); + else if (op == _CMP_GE_OQ) + return movemask_cmp_ps<_CMP_GE_OQ>(y1, y2); + else if (op == _CMP_GT_OQ) + return movemask_cmp_ps<_CMP_GT_OQ>(y1, y2); + + REALM_ASSERT(false); + return 0; +} + +static inline int movemask_cmp_pd(__m256d* y1, __m256d* y2, int op) +{ + // todo, use constexpr; + if (op == _CMP_EQ_OQ) + return movemask_cmp_pd<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_NEQ_OQ) + return movemask_cmp_pd<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_LT_OQ) + return movemask_cmp_pd<_CMP_LT_OQ>(y1, y2); + else if (op == _CMP_LE_OQ) + return movemask_cmp_pd<_CMP_LE_OQ>(y1, y2); + else if (op == _CMP_GE_OQ) + return movemask_cmp_pd<_CMP_GE_OQ>(y1, y2); + else if (op == _CMP_GT_OQ) + return movemask_cmp_pd<_CMP_GT_OQ>(y1, y2); + + REALM_ASSERT(false); + return 0; +} + + +#endif +#endif + +// Instructions introduced by SSE 3 and 4.2 +static inline __m128i _mm_cmpgt_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpgtq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i _mm_cmpeq_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpeqq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi8_epi16(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxbw %1, %0" : "=x" (xmm1) : "xm" (xmm2) : "xmm1"); + return xmm1; +} +static inline __m128i __attribute__((always_inline)) _mm_cvtepi16_epi32(__m128i xmm2) +{ + __m128i xmm1; + asm("pmovsxwd %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi32_epi64(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxdq %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +} // namespace realm + +#endif +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/replication.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/replication.hpp new file mode 100644 index 0000000..7d7299d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/replication.hpp @@ -0,0 +1,514 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_REPLICATION_HPP +#define REALM_REPLICATION_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace util { +class Logger; +} + +// FIXME: Be careful about the possibility of one modification function being called by another where both do +// transaction logging. + +// FIXME: The current table/subtable selection scheme assumes that a TableRef of a subtable is not accessed after any +// modification of one of its ancestor tables. + +// FIXME: Checking on same Table* requires that ~Table checks and nullifies on match. Another option would be to store +// m_selected_table as a TableRef. Yet another option would be to assign unique identifiers to each Table instance via +// Allocator. Yet another option would be to explicitely invalidate subtables recursively when parent is modified. + +/// Replication is enabled by passing an instance of an implementation of this +/// class to the SharedGroup constructor. +class Replication : public _impl::TransactLogConvenientEncoder, protected _impl::TransactLogStream { +public: + // Be sure to keep this type aligned with what is actually used in + // SharedGroup. + using version_type = _impl::History::version_type; + using InputStream = _impl::NoCopyInputStream; + class TransactLogApplier; + class Interrupted; // Exception + class SimpleIndexTranslator; + + virtual std::string get_database_path() = 0; + + /// Called during construction of the associated SharedGroup object. + /// + /// \param shared_group The assocoated SharedGroup object. + virtual void initialize(SharedGroup& shared_group) = 0; + + /// Called by the associated SharedGroup object when a session is + /// initiated. A *session* is a sequence of of temporally overlapping + /// accesses to a specific Realm file, where each access consists of a + /// SharedGroup object through which the Realm file is open. Session + /// initiation occurs during the first opening of the Realm file within such + /// a session. + /// + /// Session initiation fails if this function throws. + /// + /// \param version The current version of the associated Realm. Out-of-Realm + /// history implementation can use this to trim off history entries that + /// were successfully added to the history, but for which the corresponding + /// subsequent commits on the Realm file failed. + /// + /// The default implementation does nothing. + virtual void initiate_session(version_type version) = 0; + + /// Called by the associated SharedGroup object when a session is + /// terminated. See initiate_session() for the definition of a + /// session. Session termination occurs upon closing the Realm through the + /// last SharedGroup object within the session. + /// + /// The default implementation does nothing. + virtual void terminate_session() noexcept = 0; + + /// \defgroup replication_transactions + //@{ + + /// From the point of view of the Replication class, a transaction is + /// initiated when, and only when the associated SharedGroup object calls + /// initiate_transact() and the call is successful. The associated + /// SharedGroup object must terminate every initiated transaction either by + /// calling finalize_commit() or by calling abort_transact(). It may only + /// call finalize_commit(), however, after calling prepare_commit(), and + /// only when prepare_commit() succeeds. If prepare_commit() fails (i.e., + /// throws) abort_transact() must still be called. + /// + /// The associated SharedGroup object is supposed to terminate a transaction + /// as soon as possible, and is required to terminate it before attempting + /// to initiate a new one. + /// + /// initiate_transact() is called by the associated SharedGroup object as + /// part of the initiation of a transaction, and at a time where the caller + /// has acquired exclusive write access to the local Realm. The Replication + /// implementation is allowed to perform "precursor transactions" on the + /// local Realm at this time. During the initiated transaction, the + /// associated SharedGroup object must inform the Replication object of all + /// modifying operations by calling set_value() and friends. + /// + /// FIXME: There is currently no way for implementations to perform + /// precursor transactions, since a regular transaction would cause a dead + /// lock when it tries to acquire a write lock. Consider giving access to + /// special non-locking precursor transactions via an extra argument to this + /// function. + /// + /// prepare_commit() serves as the first phase of a two-phase commit. This + /// function is called by the associated SharedGroup object immediately + /// before the commit operation on the local Realm. The associated + /// SharedGroup object will then, as the second phase, either call + /// finalize_commit() or abort_transact() depending on whether the commit + /// operation succeeded or not. The Replication implementation is allowed to + /// modify the Realm via the associated SharedGroup object at this time + /// (important to in-Realm histories). + /// + /// initiate_transact() and prepare_commit() are allowed to block the + /// calling thread if, for example, they need to communicate over the + /// network. If a calling thread is blocked in one of these functions, it + /// must be possible to interrupt the blocking operation by having another + /// thread call interrupt(). The contract is as follows: When interrupt() is + /// called, then any execution of initiate_transact() or prepare_commit(), + /// initiated before the interruption, must complete without blocking, or + /// the execution must be aborted by throwing an Interrupted exception. If + /// initiate_transact() or prepare_commit() throws Interrupted, it counts as + /// a failed operation. + /// + /// finalize_commit() is called by the associated SharedGroup object + /// immediately after a successful commit operation on the local Realm. This + /// happens at a time where modification of the Realm is no longer possible + /// via the associated SharedGroup object. In the case of in-Realm + /// histories, the changes are automatically finalized as part of the commit + /// operation performed by the caller prior to the invocation of + /// finalize_commit(), so in that case, finalize_commit() might not need to + /// do anything. + /// + /// abort_transact() is called by the associated SharedGroup object to + /// terminate a transaction without committing. That is, any transaction + /// that is not terminated by finalize_commit() is terminated by + /// abort_transact(). This could be due to an explicit rollback, or due to a + /// failed commit attempt. + /// + /// Note that finalize_commit() and abort_transact() are not allowed to + /// throw. + /// + /// \param current_version The version of the snapshot that the current + /// transaction is based on. + /// + /// \param history_updated Pass true only when the history has already been + /// updated to reflect the currently bound snapshot, such as when + /// _impl::History::update_early_from_top_ref() was called during the + /// transition from a read transaction to the current write transaction. + /// + /// \return prepare_commit() returns the version of the new snapshot + /// produced by the transaction. + /// + /// \throw Interrupted Thrown by initiate_transact() and prepare_commit() if + /// a blocking operation was interrupted. + + void initiate_transact(version_type current_version, bool history_updated); + version_type prepare_commit(version_type current_version); + void finalize_commit() noexcept; + void abort_transact() noexcept; + + //@} + + + /// Interrupt any blocking call to a function in this class. This function + /// may be called asyncronously from any thread, but it may not be called + /// from a system signal handler. + /// + /// Some of the public function members of this class may block, but only + /// when it it is explicitely stated in the documention for those functions. + /// + /// FIXME: Currently we do not state blocking behaviour for all the + /// functions that can block. + /// + /// After any function has returned with an interruption indication, the + /// only functions that may safely be called are abort_transact() and the + /// destructor. If a client, after having received an interruption + /// indication, calls abort_transact() and then clear_interrupt(), it may + /// resume normal operation through this Replication object. + void interrupt() noexcept; + + /// May be called by a client to reset this Replication object after an + /// interrupted transaction. It is not an error to call this function in a + /// situation where no interruption has occured. + void clear_interrupt() noexcept; + + /// Apply a changeset to the specified group. + /// + /// \param changeset The changes to be applied. + /// + /// \param group The destination group to apply the changeset to. + /// + /// \param logger If specified, and the library was compiled in debug mode, + /// then a line describing each individual operation is writted to the + /// specified logger. + /// + /// \throw BadTransactLog If the changeset could not be successfully parsed, + /// or ended prematurely. + static void apply_changeset(InputStream& changeset, Group& group, util::Logger* logger = nullptr); + + /// CAUTION: These values are stored in Realm files, so value reassignment + /// is not allowed. + enum HistoryType { + /// No history available. No support for either continuous transactions + /// or inter-client synchronization. + hist_None = 0, + + /// Out-of-Realm history supporting continuous transactions. + /// + /// NOTE: This history type is no longer in use. The value needs to stay + /// reserved in case someone tries to open an old Realm file. + hist_OutOfRealm = 1, + + /// In-Realm history supporting continuous transactions + /// (make_in_realm_history()). + hist_InRealm = 2, + + /// In-Realm history supporting continuous transactions and client-side + /// synchronization protocol (realm::sync::ClientHistory). + hist_SyncClient = 3, + + /// In-Realm history supporting continuous transactions and server-side + /// synchronization protocol (realm::_impl::ServerHistory). + hist_SyncServer = 4 + }; + + /// Returns the type of history maintained by this Replication + /// implementation, or \ref hist_None if no history is maintained by it. + /// + /// This type is used to ensure that all session participants agree on + /// history type, and that the Realm file contains a compatible type of + /// history, at the beginning of a new session. + /// + /// As a special case, if there is no top array (Group::m_top) at the + /// beginning of a new session, then the history type is still undecided and + /// all history types (as returned by get_history_type()) are threfore + /// allowed for the session initiator. Note that this case only arises if + /// there was no preceding session, or if no transaction was sucessfully + /// committed during any of the preceding sessions. As soon as a transaction + /// is successfully committed, the Realm contains at least a top array, and + /// from that point on, the history type is generally fixed, although still + /// subject to certain allowed changes (as mentioned below). + /// + /// For the sake of backwards compatibility with older Realm files that does + /// not store any history type, the following rule shall apply: + /// + /// - If the top array of a Realm file (Group::m_top) does not contain a + /// history type, because it is too short, it shall be understood as + /// implicitly storing the type \ref hist_None. + /// + /// Note: In what follows, the meaning of *preceding session* is: The last + /// preceding session that modified the Realm by sucessfully committing a + /// new snapshot. + /// + /// It shall be allowed to switch to a \ref hist_InRealm history if the + /// stored history type is \ref hist_None. This can be done simply by adding + /// a new history to the Realm file. This is possible because histories of + /// this type a transient in nature, and need not survive from one session + /// to the next. + /// + /// On the other hand, as soon as a history of type \ref hist_InRealm is + /// added to a Realm file, that history type is binding for all subsequent + /// sessions. In theory, this constraint is not necessary, and a later + /// switch to \ref hist_None would be possible because of the transient + /// nature of it, however, because the \ref hist_InRealm history remains in + /// the Realm file, there are practical complications, and for that reason, + /// such switching shall not be supported. + /// + /// The \ref hist_SyncClient history type can only be used if the stored + /// history type is also \ref hist_SyncClient, or when there is no top array + /// yet. Likewise, the \ref hist_SyncServer history type can only be used if + /// the stored history type is also \ref hist_SyncServer, or when there is + /// no top array yet. Additionally, when the stored history type is \ref + /// hist_SyncClient or \ref hist_SyncServer, then all subsequent sessions + /// must have the same type. These restrictions apply because such a history + /// needs to be maintained persistently across sessions. + /// + /// In general, if there is no stored history type (no top array) at the + /// beginning of a new session, or if the stored type disagrees with what is + /// returned by get_history_type() (which is possible due to particular + /// allowed changes of history type), the actual history type (as returned + /// by get_history_type()) used during that session, must be stored in the + /// Realm during the first successfully committed transaction in that + /// session. But note that there is still no need to expand the top array to + /// store the history type \ref hist_None, due to the rule mentioned above. + /// + /// This function must return \ref hist_None when, and only when + /// get_history() returns null. + virtual HistoryType get_history_type() const noexcept = 0; + + /// Returns the schema version of the history maintained by this Replication + /// implementation, or 0 if no history is maintained by it. All session + /// participants must agree on history schema version. + /// + /// Must return 0 if get_history_type() returns \ref hist_None. + virtual int get_history_schema_version() const noexcept = 0; + + /// Implementation may assume that this function is only ever called with a + /// stored schema version that is less than what was returned by + /// get_history_schema_version(). + virtual bool is_upgradable_history_schema(int stored_schema_version) const noexcept = 0; + + /// The implementation may assume that this function is only ever called if + /// is_upgradable_history_schema() was called with the same stored schema + /// version, and returned true. This implies that the specified stored + /// schema version is always strictly less than what was returned by + /// get_history_schema_version(). + virtual void upgrade_history_schema(int stored_schema_version) = 0; + + /// Returns an object that gives access to the history of changesets in a + /// way that allows for continuous transactions to work + /// (Group::advance_transact() in particular). + /// + /// This function must return null when, and only when get_history_type() + /// returns \ref hist_None. + virtual _impl::History* get_history() = 0; + + /// Returns false by default, but must return true if, and only if this + /// history object represents a session participant that is a sync + /// agent. This is used to enforce the "maximum one sync agent per session" + /// constraint. + virtual bool is_sync_agent() const noexcept; + + virtual ~Replication() noexcept + { + } + +protected: + Replication(); + + + //@{ + + /// do_initiate_transact() is called by initiate_transact(), and likewise + /// for do_prepare_commit), do_finalize_commit(), and do_abort_transact(). + /// + /// With respect to exception safety, the Replication implementation has two + /// options: It can prepare to accept the accumulated changeset in + /// do_prepapre_commit() by allocating all required resources, and delay the + /// actual acceptance to do_finalize_commit(), which requires that the final + /// acceptance can be done without any risk of failure. Alternatively, the + /// Replication implementation can fully accept the changeset in + /// do_prepapre_commit() (allowing for failure), and then discard that + /// changeset during the next invocation of do_initiate_transact() if + /// `current_version` indicates that the previous transaction failed. + + virtual void do_initiate_transact(version_type current_version, bool history_updated) = 0; + virtual version_type do_prepare_commit(version_type orig_version) = 0; + virtual void do_finalize_commit() noexcept = 0; + virtual void do_abort_transact() noexcept = 0; + + //@} + + + virtual void do_interrupt() noexcept = 0; + + virtual void do_clear_interrupt() noexcept = 0; + + friend class _impl::TransactReverser; +}; + + +class Replication::Interrupted : public std::exception { +public: + const char* what() const noexcept override + { + return "Interrupted"; + } +}; + + +class TrivialReplication : public Replication { +public: + ~TrivialReplication() noexcept + { + } + +protected: + typedef Replication::version_type version_type; + + TrivialReplication(const std::string& database_file); + + virtual version_type prepare_changeset(const char* data, size_t size, version_type orig_version) = 0; + virtual void finalize_changeset() noexcept = 0; + + static void apply_changeset(const char* data, size_t size, SharedGroup& target, util::Logger* logger = nullptr); + + bool is_history_updated() const noexcept; + + BinaryData get_uncommitted_changes() const noexcept; + + std::string get_database_path() override; + void initialize(SharedGroup&) override; + void do_initiate_transact(version_type, bool) override; + version_type do_prepare_commit(version_type orig_version) override; + void do_finalize_commit() noexcept override; + void do_abort_transact() noexcept override; + void do_interrupt() noexcept override; + void do_clear_interrupt() noexcept override; + void transact_log_reserve(size_t n, char** new_begin, char** new_end) override; + void transact_log_append(const char* data, size_t size, char** new_begin, char** new_end) override; + +private: + const std::string m_database_file; + util::Buffer m_transact_log_buffer; + bool m_history_updated; + void internal_transact_log_reserve(size_t, char** new_begin, char** new_end); + + size_t transact_log_size(); +}; + + +// Implementation: + +inline Replication::Replication() + : _impl::TransactLogConvenientEncoder(static_cast<_impl::TransactLogStream&>(*this)) +{ +} + +inline void Replication::initiate_transact(version_type current_version, bool history_updated) +{ + do_initiate_transact(current_version, history_updated); + reset_selection_caches(); +} + +inline Replication::version_type Replication::prepare_commit(version_type orig_version) +{ + return do_prepare_commit(orig_version); +} + +inline void Replication::finalize_commit() noexcept +{ + do_finalize_commit(); +} + +inline void Replication::abort_transact() noexcept +{ + do_abort_transact(); +} + +inline void Replication::interrupt() noexcept +{ + do_interrupt(); +} + +inline void Replication::clear_interrupt() noexcept +{ + do_clear_interrupt(); +} + +inline bool Replication::is_sync_agent() const noexcept +{ + return false; +} + +inline TrivialReplication::TrivialReplication(const std::string& database_file) + : m_database_file(database_file) +{ +} + +inline bool TrivialReplication::is_history_updated() const noexcept +{ + return m_history_updated; +} + +inline BinaryData TrivialReplication::get_uncommitted_changes() const noexcept +{ + const char* data = m_transact_log_buffer.data(); + size_t size = write_position() - data; + return BinaryData(data, size); +} + +inline size_t TrivialReplication::transact_log_size() +{ + return write_position() - m_transact_log_buffer.data(); +} + +inline void TrivialReplication::transact_log_reserve(size_t n, char** new_begin, char** new_end) +{ + internal_transact_log_reserve(n, new_begin, new_end); +} + +inline void TrivialReplication::internal_transact_log_reserve(size_t n, char** new_begin, char** new_end) +{ + char* data = m_transact_log_buffer.data(); + size_t size = write_position() - data; + m_transact_log_buffer.reserve_extra(size, n); + data = m_transact_log_buffer.data(); // May have changed + *new_begin = data + size; + *new_end = data + m_transact_log_buffer.size(); +} + +} // namespace realm + +#endif // REALM_REPLICATION_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/row.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/row.hpp new file mode 100644 index 0000000..6261e51 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/row.hpp @@ -0,0 +1,825 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_ROW_HPP +#define REALM_ROW_HPP + +#include + +#include +#include +#include +#include +#include + +namespace realm { + +template +class BasicRow; + + +/// This class is a "mixin" and contains the common set of functions for several +/// distinct row-like classes. +/// +/// There is a direct and natural correspondance between the functions in this +/// class and functions in Table of the same name. For example: +/// +/// table[i].get_int(j) == table.get_int(i,j) +/// +/// The effect of calling most of the row accessor functions on a detached +/// accessor is unspecified and may lead to general corruption, and/or a +/// crash. The exceptions are is_attached(), detach(), get_table(), get_index(), +/// and the destructor. Note however, that get_index() will still return an +/// unspecified value for a deatched accessor. +/// +/// When a row accessor is evaluated in a boolean context, it evaluates to true +/// if, and only if it is attached. +/// +/// \tparam T A const or non-const table type (currently either `Table` or +/// `const Table`). +/// +/// \tparam R A specific row accessor class (BasicRow or BasicRowExpr) providing +/// members `T* impl_get_table() const`, `size_t impl_get_row_ndx() +/// const`, and `void impl_detach()`. Neither are allowed to throw. +/// +/// \sa Table +/// \sa BasicRow +template +class RowFuncs { +public: + typedef T table_type; + + typedef BasicTableRef ConstTableRef; + typedef BasicTableRef TableRef; // Same as ConstTableRef if `T` is 'const' + + typedef typename util::CopyConst::type L; + using ConstLinkViewRef = std::shared_ptr; + using LinkViewRef = std::shared_ptr; // Same as ConstLinkViewRef if `T` is 'const' + + int_fast64_t get_int(size_t col_ndx) const noexcept; + bool get_bool(size_t col_ndx) const noexcept; + float get_float(size_t col_ndx) const noexcept; + double get_double(size_t col_ndx) const noexcept; + StringData get_string(size_t col_ndx) const noexcept; + BinaryData get_binary(size_t col_ndx) const noexcept; + OldDateTime get_olddatetime(size_t col_ndx) const noexcept; + Timestamp get_timestamp(size_t col_ndx) const noexcept; + ConstTableRef get_subtable(size_t col_ndx) const; + TableRef get_subtable(size_t col_ndx); + size_t get_subtable_size(size_t col_ndx) const noexcept; + size_t get_link(size_t col_ndx) const noexcept; + bool is_null_link(size_t col_ndx) const noexcept; + bool is_null(size_t col_ndx) const noexcept; + ConstLinkViewRef get_linklist(size_t col_ndx) const; + LinkViewRef get_linklist(size_t col_ndx); + bool linklist_is_empty(size_t col_ndx) const noexcept; + size_t get_link_count(size_t col_ndx) const noexcept; + Mixed get_mixed(size_t col_ndx) const noexcept; + DataType get_mixed_type(size_t col_ndx) const noexcept; + template + U get(size_t col_ndx) const noexcept; + + void set_int(size_t col_ndx, int_fast64_t value); + void set_int_unique(size_t col_ndx, int_fast64_t value); + void add_int(size_t col_ndx, int_fast64_t value); + void set_bool(size_t col_ndx, bool value); + void set_float(size_t col_ndx, float value); + void set_double(size_t col_ndx, double value); + void set_string(size_t col_ndx, StringData value); + void set_string_unique(size_t col_ndx, StringData value); + void set_binary(size_t col_ndx, BinaryData value); + void set_olddatetime(size_t col_ndx, OldDateTime value); + void set_timestamp(size_t col_ndx, Timestamp value); + void set_subtable(size_t col_ndx, const Table* value); + void set_link(size_t col_ndx, size_t value); + void nullify_link(size_t col_ndx); + void set_mixed(size_t col_ndx, Mixed value); + void set_mixed_subtable(size_t col_ndx, const Table* value); + void set_null(size_t col_ndx); + void set_null_unique(size_t col_ndx); + + void insert_substring(size_t col_ndx, size_t pos, StringData); + void remove_substring(size_t col_ndx, size_t pos, size_t size); + + //@{ + /// Note that these operations will cause the row accessor to be detached. + void remove(); + void move_last_over(); + //@} + + size_t get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept; + size_t get_backlink(const Table& src_table, size_t src_col_ndx, size_t backlink_ndx) const noexcept; + + size_t get_column_count() const noexcept; + DataType get_column_type(size_t col_ndx) const noexcept; + StringData get_column_name(size_t col_ndx) const noexcept; + size_t get_column_index(StringData name) const noexcept; + + /// Returns true if, and only if this accessor is currently attached to a + /// row. + /// + /// A row accesor may get detached from the underlying row for various + /// reasons (see below). When it does, it no longer refers to anything, and + /// can no longer be used, except for calling is_attached(), detach(), + /// get_table(), get_index(), and the destructor. The consequences of + /// calling other methods on a detached row accessor are unspecified. There + /// are a few Realm functions (Table::find_pkey_int()) that return a + /// detached row accessor to indicate a 'null' result. In all other cases, + /// however, row accessors obtained by calling functions in the Realm API + /// are always in the 'attached' state immediately upon return from those + /// functions. + /// + /// A row accessor becomes detached if the underlying row is removed, if the + /// associated table accessor becomes detached, or if the detach() method is + /// called. A row accessor does not become detached for any other reason. + bool is_attached() const noexcept; + + /// Detach this accessor from the row it was attached to. This function has + /// no effect if the accessor was already detached (idempotency). + void detach() noexcept; + + /// The table containing the row to which this accessor is currently + /// bound. For a detached accessor, the returned value is null. + const table_type* get_table() const noexcept; + table_type* get_table() noexcept; + + /// The index of the row to which this accessor is currently bound. For a + /// detached accessor, the returned value is unspecified. + size_t get_index() const noexcept; + + explicit operator bool() const noexcept; + +private: + const T* table() const noexcept; + T* table() noexcept; + size_t row_ndx() const noexcept; +}; + + +/// This class is a special kind of row accessor. It differes from a real row +/// accessor (BasicRow) by having a trivial and fast copy constructor and +/// descructor. It is supposed to be used as the return type of functions such +/// as Table::operator[](), and then to be used as a basis for constructing a +/// real row accessor. Objects of this class are intended to only ever exist as +/// temporaries. +/// +/// In contrast to a real row accessor (`BasicRow`), objects of this class do +/// not keep the parent table "alive", nor are they maintained (adjusted) across +/// row insertions and row removals like real row accessors are. +/// +/// \sa BasicRow +template +class BasicRowExpr : public RowFuncs> { +public: + BasicRowExpr() noexcept; + + template + BasicRowExpr(const BasicRowExpr&) noexcept; + +private: + T* m_table; // nullptr if detached. + size_t m_row_ndx; // Undefined if detached. + + BasicRowExpr(T*, size_t init_row_ndx) noexcept; + + T* impl_get_table() const noexcept; + size_t impl_get_row_ndx() const noexcept; + void impl_detach() noexcept; + + // Make impl_get_table(), impl_get_row_ndx(), and impl_detach() accessible + // from RowFuncs. + friend class RowFuncs>; + + // Make m_table and m_col_ndx accessible from BasicRowExpr(const + // BasicRowExpr&) for any U. + template + friend class BasicRowExpr; + + // Make m_table and m_col_ndx accessible from + // BasicRow::BaicRow(BasicRowExpr) for any U. + template + friend class BasicRow; + + // Make BasicRowExpr(T*, size_t) accessible from Table. + friend class Table; +}; + +// fwd decl +class Group; + +class RowBase { +protected: + TableRef m_table; // nullptr if detached. + size_t m_row_ndx; // Undefined if detached. + + void attach(Table*, size_t row_ndx) noexcept; + void reattach(Table*, size_t row_ndx) noexcept; + void impl_detach() noexcept; + + RowBase() + { + } + + RowBase(const RowBase&) = delete; + using HandoverPatch = RowBaseHandoverPatch; + + RowBase(const RowBase& source, HandoverPatch& patch); + +public: + static void generate_patch(const RowBase& source, HandoverPatch& patch); + void apply_patch(HandoverPatch& patch, Group& group); + +private: + RowBase* m_prev = nullptr; // nullptr if first, undefined if detached. + RowBase* m_next = nullptr; // nullptr if last, undefined if detached. + + // Table needs to be able to modify m_table and m_row_ndx. + friend class Table; +}; + + +/// An accessor class for table rows (a.k.a. a "row accessor"). +/// +/// For as long as it remains attached, a row accessor will keep the parent +/// table accessor alive. In case the lifetime of the parent table is not +/// managed by reference counting (such as when the table is an automatic +/// variable on the stack), the destruction of the table will cause all +/// remaining row accessors to be detached. +/// +/// While attached, a row accessor is bound to a particular row of the parent +/// table. If that row is removed, the accesssor becomes detached. If rows are +/// inserted or removed before it (at lower row index), then the accessor is +/// automatically adjusted to account for the change in index of the row to +/// which the accessor is bound. In other words, a row accessor is bound to the +/// contents of a row, not to a row index. See also is_attached(). +/// +/// Row accessors are created and used as follows: +/// +/// Row row = table[7]; // 8th row of `table` +/// ConstRow crow = ctable[2]; // 3rd row of const `ctable` +/// Row first_row = table.front(); +/// Row last_row = table.back(); +/// +/// float v = row.get_float(1); // Get the float in the 2nd column +/// row.set_string(0, "foo"); // Update the string in the 1st column +/// +/// Table* t = row.get_table(); // The parent table +/// size_t i = row.get_index(); // The current row index +/// +/// \sa RowFuncs +template +class BasicRow : private RowBase, public RowFuncs> { +public: + BasicRow() noexcept; + + template + BasicRow(BasicRowExpr) noexcept; + + BasicRow(const BasicRow&) noexcept; + + template + BasicRow(const BasicRow&) noexcept; + + template + BasicRow& operator=(BasicRowExpr) noexcept; + + template + BasicRow& operator=(BasicRow) noexcept; + + BasicRow& operator=(const BasicRow&) noexcept; + + ~BasicRow() noexcept; + +private: + T* impl_get_table() const noexcept; + size_t impl_get_row_ndx() const noexcept; + + // Make impl_get_table(), impl_get_row_ndx(), and impl_detach() accessible + // from RowFuncs. + friend class RowFuncs>; + + // Make m_table and m_col_ndx accessible from BasicRow(const BasicRow&) + // for any U. + template + friend class BasicRow; + +public: + std::unique_ptr> clone_for_handover(std::unique_ptr& patch) const + { + patch.reset(new HandoverPatch); + std::unique_ptr> retval(new BasicRow(*this, *patch)); + return retval; + } + + static void generate_patch(const BasicRow& row, std::unique_ptr& patch) + { + patch.reset(new HandoverPatch); + RowBase::generate_patch(row, *patch); + } + + void apply_and_consume_patch(std::unique_ptr& patch, Group& group) + { + apply_patch(*patch, group); + patch.reset(); + } + + void apply_patch(HandoverPatch& patch, Group& group) + { + RowBase::apply_patch(patch, group); + } + +private: + BasicRow(const BasicRow& source, HandoverPatch& patch) + : RowBase(source, patch) + { + } + friend class SharedGroup; +}; + +typedef BasicRow Row; +typedef BasicRow ConstRow; + + +// Implementation + +template +inline int_fast64_t RowFuncs::get_int(size_t col_ndx) const noexcept +{ + return table()->get_int(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::get_bool(size_t col_ndx) const noexcept +{ + return table()->get_bool(col_ndx, row_ndx()); +} + +template +inline float RowFuncs::get_float(size_t col_ndx) const noexcept +{ + return table()->get_float(col_ndx, row_ndx()); +} + +template +inline double RowFuncs::get_double(size_t col_ndx) const noexcept +{ + return table()->get_double(col_ndx, row_ndx()); +} + +template +inline StringData RowFuncs::get_string(size_t col_ndx) const noexcept +{ + return table()->get_string(col_ndx, row_ndx()); +} + +template +inline BinaryData RowFuncs::get_binary(size_t col_ndx) const noexcept +{ + return table()->get_binary(col_ndx, row_ndx()); +} + +template +inline OldDateTime RowFuncs::get_olddatetime(size_t col_ndx) const noexcept +{ + return table()->get_olddatetime(col_ndx, row_ndx()); +} + +template +inline Timestamp RowFuncs::get_timestamp(size_t col_ndx) const noexcept +{ + return table()->get_timestamp(col_ndx, row_ndx()); +} + +template +inline typename RowFuncs::ConstTableRef RowFuncs::get_subtable(size_t col_ndx) const +{ + return table()->get_subtable(col_ndx, row_ndx()); // Throws +} + +template +inline typename RowFuncs::TableRef RowFuncs::get_subtable(size_t col_ndx) +{ + return table()->get_subtable(col_ndx, row_ndx()); // Throws +} + +template +inline size_t RowFuncs::get_subtable_size(size_t col_ndx) const noexcept +{ + return table()->get_subtable_size(col_ndx, row_ndx()); +} + +template +inline size_t RowFuncs::get_link(size_t col_ndx) const noexcept +{ + return table()->get_link(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::is_null_link(size_t col_ndx) const noexcept +{ + return table()->is_null_link(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::is_null(size_t col_ndx) const noexcept +{ + return table()->is_null(col_ndx, row_ndx()); +} + +template +inline typename RowFuncs::ConstLinkViewRef RowFuncs::get_linklist(size_t col_ndx) const +{ + return table()->get_linklist(col_ndx, row_ndx()); // Throws +} + +template +inline typename RowFuncs::LinkViewRef RowFuncs::get_linklist(size_t col_ndx) +{ + return table()->get_linklist(col_ndx, row_ndx()); // Throws +} + +template +inline bool RowFuncs::linklist_is_empty(size_t col_ndx) const noexcept +{ + return table()->linklist_is_empty(col_ndx, row_ndx()); +} + +template +inline size_t RowFuncs::get_link_count(size_t col_ndx) const noexcept +{ + return table()->get_link_count(col_ndx, row_ndx()); +} + +template +inline Mixed RowFuncs::get_mixed(size_t col_ndx) const noexcept +{ + return table()->get_mixed(col_ndx, row_ndx()); +} + +template +inline DataType RowFuncs::get_mixed_type(size_t col_ndx) const noexcept +{ + return table()->get_mixed_type(col_ndx, row_ndx()); +} + +template +template +inline U RowFuncs::get(size_t col_ndx) const noexcept +{ + return table()->template get(col_ndx, row_ndx()); +} + +template +inline void RowFuncs::set_int(size_t col_ndx, int_fast64_t value) +{ + table()->set_int(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_int_unique(size_t col_ndx, int_fast64_t value) +{ + table()->set_int_unique(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::add_int(size_t col_ndx, int_fast64_t value) +{ + table()->add_int(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_bool(size_t col_ndx, bool value) +{ + table()->set_bool(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_float(size_t col_ndx, float value) +{ + table()->set_float(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_double(size_t col_ndx, double value) +{ + table()->set_double(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_string(size_t col_ndx, StringData value) +{ + table()->set_string(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_string_unique(size_t col_ndx, StringData value) +{ + table()->set_string_unique(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_binary(size_t col_ndx, BinaryData value) +{ + table()->set_binary(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_olddatetime(size_t col_ndx, OldDateTime value) +{ + table()->set_olddatetime(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_timestamp(size_t col_ndx, Timestamp value) +{ + table()->set_timestamp(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_subtable(size_t col_ndx, const Table* value) +{ + table()->set_subtable(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_link(size_t col_ndx, size_t value) +{ + table()->set_link(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::nullify_link(size_t col_ndx) +{ + table()->nullify_link(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::set_mixed(size_t col_ndx, Mixed value) +{ + table()->set_mixed(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_mixed_subtable(size_t col_ndx, const Table* value) +{ + table()->set_mixed_subtable(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_null(size_t col_ndx) +{ + table()->set_null(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::set_null_unique(size_t col_ndx) +{ + table()->set_null_unique(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::insert_substring(size_t col_ndx, size_t pos, StringData value) +{ + table()->insert_substring(col_ndx, row_ndx(), pos, value); // Throws +} + +template +inline void RowFuncs::remove_substring(size_t col_ndx, size_t pos, size_t size) +{ + table()->remove_substring(col_ndx, row_ndx(), pos, size); // Throws +} + +template +inline void RowFuncs::remove() +{ + table()->remove(row_ndx()); // Throws +} + +template +inline void RowFuncs::move_last_over() +{ + table()->move_last_over(row_ndx()); // Throws +} + +template +inline size_t RowFuncs::get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept +{ + return table()->get_backlink_count(row_ndx(), src_table, src_col_ndx); +} + +template +inline size_t RowFuncs::get_backlink(const Table& src_table, size_t src_col_ndx, size_t backlink_ndx) const + noexcept +{ + return table()->get_backlink(row_ndx(), src_table, src_col_ndx, backlink_ndx); +} + +template +inline size_t RowFuncs::get_column_count() const noexcept +{ + return table()->get_column_count(); +} + +template +inline DataType RowFuncs::get_column_type(size_t col_ndx) const noexcept +{ + return table()->get_column_type(col_ndx); +} + +template +inline StringData RowFuncs::get_column_name(size_t col_ndx) const noexcept +{ + return table()->get_column_name(col_ndx); +} + +template +inline size_t RowFuncs::get_column_index(StringData name) const noexcept +{ + return table()->get_column_index(name); +} + +template +inline bool RowFuncs::is_attached() const noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline void RowFuncs::detach() noexcept +{ + static_cast(this)->impl_detach(); +} + +template +inline const T* RowFuncs::get_table() const noexcept +{ + return table(); +} + +template +inline T* RowFuncs::get_table() noexcept +{ + return table(); +} + +template +inline size_t RowFuncs::get_index() const noexcept +{ + return row_ndx(); +} + +template +inline RowFuncs::operator bool() const noexcept +{ + return is_attached(); +} + +template +inline const T* RowFuncs::table() const noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline T* RowFuncs::table() noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline size_t RowFuncs::row_ndx() const noexcept +{ + return static_cast(this)->impl_get_row_ndx(); +} + + +template +inline BasicRowExpr::BasicRowExpr() noexcept + : m_table(0) + , m_row_ndx(0) +{ +} + +template +template +inline BasicRowExpr::BasicRowExpr(const BasicRowExpr& expr) noexcept + : m_table(expr.m_table) + , m_row_ndx(expr.m_row_ndx) +{ +} + +template +inline BasicRowExpr::BasicRowExpr(T* init_table, size_t init_row_ndx) noexcept + : m_table(init_table) + , m_row_ndx(init_row_ndx) +{ +} + +template +inline T* BasicRowExpr::impl_get_table() const noexcept +{ + return m_table; +} + +template +inline size_t BasicRowExpr::impl_get_row_ndx() const noexcept +{ + return m_row_ndx; +} + +template +inline void BasicRowExpr::impl_detach() noexcept +{ + m_table = nullptr; +} + + +template +inline BasicRow::BasicRow() noexcept +{ +} + +template +inline BasicRow::BasicRow(const BasicRow& row) noexcept + : RowBase() +{ + attach(const_cast(row.m_table.get()), row.m_row_ndx); +} + +template +template +inline BasicRow::BasicRow(BasicRowExpr expr) noexcept +{ + T* expr_table = expr.m_table; // Check that pointer types are compatible + attach(const_cast(expr_table), expr.m_row_ndx); +} + +template +template +inline BasicRow::BasicRow(const BasicRow& row) noexcept +{ + T* row_table = row.m_table.get(); // Check that pointer types are compatible + attach(const_cast(row_table), row.m_row_ndx); +} + +template +template +inline BasicRow& BasicRow::operator=(BasicRowExpr expr) noexcept +{ + T* expr_table = expr.m_table; // Check that pointer types are compatible + reattach(const_cast(expr_table), expr.m_row_ndx); + return *this; +} + +template +template +inline BasicRow& BasicRow::operator=(BasicRow row) noexcept +{ + T* row_table = row.m_table.get(); // Check that pointer types are compatible + reattach(const_cast(row_table), row.m_row_ndx); + return *this; +} + +template +inline BasicRow& BasicRow::operator=(const BasicRow& row) noexcept +{ + reattach(const_cast(row.m_table.get()), row.m_row_ndx); + return *this; +} + +template +inline BasicRow::~BasicRow() noexcept +{ + RowBase::impl_detach(); +} + +template +inline T* BasicRow::impl_get_table() const noexcept +{ + return m_table.get(); +} + +template +inline size_t BasicRow::impl_get_row_ndx() const noexcept +{ + return m_row_ndx; +} + +} // namespace realm + +#endif // REALM_ROW_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/spec.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/spec.hpp new file mode 100644 index 0000000..78448c9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/spec.hpp @@ -0,0 +1,476 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_SPEC_HPP +#define REALM_SPEC_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { + +class Table; +class SubspecRef; +class ConstSubspecRef; + +class Spec { +public: + Spec(SubspecRef) noexcept; + ~Spec() noexcept; + + Allocator& get_alloc() const noexcept; + + bool has_strong_link_columns() noexcept; + + void insert_column(size_t column_ndx, ColumnType type, StringData name, ColumnAttr attr = col_attr_None); + void rename_column(size_t column_ndx, StringData new_name); + void move_column(size_t from, size_t to); + + /// Erase the column at the specified index, and move columns at + /// succeeding indexes to the next lower index. + /// + /// This function is guaranteed to *never* throw if the spec is + /// used in a non-transactional context, or if the spec has + /// already been successfully modified within the current write + /// transaction. + void erase_column(size_t column_ndx); + + //@{ + // If a new Spec is constructed from the returned subspec + // reference, it is the responsibility of the application that the + // parent Spec object (this) is kept alive for at least as long as + // the new Spec object. + SubspecRef get_subtable_spec(size_t column_ndx) noexcept; + ConstSubspecRef get_subtable_spec(size_t column_ndx) const noexcept; + //@} + + // Column info + size_t get_column_count() const noexcept; + size_t get_public_column_count() const noexcept; + DataType get_public_column_type(size_t column_ndx) const noexcept; + ColumnType get_column_type(size_t column_ndx) const noexcept; + StringData get_column_name(size_t column_ndx) const noexcept; + + /// Returns size_t(-1) if the specified column is not found. + size_t get_column_index(StringData name) const noexcept; + + // Column Attributes + ColumnAttr get_column_attr(size_t column_ndx) const noexcept; + + size_t get_subspec_ndx(size_t column_ndx) const noexcept; + ref_type get_subspec_ref(size_t subspec_ndx) const noexcept; + SubspecRef get_subspec_by_ndx(size_t subspec_ndx) noexcept; + ConstSubspecRef get_subspec_by_ndx(size_t subspec_ndx) const noexcept; + + // Auto Enumerated string columns + void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref, ArrayParent*& keys_parent, size_t& keys_ndx); + size_t get_enumkeys_ndx(size_t column_ndx) const noexcept; + ref_type get_enumkeys_ref(size_t column_ndx, ArrayParent** keys_parent = nullptr, + size_t* keys_ndx = nullptr) noexcept; + + // Links + size_t get_opposite_link_table_ndx(size_t column_ndx) const noexcept; + void set_opposite_link_table_ndx(size_t column_ndx, size_t table_ndx); + bool has_backlinks() const noexcept; + void set_backlink_origin_column(size_t backlink_col_ndx, size_t origin_col_ndx); + size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept; + size_t find_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx) const noexcept; + + /// Get position in `Table::m_columns` of the specified column. It may be + /// different from the specified logical column index due to the presence of + /// search indexes, since their top refs are stored in Table::m_columns as + /// well. + size_t get_column_ndx_in_parent(size_t column_ndx) const; + + //@{ + /// Compare two table specs for equality. + bool operator==(const Spec&) const noexcept; + bool operator!=(const Spec&) const noexcept; + //@} + + void destroy() noexcept; + + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t) noexcept; + +#ifdef REALM_DEBUG + void verify() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + // Underlying array structure. + // + // `m_subspecs` contains one entry for each subtable column, one entry for + // each link or link list columns, two entries for each backlink column, and + // zero entries for all other column types. For subtable columns the entry + // is a ref pointing to the subtable spec, for link and link list columns it + // is the group-level table index of the target table, and for backlink + // columns the first entry is the group-level table index of the origin + // table, and the second entry is the index of the origin column in the + // origin table. + Array m_top; + ArrayInteger m_types; // 1st slot in m_top + ArrayString m_names; // 2nd slot in m_top + ArrayInteger m_attr; // 3rd slot in m_top + Array m_subspecs; // 4th slot in m_top (optional) + Array m_enumkeys; // 5th slot in m_top (optional) + bool m_has_strong_link_columns; + + Spec(Allocator&) noexcept; // Unattached + + void init(ref_type) noexcept; + void init(MemRef) noexcept; + void init(SubspecRef) noexcept; + void update_has_strong_link_columns() noexcept; + + // Similar in function to Array::init_from_parent(). + void init_from_parent() noexcept; + + ref_type get_ref() const noexcept; + + /// Called in the context of Group::commit() to ensure that + /// attached table accessors stay valid across a commit. Please + /// note that this works only for non-transactional commits. Table + /// accessors obtained during a transaction are always detached + /// when the transaction ends. + void update_from_parent(size_t old_baseline) noexcept; + + void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept; + + void set_column_type(size_t column_ndx, ColumnType type); + void set_column_attr(size_t column_ndx, ColumnAttr attr); + + /// Construct an empty spec and return just the reference to the + /// underlying memory. + static MemRef create_empty_spec(Allocator&); + + struct ColumnInfo { + size_t m_column_ref_ndx = 0; ///< Index within Table::m_columns + bool m_has_search_index = false; + }; + + ColumnInfo get_column_info(size_t column_ndx) const noexcept; + + size_t get_subspec_ndx_after(size_t column_ndx, size_t skip_column_ndx) const noexcept; + size_t get_subspec_entries_for_col_type(ColumnType type) const noexcept; + bool has_subspec() const noexcept; + + // Returns false if the spec has no columns, otherwise it returns + // true and sets `type` to the type of the first column. + static bool get_first_column_type_from_ref(ref_type, Allocator&, ColumnType& type) noexcept; + + friend class Replication; + friend class Group; + friend class Table; +}; + + +class SubspecRef { +public: + struct const_cast_tag { + }; + SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept; + ~SubspecRef() noexcept + { + } + Allocator& get_alloc() const noexcept + { + return m_parent->get_alloc(); + } + +private: + Array* const m_parent; + size_t const m_ndx_in_parent; + + SubspecRef(Array* parent, size_t ndx_in_parent) noexcept; + + friend class Spec; + friend class ConstSubspecRef; +}; + +class ConstSubspecRef { +public: + ConstSubspecRef(SubspecRef r) noexcept; + ~ConstSubspecRef() noexcept + { + } + Allocator& get_alloc() const noexcept + { + return m_parent->get_alloc(); + } + +private: + const Array* const m_parent; + size_t const m_ndx_in_parent; + + ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept; + + friend class Spec; + friend class SubspecRef; +}; + + +// Implementation: + +inline Allocator& Spec::get_alloc() const noexcept +{ + return m_top.get_alloc(); +} + +inline bool Spec::has_strong_link_columns() noexcept +{ + return m_has_strong_link_columns; +} + +inline ref_type Spec::get_subspec_ref(size_t subspec_ndx) const noexcept +{ + REALM_ASSERT(subspec_ndx < m_subspecs.size()); + + // Note that this addresses subspecs directly, indexing + // by number of sub-table columns + return m_subspecs.get_as_ref(subspec_ndx); +} + +inline Spec::Spec(SubspecRef r) noexcept + : m_top(r.m_parent->get_alloc()) + , m_types(r.m_parent->get_alloc()) + , m_names(r.m_parent->get_alloc()) + , m_attr(r.m_parent->get_alloc()) + , m_subspecs(r.m_parent->get_alloc()) + , m_enumkeys(r.m_parent->get_alloc()) +{ + init(r); +} + +// Uninitialized Spec (call init() to init) +inline Spec::Spec(Allocator& alloc) noexcept + : m_top(alloc) + , m_types(alloc) + , m_names(alloc) + , m_attr(alloc) + , m_subspecs(alloc) + , m_enumkeys(alloc) +{ +} + +inline SubspecRef Spec::get_subtable_spec(size_t column_ndx) noexcept +{ + REALM_ASSERT(column_ndx < get_column_count()); + REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); + size_t subspec_ndx = get_subspec_ndx(column_ndx); + return SubspecRef(&m_subspecs, subspec_ndx); +} + +inline ConstSubspecRef Spec::get_subtable_spec(size_t column_ndx) const noexcept +{ + REALM_ASSERT(column_ndx < get_column_count()); + REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); + size_t subspec_ndx = get_subspec_ndx(column_ndx); + return ConstSubspecRef(&m_subspecs, subspec_ndx); +} + +inline SubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) noexcept +{ + return SubspecRef(&m_subspecs, subspec_ndx); +} + +inline ConstSubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept +{ + return const_cast(this)->get_subspec_by_ndx(subspec_ndx); +} + +inline void Spec::init(ref_type ref) noexcept +{ + MemRef mem(ref, get_alloc()); + init(mem); +} + +inline void Spec::init(SubspecRef r) noexcept +{ + m_top.set_parent(r.m_parent, r.m_ndx_in_parent); + ref_type ref = r.m_parent->get_as_ref(r.m_ndx_in_parent); + init(ref); +} + +inline void Spec::init_from_parent() noexcept +{ + ref_type ref = m_top.get_ref_from_parent(); + init(ref); +} + +inline void Spec::destroy() noexcept +{ + m_top.destroy_deep(); +} + +inline size_t Spec::get_ndx_in_parent() const noexcept +{ + return m_top.get_ndx_in_parent(); +} + +inline void Spec::set_ndx_in_parent(size_t ndx) noexcept +{ + m_top.set_ndx_in_parent(ndx); +} + +inline ref_type Spec::get_ref() const noexcept +{ + return m_top.get_ref(); +} + +inline void Spec::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_top.set_parent(parent, ndx_in_parent); +} + +inline void Spec::rename_column(size_t column_ndx, StringData new_name) +{ + REALM_ASSERT(column_ndx < m_types.size()); + m_names.set(column_ndx, new_name); +} + +inline size_t Spec::get_column_count() const noexcept +{ + // This is the total count of columns, including backlinks (not public) + return m_types.size(); +} + +inline size_t Spec::get_public_column_count() const noexcept +{ + // Backlinks are the last columns, and do not have names, so getting + // the number of names gives us the count of user facing columns + return m_names.size(); +} + +inline ColumnType Spec::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return ColumnType(m_types.get(ndx)); +} + +inline void Spec::set_column_type(size_t column_ndx, ColumnType type) +{ + REALM_ASSERT(column_ndx < get_column_count()); + + // At this point we only support upgrading to string enum + REALM_ASSERT(ColumnType(m_types.get(column_ndx)) == col_type_String); + REALM_ASSERT(type == col_type_StringEnum); + + m_types.set(column_ndx, type); // Throws + + update_has_strong_link_columns(); +} + +inline ColumnAttr Spec::get_column_attr(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return ColumnAttr(m_attr.get(ndx)); +} + +inline void Spec::set_column_attr(size_t column_ndx, ColumnAttr attr) +{ + REALM_ASSERT(column_ndx < get_column_count()); + + // At this point we only allow one attr at a time + // so setting it will overwrite existing. In the future + // we will allow combinations. + m_attr.set(column_ndx, attr); + + update_has_strong_link_columns(); +} + +inline StringData Spec::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return m_names.get(ndx); +} + +inline size_t Spec::get_column_index(StringData name) const noexcept +{ + return m_names.find_first(name); +} + +inline bool Spec::get_first_column_type_from_ref(ref_type top_ref, Allocator& alloc, ColumnType& type) noexcept +{ + const char* top_header = alloc.translate(top_ref); + ref_type types_ref = to_ref(Array::get(top_header, 0)); + const char* types_header = alloc.translate(types_ref); + if (Array::get_size_from_header(types_header) == 0) + return false; + type = ColumnType(Array::get(types_header, 0)); + return true; +} + +inline bool Spec::has_backlinks() const noexcept +{ + // backlinks are always last and do not have names. + return m_names.size() < m_types.size(); + + // Fixme: It's bad design that backlinks are stored and recognized like this. Backlink columns + // should be a column type like any other, and we should find another way to hide them away from + // the user. +} + +// Spec will have a subspec when it contains a column which is one of: +// link, linklist, backlink, or subtable. It is possible for m_top.size() +// to contain an entry for m_subspecs (at index 3) but this reference +// may be empty if the spec contains enumkeys (at index 4) but no subspec types. +inline bool Spec::has_subspec() const noexcept +{ + return (m_top.size() >= 4) && (m_top.get_as_ref(3) != 0); +} + +inline bool Spec::operator!=(const Spec& s) const noexcept +{ + return !(*this == s); +} + + +inline SubspecRef::SubspecRef(Array* parent, size_t ndx_in_parent) noexcept + : m_parent(parent) + , m_ndx_in_parent(ndx_in_parent) +{ +} + +inline SubspecRef::SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept + : m_parent(const_cast(r.m_parent)) + , m_ndx_in_parent(r.m_ndx_in_parent) +{ +} + +inline ConstSubspecRef::ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept + : m_parent(parent) + , m_ndx_in_parent(ndx_in_parent) +{ +} + +inline ConstSubspecRef::ConstSubspecRef(SubspecRef r) noexcept + : m_parent(r.m_parent) + , m_ndx_in_parent(r.m_ndx_in_parent) +{ +} + + +} // namespace realm + +#endif // REALM_SPEC_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/string_data.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/string_data.hpp new file mode 100644 index 0000000..a617ebe --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/string_data.hpp @@ -0,0 +1,379 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_STRING_HPP +#define REALM_STRING_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +/// A reference to a chunk of character data. +/// +/// An instance of this class can be thought of as a type tag on a region of +/// memory. It does not own the referenced memory, nor does it in any other way +/// attempt to manage the lifetime of it. +/// +/// A null character inside the referenced region is considered a part of the +/// string by Realm. +/// +/// For compatibility with C-style strings, when a string is stored in a Realm +/// database, it is always followed by a terminating null character, regardless +/// of whether the string itself has internal null characters. This means that +/// when a StringData object is extracted from Realm, the referenced region is +/// guaranteed to be followed immediately by an extra null character, but that +/// null character is not inside the referenced region. Therefore, all of the +/// following forms are guaranteed to return a pointer to a null-terminated +/// string: +/// +/// \code{.cpp} +/// +/// group.get_table_name(...).data() +/// table.get_column_name().data() +/// table.get_string(...).data() +/// table.get_mixed(...).get_string().data() +/// +/// \endcode +/// +/// Note that in general, no assumptions can be made about what follows a string +/// that is referenced by a StringData object, or whether anything follows it at +/// all. In particular, the receiver of a StringData object cannot assume that +/// the referenced string is followed by a null character unless there is an +/// externally provided guarantee. +/// +/// This class makes it possible to distinguish between a 'null' reference and a +/// reference to the empty string (see is_null()). +/// +/// \sa BinaryData +/// \sa Mixed +class StringData { +public: + /// Construct a null reference. + StringData() noexcept; + + /// If \a external_data is 'null', \a data_size must be zero. + StringData(const char* external_data, size_t data_size) noexcept; + + template + StringData(const std::basic_string&); + + template + operator std::basic_string() const; + + // StringData does not store data, callers must manage their own strings. + template + StringData(const std::basic_string&&) = delete; + + template + StringData(const util::Optional>&); + + StringData(const null&) noexcept; + + /// Initialize from a zero terminated C style string. Pass null to construct + /// a null reference. + StringData(const char* c_str) noexcept; + + char operator[](size_t i) const noexcept; + + const char* data() const noexcept; + size_t size() const noexcept; + + /// Is this a null reference? + /// + /// An instance of StringData is a null reference when, and only when the + /// stored size is zero (size()) and the stored pointer is the null pointer + /// (data()). + /// + /// In the case of the empty string, the stored size is still zero, but the + /// stored pointer is **not** the null pointer. It could for example point + /// to the empty string literal. Note that the actual value of the pointer + /// is immaterial in this case (as long as it is not zero), because when the + /// size is zero, it is an error to dereference the pointer. + /// + /// Conversion of a StringData object to `bool` yields the logical negation + /// of the result of calling this function. In other words, a StringData + /// object is converted to true if it is not the null reference, otherwise + /// it is converted to false. + bool is_null() const noexcept; + + friend bool operator==(const StringData&, const StringData&) noexcept; + friend bool operator!=(const StringData&, const StringData&) noexcept; + + //@{ + /// Trivial bytewise lexicographical comparison. + friend bool operator<(const StringData&, const StringData&) noexcept; + friend bool operator>(const StringData&, const StringData&) noexcept; + friend bool operator<=(const StringData&, const StringData&) noexcept; + friend bool operator>=(const StringData&, const StringData&) noexcept; + //@} + + bool begins_with(StringData) const noexcept; + bool ends_with(StringData) const noexcept; + bool contains(StringData) const noexcept; + bool contains(StringData d, const std::array &charmap) const noexcept; + + // Wildcard matching ('?' for single char, '*' for zero or more chars) + // case insensitive version in unicode.hpp + bool like(StringData) const noexcept; + + //@{ + /// Undefined behavior if \a n, \a i, or i+n is greater than + /// size(). + StringData prefix(size_t n) const noexcept; + StringData suffix(size_t n) const noexcept; + StringData substr(size_t i, size_t n) const noexcept; + StringData substr(size_t i) const noexcept; + //@} + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const StringData&); + + explicit operator bool() const noexcept; + +private: + const char* m_data; + size_t m_size; + + static bool matchlike(const StringData& text, const StringData& pattern) noexcept; + static bool matchlike_ins(const StringData& text, const StringData& pattern_upper, + const StringData& pattern_lower) noexcept; + + friend bool string_like_ins(StringData, StringData) noexcept; + friend bool string_like_ins(StringData, StringData, StringData) noexcept; +}; + + +// Implementation: + +inline StringData::StringData() noexcept + : m_data(nullptr) + , m_size(0) +{ +} + +inline StringData::StringData(const char* external_data, size_t data_size) noexcept + : m_data(external_data) + , m_size(data_size) +{ + REALM_ASSERT_DEBUG(external_data || data_size == 0); +} + +template +inline StringData::StringData(const std::basic_string& s) + : m_data(s.data()) + , m_size(s.size()) +{ +} + +template +inline StringData::operator std::basic_string() const +{ + return std::basic_string(m_data, m_size); +} + +template +inline StringData::StringData(const util::Optional>& s) + : m_data(s ? s->data() : nullptr) + , m_size(s ? s->size() : 0) +{ +} + +inline StringData::StringData(const null&) noexcept + : m_data(nullptr) + , m_size(0) +{ +} + +inline StringData::StringData(const char* c_str) noexcept + : m_data(c_str) + , m_size(0) +{ + if (c_str) + m_size = std::char_traits::length(c_str); +} + +inline char StringData::operator[](size_t i) const noexcept +{ + return m_data[i]; +} + +inline const char* StringData::data() const noexcept +{ + return m_data; +} + +inline size_t StringData::size() const noexcept +{ + return m_size; +} + +inline bool StringData::is_null() const noexcept +{ + return !m_data; +} + +inline bool operator==(const StringData& a, const StringData& b) noexcept +{ + return a.m_size == b.m_size && a.is_null() == b.is_null() && safe_equal(a.m_data, a.m_data + a.m_size, b.m_data); +} + +inline bool operator!=(const StringData& a, const StringData& b) noexcept +{ + return !(a == b); +} + +inline bool operator<(const StringData& a, const StringData& b) noexcept +{ + if (a.is_null() && !b.is_null()) { + // Null strings are smaller than all other strings, and not + // equal to empty strings. + return true; + } + return std::lexicographical_compare(a.m_data, a.m_data + a.m_size, b.m_data, b.m_data + b.m_size); +} + +inline bool operator>(const StringData& a, const StringData& b) noexcept +{ + return b < a; +} + +inline bool operator<=(const StringData& a, const StringData& b) noexcept +{ + return !(b < a); +} + +inline bool operator>=(const StringData& a, const StringData& b) noexcept +{ + return !(a < b); +} + +inline bool StringData::begins_with(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + return d.m_size <= m_size && safe_equal(m_data, m_data + d.m_size, d.m_data); +} + +inline bool StringData::ends_with(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + return d.m_size <= m_size && safe_equal(m_data + m_size - d.m_size, m_data + m_size, d.m_data); +} + +inline bool StringData::contains(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size == 0 || std::search(m_data, m_data + m_size, d.m_data, d.m_data + d.m_size) != m_data + m_size; +} + +/// This method takes an array that maps chars to distance that can be moved (and zero for chars not in needle), +/// allowing the method to apply Boyer-Moore for quick substring search +/// The map is calculated in the StringNode class (so it can be reused across searches) +inline bool StringData::contains(StringData d, const std::array &charmap) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + size_t needle_size = d.size(); + if (needle_size == 0) + return true; + + // Prepare vars to avoid lookups in loop + size_t last_char_pos = d.size()-1; + unsigned char lastChar = d[last_char_pos]; + + // Do Boyer-Moore search + size_t p = last_char_pos; + while (p < m_size) { + unsigned char c = m_data[p]; // Get candidate for last char + + if (c == lastChar) { + StringData candidate = substr(p-needle_size+1, needle_size); + if (candidate == d) + return true; // text found! + } + + // If we don't have a match, see how far we can move char_pos + if (charmap[c] == 0) + p += needle_size; // char was not present in search string + else + p += charmap[c]; + } + + return false; +} + +inline bool StringData::like(StringData d) const noexcept +{ + if (is_null() || d.is_null()) { + return (is_null() && d.is_null()); + } + + return matchlike(*this, d); +} + +inline StringData StringData::prefix(size_t n) const noexcept +{ + return substr(0, n); +} + +inline StringData StringData::suffix(size_t n) const noexcept +{ + return substr(m_size - n); +} + +inline StringData StringData::substr(size_t i, size_t n) const noexcept +{ + return StringData(m_data + i, n); +} + +inline StringData StringData::substr(size_t i) const noexcept +{ + return substr(i, m_size - i); +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const StringData& d) +{ + for (const char* i = d.m_data; i != d.m_data + d.m_size; ++i) + out << *i; + return out; +} + +inline StringData::operator bool() const noexcept +{ + return !is_null(); +} + +} // namespace realm + +#endif // REALM_STRING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp new file mode 100644 index 0000000..3d003be --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp @@ -0,0 +1,40 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#include + +#ifndef REALM_SYNC_CHANGESET_COOKER_HPP +#define REALM_SYNC_CHANGESET_COOKER_HPP + +namespace realm { +namespace sync { + +/// Copy raw changesets unmodified. +class TrivialChangesetCooker: public ClientHistory::ChangesetCooker { +public: + bool cook_changeset(const Group&, const char* changeset, + std::size_t changeset_size, + util::AppendBuffer&) override; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CHANGESET_COOKER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/client.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/client.hpp new file mode 100644 index 0000000..778e8f7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/client.hpp @@ -0,0 +1,726 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_CLIENT_HPP +#define REALM_SYNC_CLIENT_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { +namespace sync { + +class Client { +public: + enum class Error; + + enum class ReconnectMode { + /// This is the mode that should always be used in production. In this + /// mode the client uses a scheme for determining a reconnect delay that + /// prevents it from creating too many connection requests in a short + /// amount of time (i.e., a server hammering protection mechanism). + normal, + + /// Delay reconnect attempts indefinitely. For testing purposes only. + /// + /// A reconnect attempt can be manually scheduled by calling + /// cancel_reconnect_delay(). In particular, when a connection breaks, + /// or when an attempt at establishing the connection fails, the error + /// handler is called. If one calls cancel_reconnect_delay() from that + /// invocation of the error handler, the effect is to allow another + /// reconnect attempt to occur. + never, + + /// Never delay reconnect attempts. Perform them immediately. For + /// testing purposes only. + immediate + }; + + struct Config { + Config() {} + + /// The maximum number of Realm files that will be kept open + /// concurrently by this client. The client keeps a cache of open Realm + /// files for efficiency reasons. + long max_open_files = 256; + + /// An optional logger to be used by the client. If no logger is + /// specified, the client will use an instance of util::StderrLogger + /// with the log level threshold set to util::Logger::Level::info. The + /// client does not require a thread-safe logger, and it guarantees that + /// all logging happens on behalf of the thread that executes run(). + util::Logger* logger = nullptr; + + /// verify_servers_ssl_certificate controls whether the server certificate + /// is verified for SSL connections. It should always be true in production. + /// + /// A server certificate is verified by first checking that the + /// certificate has a valid signature chain back to a trust/anchor certificate, + /// and secondly checking that the host name of the Realm URL matches + /// a host name contained in the certificate. + /// The host name of the certificate is stored in either Common Name or + /// the Alternative Subject Name (DNS section). + /// + /// From the point of view of OpenSSL, setting verify_servers_ssl_certificate + /// to false means calling `SSL_set_verify()` with `SSL_VERIFY_NONE`. + /// Setting verify_servers_ssl_certificate to true means calling `SSL_set_verify()` + /// with `SSL_VERIFY_PEER`, and setting the host name using the function + /// X509_VERIFY_PARAM_set1_host() (OpenSSL version 1.0.2 or newer). + /// For other platforms, an equivalent procedure is followed. + bool verify_servers_ssl_certificate = true; + + /// ssl_trust_certificate_path is the path of a trust/anchor certificate + /// used by the client to verify the server certificate. + /// If ssl_trust_certificate_path is None (default), the default device + /// trust/anchor store is used. + util::Optional ssl_trust_certificate_path; // default None + + /// Use ports 80 and 443 by default instead of 7800 and 7801 + /// respectively. Ideally, these default ports should have been made + /// available via a different URI scheme instead (http/https or ws/wss). + bool enable_default_port_hack = true; + + /// For testing purposes only. + ReconnectMode reconnect_mode = ReconnectMode::normal; + + /// Create a separate connection for each session. For testing purposes + /// only. + /// + // FIXME: This setting is ignored for now, due to limitations in the + // load balancer. + bool one_connection_per_session = false; + + /// Do not access the local file system. Sessions will act as if + /// initiated on behalf of an empty (or nonexisting) local Realm + /// file. Received DOWNLOAD messages will be accepted, but otherwise + /// ignored. No UPLOAD messages will be generated. For testing purposes + /// only. + bool dry_run = false; + + /// The default changeset cooker to be used by new sessions. Can be + /// overridden by Session::Config::changeset_cooker. + /// + /// \sa make_client_history(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The number of ms between periodic keep-alive pings. + uint_fast64_t ping_keepalive_period_ms = 600000; + + /// The number of ms to wait for keep-alive pongs. + uint_fast64_t pong_keepalive_timeout_ms = 300000; + + /// The number of ms to wait for urgent pongs. + uint_fast64_t pong_urgent_timeout_ms = 5000; + }; + + /// \throw util::EventLoop::Implementation::NotAvailable if no event loop + /// implementation was specified, and + /// util::EventLoop::Implementation::get_default() throws it. + Client(Config = {}); + Client(Client&&) noexcept; + ~Client() noexcept; + + /// Run the internal event-loop of the client. At most one thread may + /// execute run() at any given time. The call will not return until somebody + /// calls stop(). + void run(); + + /// See run(). + /// + /// Thread-safe. + void stop() noexcept; + + /// \brief Cancel current or next reconnect delay for all servers. + /// + /// This corresponds to calling Session::cancel_reconnect_delay() on all + /// bound sessions, but will also cancel reconnect delays applying to + // servers for which there are currently no bound sessions. + void cancel_reconnect_delay(); + +private: + class Impl; + std::unique_ptr m_impl; + friend class Session; +}; + + +/// Supported protocols: +/// +/// Protocol URL scheme Default port +/// ----------------------------------------------------------------------------------- +/// realm "realm:" 7800 (80 if Client::Config::enable_default_port_hack) +/// realm_ssl "realms:" 7801 (443 if Client::Config::enable_default_port_hack) +/// +enum class Protocol { + realm, + realm_ssl +}; + + +class BadServerUrl; // Exception + + +/// \brief Client-side representation of a Realm file synchronization session. +/// +/// A synchronization session deals with precisely one local Realm file. To +/// synchronize multiple local Realm files, you need multiple sessions. +/// +/// A session object is always associated with a particular client object (\ref +/// Client). The application must ensure that the destruction of the associated +/// client object never happens before the destruction of the session +/// object. The consequences of a violation are unspecified. +/// +/// A session object is always associated with a particular local Realm file, +/// however, a session object does not represent a session until it is bound to +/// a server side Realm, i.e., until bind() is called. From the point of view of +/// the thread that calls bind(), the session starts precisely when the +/// execution of bind() starts, i.e., before bind() returns. +/// +/// At most one session is allowed to exist for a particular local Realm file +/// (file system inode) at any point in time. Multiple objects may coexists, as +/// long as bind() has been called on at most one of them. Additionally, two +/// sessions are allowed to exist at different times, and with no overlap in +/// time, as long as they are associated with the same client object, or with +/// two different client objects that do not overlap in time. This means, in +/// particular, that it is an error to create two nonoverlapping sessions for +/// the same local Realm file, it they are associated with two different client +/// objects that overlap in time. It is the responsibility of the application to +/// ensure that these rules are adhered to. The consequences of a violation are +/// unspecified. +/// +/// Thread-safety: It is safe for multiple threads to construct, use (with some +/// exceptions), and destroy session objects concurrently, regardless of whether +/// those session objects are associated with the same, or with different Client +/// objects. Please note that some of the public member functions are fully +/// thread-safe, while others are not. +class Session { +public: + using port_type = util::network::Endpoint::port_type; + using version_type = _impl::History::version_type; + using SyncTransactCallback = void(VersionID old_version, VersionID new_version); + using ProgressHandler = void(std::uint_fast64_t downloaded_bytes, + std::uint_fast64_t downloadable_bytes, + std::uint_fast64_t uploaded_bytes, + std::uint_fast64_t uploadable_bytes, + std::uint_fast64_t progress_version); + using WaitOperCompletionHandler = std::function; + + class Config { + public: + Config() {} + + /// If not null, overrides whatever is specified by + /// Client::Config::changeset_cooker. + /// + /// The shared ownership over the cooker will be relinquished shortly + /// after the destruction of the session object as long as the event + /// loop of the client is being executed (Client::run()). + /// + /// CAUTION: ChangesetCooker::cook_changeset() of the specified cooker + /// may get called before the call to bind() returns, and it may get + /// called (or continue to execute) after the session object is + /// destroyed. The application must specify an object for which that + /// function can safely be called, and continue to execute from the + /// point in time where bind() starts executing, and up until the point + /// in time where the last invocation of `client.run()` returns. Here, + /// `client` refers to the associated Client object. + /// + /// \sa make_client_history(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The encryption key the SharedGroup will be opened with. + Optional> encryption_key; + }; + + /// \brief Start a new session for the specified client-side Realm. + /// + /// Note that the session is not fully activated until you call bind(). Also + /// note that if you call set_sync_transact_callback(), it must be done + /// before calling bind(). + /// + /// \param realm_path The file-system path of a local client-side Realm + /// file. + Session(Client&, std::string realm_path, Config = {}); + + Session(Session&&) noexcept; + + ~Session() noexcept; + + /// \brief Set a function to be called when the local Realm has changed due + /// to integration of a downloaded changeset. + /// + /// Specify the callback function that will be called when one or more + /// transactions are performed to integrate downloaded changesets into the + /// client-side Realm, that is associated with this session. + /// + /// The callback function will always be called by the thread that executes + /// the event loop (Client::run()), but not until bind() is called. If the + /// callback function throws an exception, that exception will "travel" out + /// through Client::run(). + /// + /// Note: Any call to this function must have returned before bind() is + /// called. If this function is called multiple times, each call overrides + /// the previous setting. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// CAUTION: The specified callback function may be called before the call + /// to bind() returns, and it may be called (or continue to execute) after + /// the session object is destroyed. The application must pass a handler + /// that can be safely called, and can safely continue to execute from the + /// point in time where bind() starts executing, and up until the point in + /// time where the last invocation of `client.run()` returns. Here, `client` + /// refers to the associated Client object. + void set_sync_transact_callback(std::function); + + /// \brief Set a handler to monitor the state of download and upload + /// progress. + /// + /// The handler must have signature + /// + /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes, + /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes, + /// uint_fast64_t progress_version); + /// + /// downloaded_bytes is the size in bytes of all downloaded changesets. + /// downloadable_bytes is the size in bytes of the part of the server + /// history that do not originate from this client. + /// + /// uploaded_bytes is the size in bytes of all locally produced changesets + /// that have been received and acknowledged by the server. + /// uploadable_bytes is the size in bytes of all locally produced changesets. + /// + /// Due to the nature of the merge rules, it is possible that the size of an + /// uploaded changeset uploaded from one client is not equal to the size of + /// the changesets that other clients will download. + /// + /// Typical uses of this function: + /// + /// Upload completion can be checked by + /// + /// bool upload_complete = (uploaded_bytes == uploadable_bytes); + /// + /// Download completion could be checked by + /// + /// bool download_complete = (downloaded_bytes == downloadable_bytes); + /// + /// However, download completion might never be reached because the server + /// can receive new changesets from other clients. + /// An alternative strategy is to cache downloadable_bytes from the callback, + /// and use the cached value as the threshold. + /// + /// bool download_complete = (downloaded_bytes == cached_downloadable_bytes); + /// + /// Upload progress can be calculated by caching an initial value of + /// uploaded_bytes from the last, or next, callback. Then + /// + /// double upload_progress = + /// (uploaded_bytes - initial_uploaded_bytes) + /// ------------------------------------------- + /// (uploadable_bytes - initial_uploaded_bytes) + /// + /// Download progress can be calculates similarly: + /// + /// double download_progress = + /// (downloaded_bytes - initial_downloaded_bytes) + /// ----------------------------------------------- + /// (downloadable_bytes - initial_downloaded_bytes) + /// + /// progress_version is 0 at the start of a session. When at least one + /// DOWNLOAD message has been received from the server, progress_version is + /// positive. progress_version can be used to ensure that the reported + /// progress contains information obtained from the server in the current + /// session. The server will send a message as soon as possible, and the + /// progress handler will eventually be called with a positive progress_version + /// unless the session is interrupted before a message from the server has + /// been received. + /// + /// The handler is called on the event loop thread. + /// The handler is called after or during set_progress_handler(), + /// after bind(), after each DOWNLOAD message, and after each local + /// transaction (nonsync_transact_notify). + /// + /// set_progress_handler() is not thread safe and it must be called before + /// bind() is called. Subsequent calls to set_progress_handler() overwrite + /// the previous calls. Typically, this function is called once per session. + /// + /// The progress handler is also posted to the event loop during the + /// execution of set_progress_handler(). + /// + /// CAUTION: The specified callback function may be called before the call + /// to set_progress_handler() returns, and it may be called + /// (or continue to execute) after the session object is destroyed. + /// The application must pass a handler that can be safely called, and can + /// execute from the point in time where set_progress_handler() is called, + /// and up until the point in time where the last invocation of + /// `client.run()` returns. Here, `client` refers to the associated + /// Client object. + void set_progress_handler(std::function); + + /// \brief Signature of an error handler. + /// + /// \param ec The error code. For the list of errors reported by the server, + /// see \ref ProtocolError (or `protocol.md`). For the list of errors + /// corresponding with protocol violation that are detected by the client, + /// see Client::Error. + /// + /// \param is_fatal The error is of a kind that is likely to persist, and + /// cause all future reconnect attempts to fail. The client may choose to + /// try to reconnect again later, but if so, the waiting period will be + /// substantial. + /// + /// \param detailed_message The message associated with the error. It is + /// usually equal to `ec.message()`, but may also be a more specific message + /// (one that provides extra context). The purpose of this message is mostly + /// to aid debugging. For non-debugging purposes, `ec.message()` should + /// generally be considered sufficient. + /// + /// \sa set_error_handler(). + using ErrorHandler = void(std::error_code ec, bool is_fatal, + const std::string& detailed_message); + + /// \brief Set the error handler for this session. + /// + /// Sets a function to be called when an error causes a connection + /// initiation attempt to fail, or an established connection to be broken. + /// + /// When a connection is established on behalf of multiple sessions, a + /// connection-level error will be reported to all those sessions. A + /// session-level error, on the other hand, will only be reported to the + /// affected session. + /// + /// The callback function will always be called by the thread that executes + /// the event loop (Client::run()), but not until bind() is called. If the + /// callback function throws an exception, that exception will "travel" out + /// through Client::run(). + /// + /// Note: Any call to this function must have returned before bind() is + /// called. If this function is called multiple times, each call overrides + /// the previous setting. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// CAUTION: The specified callback function may be called before the call + /// to bind() returns, and it may be called (or continue to execute) after + /// the session object is destroyed. The application must pass a handler + /// that can be safely called, and can safely continue to execute from the + /// point in time where bind() starts executing, and up until the point in + /// time where the last invocation of `client.run()` returns. Here, `client` + /// refers to the associated Client object. + void set_error_handler(std::function); + + /// @{ \brief Bind this session to the specified server side Realm. + /// + /// No communication takes place on behalf of this session before the + /// session is bound, but as soon as the session becomes bound, the server + /// will start to push changes to the client, and vice versa. + /// + /// If a callback function was set using set_sync_transact_callback(), then + /// that callback function will start to be called as changesets are + /// downloaded and integrated locally. It is important to understand that + /// callback functions are executed by the event loop thread (Client::run()) + /// and the callback function may therefore be called before bind() returns. + /// + /// Note: It is an error if this function is called more than once per + /// Session object. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// \param server_url For example "realm://sync.realm.io/test". See \a + /// server_address, \a server_path, and \a server_port for information about + /// the individual components of the URL. See \ref Protocol for the list of + /// available URL schemes and the associated default ports. The 2-argument + /// version has exactly the same affect as the 5-argument version. + /// + /// \param server_address The fully qualified host name, or IP address of + /// the server. + /// + /// \param server_path The virtual path by which the server identifies the + /// Realm. This path must always be an absolute path, and must therefore + /// always contain a leading slash (`/`). Further more, each segment of the + /// virtual path must consist of one or more characters that are either + /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to + /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or + /// `.realm.management`. These rules are necessary because the server + /// currently reserves the right to use the specified path as part of the + /// file system path of a Realm file. It is expected that these rules will + /// be significantly relaxed in the future by completely decoupling the + /// virtual paths from actual file system paths. + /// + /// \param signed_user_token A cryptographically signed token describing the + /// identity and access rights of the current user. See \ref Protocol. + /// + /// \param server_port If zero, use the default port for the specified + /// protocol. See \ref Protocol for information on default ports. + /// + /// \param protocol See \ref Protocol. + /// + /// \throw BadServerUrl if the specified server URL is malformed. + void bind(std::string server_url, std::string signed_user_token); + void bind(std::string server_address, std::string server_path, + std::string signed_user_token, port_type server_port = 0, + Protocol protocol = Protocol::realm); + /// @} + + /// \brief Refresh the user token associated with this session. + /// + /// This causes the REFRESH protocol message to be sent to the server. See + /// \ref Protocol. + /// + /// In an on-going session a client may expect its access token to expire at + /// a certain time and schedule acquisition of a fresh access token (using a + /// refresh token or by other means) in due time to provide a better user + /// experience. Without refreshing the token, the client will be notified + /// that the session is terminated due to insufficient privileges and must + /// reacquire a fresh token, which is a potentially disruptive process. + /// + /// It is an error to call this function before calling `Client::bind()`. + /// + /// Note: This function is thread-safe. + /// + /// \param signed_user_token A cryptographically signed token describing the + /// identity and access rights of the current user. See \ref Protocol. + void refresh(std::string signed_user_token); + + /// \brief Inform the synchronization agent about changes of local origin. + /// + /// This function must be called by the application after a transaction + /// performed on its behlaf, that is, after a transaction that is not + /// performed to integrate a changeset that was downloaded from the server. + /// + /// It is an error to call this function before bind() has been called, and + /// has returned. + /// + /// Note: This function is fully thread-safe. That is, it may be called by + /// any thread, and by multiple threads concurrently. + void nonsync_transact_notify(version_type new_version); + + /// @{ \brief Wait for upload, download, or upload+download completion. + /// + /// async_wait_for_upload_completion() initiates an asynchronous wait for + /// upload to complete, async_wait_for_download_completion() initiates an + /// asynchronous wait for download to complete, and + /// async_wait_for_sync_completion() initiates an asynchronous wait for + /// upload and download to complete. + /// + /// Upload is considered complete when all non-empty changesets of local + /// origin have been uploaded to the server, and the server has acknowledged + /// reception of them. Changesets of local origin introduced after the + /// initiation of the session (after bind() is called) will generally not be + /// considered for upload unless they are announced to this client through + /// nonsync_transact_notify() prior to the initiation of the wait operation, + /// i.e., prior to the invocation of async_wait_for_upload_completion() or + /// async_wait_for_sync_completion(). Unannounced changesets may get picked + /// up, but there is no guarantee that they will be, however, if a certain + /// changeset is announced, then all previous changesets are implicitely + /// announced. Also all preexisting changesets are implicitely announced + /// when the session is initiated. + /// + /// Download is considered complete when all non-empty changesets of remote + /// origin have been downloaded from the server, and integrated into the + /// local Realm state. To know what is currently outstanding on the server, + /// the client always sends a special "marker" message to the server, and + /// waits until it has downloaded all outstanding changesets that were + /// present on the server at the time when the server received that marker + /// message. Each call to async_wait_for_download_completion() and + /// async_wait_for_sync_completion() therefore requires a full client <-> + /// server round-trip. + /// + /// If a new wait operation is initiated while other wait operations are in + /// progress, the waiting period of operations in progress may, or may not + /// get extended. The client must not assume either. The client may assume, + /// however, that async_wait_for_upload_completion() will not affect the + /// waiting period of async_wait_for_download_completion(), and vice versa. + /// + /// It is an error to call these functions before bind() has been called, + /// and has returned. + /// + /// The specified completion handlers will always be executed by the thread + /// that executes the event loop (the thread that calls Client::run()). If + /// the handler throws an exception, that exception will "travel" out + /// through Client::run(). + /// + /// If incomplete wait operations exist when the session is terminated, + /// those wait operations will be canceled. Session termination is an event + /// that happens in the context of the client's event loop thread shortly + /// after the destruction of the session object. The std::error_code + /// argument passed to the completion handler of a canceled wait operation + /// will be `util::error::operation_aborted`. For uncanceled wait operations + /// it will be `std::error_code{}`. Note that as long as the client's event + /// loop thread is running, all completion handlers will be called + /// regardless of whether the operations get canceled or not. + /// + /// CAUTION: The specified completion handlers may be called before the + /// initiation function returns (e.g. before + /// async_wait_for_upload_completion() returns), and it may be called (or + /// continue to execute) after the session object is destroyed. The + /// application must pass a handler that can be safely called, and can + /// safely continue to execute from the point in time where the initiating + /// function starts executing, and up until the point in time where the last + /// invocation of `clint.run()` returns. Here, `client` refers to the + /// associated Client object. + /// + /// Note: These functions are fully thread-safe. That is, they may be called + /// by any thread, and by multiple threads concurrently. + void async_wait_for_sync_completion(WaitOperCompletionHandler); + void async_wait_for_upload_completion(WaitOperCompletionHandler); + void async_wait_for_download_completion(WaitOperCompletionHandler); + /// @} + + /// @{ \brief Synchronous wait for upload or download completion. + /// + /// These functions are synchronous equivalents to + /// async_wait_for_upload_completion() and + /// async_wait_for_download_completion() respectively. This means that they + /// block the caller until the completion condition is satisfied, or the + /// client event loop is stopped (Client::stop()). + /// + /// It is an error to call these functions before bind() has been called, + /// and has returned. + /// + /// Note: These functions are fully thread-safe. That is, they may be called + /// by any thread, and by multiple threads concurrently. + void wait_for_upload_complete_or_client_stopped(); + void wait_for_download_complete_or_client_stopped(); + /// @} + + /// \brief Cancel the current or next reconnect delay for the server + /// associated with this session. + /// + /// When the network connection is severed, or an attempt to establish + /// connection fails, a certain delay will take effect before the client + /// will attempt to reestablish the connection. This delay will generally + /// grow with the number of unsuccessful reconnect attempts, and can grow to + /// over a minute. In some cases however, the application will know when it + /// is a good time to stop waiting and retry immediately. One example is + /// when a device has been offline for a while, and the operating system + /// then tells the application that network connectivity has been restored. + /// + /// Clearly, this function should not be called too often and over extended + /// periods of time, as that would effectively disable the built-in "server + /// hammering" protection. + /// + /// It is an error to call this function before bind() has been called, and + /// has returned. + /// + /// This function is fully thread-safe. That is, it may be called by any + /// thread, and by multiple threads concurrently. + void cancel_reconnect_delay(); + +private: + class Impl; + Impl* m_impl; + + void async_wait_for(bool upload_completion, bool download_completion, + WaitOperCompletionHandler); +}; + + +/// \brief Protocol errors discovered by the client. +/// +/// These errors will terminate the network connection (disconnect all sessions +/// associated with the affected connection), and the error will be reported to +/// the application via the error handlers of the affected sessions. +enum class Client::Error { + connection_closed = 100, ///< Connection closed (no error) + unknown_message = 101, ///< Unknown type of input message + bad_syntax = 102, ///< Bad syntax in input message head + limits_exceeded = 103, ///< Limits exceeded in input message + bad_session_ident = 104, ///< Bad session identifier in input message + bad_message_order = 105, ///< Bad input message order + bad_file_ident_pair = 106, ///< Bad file identifier pair (ALLOC) + bad_progress = 107, ///< Bad progress information (DOWNLOAD) + bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD) + bad_changeset_size = 109, ///< Bad changeset size in changeset header (DOWNLOAD) + bad_origin_file_ident = 110, ///< Bad origin file identifier in changeset header (DOWNLOAD) + bad_server_version = 111, ///< Bad server version in changeset header (DOWNLOAD) + bad_changeset = 112, ///< Bad changeset (DOWNLOAD) + bad_request_ident = 113, ///< Bad request identifier (MARK) + bad_error_code = 114, ///< Bad error code (ERROR), + bad_compression = 115, ///< Bad compression (DOWNLOAD) + bad_client_version = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD) +}; + +const std::error_category& client_error_category() noexcept; + +std::error_code make_error_code(Client::Error) noexcept; + +} // namespace sync +} // namespace realm + +namespace std { + +template<> struct is_error_code_enum { + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace sync { + + + +// Implementation + +class BadServerUrl: public std::exception { +public: + const char* what() const noexcept override + { + return "Bad server URL"; + } +}; + +inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler) +{ + bool upload_completion = true, download_completion = true; + async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws +} + +inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler) +{ + bool upload_completion = true, download_completion = false; + async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws +} + +inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler) +{ + bool upload_completion = false, download_completion = true; + async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CLIENT_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto.hpp new file mode 100644 index 0000000..016543e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto.hpp @@ -0,0 +1,46 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CRYPTO_HPP +#define REALM_SYNC_CRYPTO_HPP + +#include +#include + +#include +#include + +namespace realm { +namespace sync { +namespace crypto { + +/// sha1() calculates the sha1 hash value of \param in_buffer of size \param +/// in_buffer_size. The value is placed in \param out_buffer. The value has +/// size 20, and the caller must ensure that \param out_buffer has size at +/// least 20. +/// sha1() throws an exception if the underlying openssl implementation +/// fails, which should just happen in case of memory allocation failure. +void sha1(const char* in_buffer, size_t in_buffer_size, char* out_buffer); + +} // namespace crypto +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CRYPTO_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp new file mode 100644 index 0000000..f2ac3f0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp @@ -0,0 +1,86 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CRYPTO_SERVER_HPP +#define REALM_SYNC_CRYPTO_SERVER_HPP + +#include +#include + +#include +#include + +namespace realm { +namespace sync { + +struct CryptoError: std::runtime_error { + CryptoError(std::string message) : std::runtime_error(std::move(message)) {} +}; + +/// This class represents a public/private keypair, or more commonly a single public +/// key used for verifying signatures. +/// +/// Only RSA keys are supported for now. +/// +/// Its methods correspond roughly to the EVP_PKEY_* set of functionality found in +/// the OpenSSL library. +class PKey { +public: + PKey(PKey&&); + PKey& operator=(PKey&&); + ~PKey(); + + /// Load RSA public key from \a pemfile. + static PKey load_public(const std::string& pemfile); + /// Load RSA public/private keypair from \a pemfile. + static PKey load_private(const std::string& pemfile); + + /// Whether or not the key can be used for signing. + /// + /// True if the private part is loaded. + bool can_sign() const noexcept; + + /// Whether or not the key can be used for verifying. + /// + /// Always true for RSA keys. + bool can_verify() const noexcept; + + /// Sign \a message with the loaded key, if the private part is + /// loaded. Store the signed message as binary data in \a signature. + /// + /// If a private key is not loaded, throws an exception of type CryptoError. + void sign(BinaryData message, util::Buffer& signature) const; + + /// Verify that \a signature is a valid digest of \a message. + /// + /// Returns true if the signature is valid, otherwise false. If an error occurs while + /// attempting verification, an exception of type CryptoError is thrown. + bool verify(BinaryData message, BinaryData signature) const; + +private: + PKey(); + struct Impl; + std::unique_ptr m_impl; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CRYPTO_SERVER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/history.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/history.hpp new file mode 100644 index 0000000..24af37b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/history.hpp @@ -0,0 +1,341 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#include +#include + +#include +#include + +#ifndef REALM_SYNC_HISTORY_HPP +#define REALM_SYNC_HISTORY_HPP + +namespace realm { +namespace sync { + +/// SyncProgress is the progress sent by the server in the download message. The +/// server scans through its history in connection with every download message. +/// scan_server_version is the server_version of the changeset at which the +/// server ended the scan. scan_client_version is the client_version for this +/// client that was last integrated before scan_server_version. +/// latest_server_version is the end of the server history, and +/// latest_server_session_ident is the server_session_ident corresponding to +/// latest_sever_version. latest_client_version is the corresponding +/// client_version. In other words, latest_client_version is the very latest +/// version of a changeset originating from this client. +/// +/// The client persists the entire progress. It is not very important to persist +/// latest_server_version, but for consistency the entire progress is persisted. +struct SyncProgress { + using version_type = HistoryEntry::version_type; + + version_type scan_server_version = 0; + version_type scan_client_version = 0; + version_type latest_server_version = 0; + std::int_fast64_t latest_server_session_ident = 0; + version_type latest_client_version = 0; + int_fast64_t downloadable_bytes = 0; +}; + + +class ClientHistory: + public TrivialReplication { +public: + using version_type = TrivialReplication::version_type; + using file_ident_type = HistoryEntry::file_ident_type; + using SyncTransactCallback = void(VersionID old_version, VersionID new_version); + + class ChangesetCooker; + class Config; + + /// Get the version of the latest snapshot of the associated Realm, as well + /// as the file identifier pair and the synchronization progress pair as + /// they are stored in that snapshot. + /// + /// The returned current client version is the version of the latest + /// snapshot of the associated SharedGroup object, and is guaranteed to be + /// zero for the initial empty Realm state. + /// + /// The returned file identifier pair (server, client) is the one that was + /// last stored by set_file_ident_pair(). If no identifier pair has been + /// stored yet, \a client_file_ident is set to zero. + /// + /// The returned SyncProgress is the one that was last stored by + /// set_sync_progress(), or {} if set_sync_progress() has never been called + /// for the associated Realm file. + virtual void get_status(version_type& current_client_version, + file_ident_type& server_file_ident, + file_ident_type& client_file_ident, + std::int_fast64_t& client_file_ident_secret, + SyncProgress& progress) = 0; + + /// Stores the server assigned file identifier pair (server, client) in the + /// associated Realm file, such that it is available via get_status() during + /// future synchronization sessions. It is an error to set this identifier + /// pair more than once per Realm file. + /// + /// \param server_file_ident The server assigned server-side file + /// identifier. This can be any non-zero integer strictly less than 2**64. + /// The server is supposed to choose a cryptic value that cannot easily be + /// guessed by clients (intentionally or not), and its only purpose is to + /// provide a higher level of fidelity during subsequent identification of + /// the server Realm. The server does not have to guarantee that this + /// identifier is unique, but in almost all cases it will be. Since the + /// client will also always specify the path name when referring to a server + /// file, the lack of a uniqueness guarantee is effectively not a problem. + /// + /// \param client_file_ident The server assigned client-side file + /// identifier. A client-side file identifier is a non-zero positive integer + /// strictly less than 2**64. The server guarantees that all client-side + /// file identifiers generated on behalf of a particular server Realm are + /// unique with respect to each other. The server is free to generate + /// identical identifiers for two client files if they are associated with + /// different server Realms. + /// + /// The client is required to obtain the file identifiers before engaging in + /// synchronization proper, and it must store the identifiers and use them + /// to reestablish the connection between the client file and the server + /// file when engaging in future synchronization sessions. + virtual void set_file_ident_pair(file_ident_type server_file_ident, + file_ident_type client_file_ident, + std::int_fast64_t client_file_ident_secret) = 0; + + /// Stores the SyncProgress progress in the associated Realm file in a way + /// that makes it available via get_status() during future synchronization + /// sessions. Progress is reported by the server in the DOWNLOAD message. + /// + /// See struct SyncProgress for a description of \param progress. + /// + /// `progress.scan_client_version` has an effect on the process by which old + /// history entries are discarded. + /// + /// `progress.scan_client_version` The version produced on this client by + /// the last changeset, that was sent to, and integrated by the server at + /// the time `progress.scan_server_version was produced, or zero if + /// `progress.scan_server_version` is zero. + /// + /// Since all changesets produced after `progress.scan_client_version` are + /// potentially needed during operational transformation of the next + /// changeset received from the server, the implementation of this class + /// must promise to retain all history entries produced after + /// `progress.scan_client_version`. That is, a history entry with a + /// changeset, that produces version V, is guaranteed to be retained as long + /// as V is strictly greater than `progress.scan_client_version`. + /// + /// It is an error to specify a client version that is less than the + /// currently stored version, since there is no way to get discarded history + /// back. + virtual void set_sync_progress(SyncProgress progress) = 0; + + /// Get the first history entry whose changeset produced a version that + /// succeeds `begin_version` and, and does not succeed `end_version`, whose + /// changeset was not produced by integration of a changeset received from + /// the server, and whose changeset was not empty. + /// + /// \param begin_version, end_version The range of versions to consider. If + /// `begin_version` is equal to `end_version`, this is the empty range. If + /// `begin_version` is zero, it means that everything preceding + /// `end_version` is to be considered, which is again the empty range if + /// `end_version` is also zero. Zero is a special value in that no changeset + /// produces that version. It is an error if `end_version` precedes + /// `begin_version`, or if `end_version` is zero and `begin_version` is not. + /// + /// \param buffer Owner of memory referenced by entry.changeset upon return. + /// + /// \return The version produced by the changeset of the located history + /// entry, or zero if no history entry exists matching the specified + /// criteria. + virtual version_type find_history_entry_for_upload(version_type begin_version, + version_type end_version, + HistoryEntry& entry, + std::unique_ptr& buffer) const = 0; + + using RemoteChangeset = Transformer::RemoteChangeset; + + /// \brief Integrate a sequence of remote changesets using a single Realm + /// transaction. + /// + /// Each changeset will be transformed as if by a call to + /// Transformer::transform_remote_changeset(), and then applied to the + /// associated Realm. + /// + /// As a final step, each changeset will be added to the local history (list + /// of applied changesets). + /// + /// \param progress is the SyncProgress received in the download message. + /// Progress will be persisted along with the changesets. + /// + /// \param num_changesets The number of passed changesets. Must be non-zero. + /// + /// \param callback An optional callback which will be called with the + /// version immediately processing the sync transaction and that of the sync + /// transaction. + /// + /// \return The new local version produced by the application of the + /// transformed changeset. + virtual version_type integrate_remote_changesets(SyncProgress progress, + const RemoteChangeset* changesets, + std::size_t num_changesets, + util::Logger* replay_logger, + std::function& callback) = 0; + + /// Get the persisted upload/download progress in bytes. + virtual void get_upload_download_bytes(uint_fast64_t& downloaded_bytes, + uint_fast64_t& downloadable_bytes, + uint_fast64_t& uploaded_bytes, + uint_fast64_t& uploadable_bytes) = 0; + + /// See set_cooked_progress(). + struct CookedProgress { + std::int_fast64_t changeset_index = 0; + std::int_fast64_t intrachangeset_progress = 0; + }; + + /// Returns the persisted progress that was last stored by + /// set_cooked_progress(). + /// + /// Initially, until explicitly modified, both + /// `CookedProgress::changeset_index` and + /// `CookedProgress::intrachangeset_progress` are zero. + virtual CookedProgress get_cooked_progress() const = 0; + + /// Persistently stores the point of progress of the consumer of cooked + /// changesets. + /// + /// As well as allowing for later retrieval, the specification of the point + /// of progress of the consumer of cooked changesets also has the effect of + /// trimming obsolete cooked changesets from the Realm file. Indeed, if this + /// function is never called, but cooked changesets are continually being + /// produced, then the Realm file will grow without bounds. + /// + /// Behavior is undefined if the specified index + /// (CookedProgress::changeset_index) is lower than the index returned by + /// get_cooked_progress(). + /// + /// The intrachangeset progress field + /// (CookedProgress::intrachangeset_progress) will be faithfully persisted, + /// but will otherwise be treated as an opaque object by the history + /// internals. + virtual void set_cooked_progress(CookedProgress) = 0; + + /// Get the number of cooked changesets so far produced for this Realm. This + /// is the number of cooked changesets that are currently in the Realm file + /// plus the number of cooked changesets that have been trimmed off so far. + virtual std::int_fast64_t get_num_cooked_changesets() const = 0; + + /// Fetch the cooked changeset at the specified index. + /// + /// Cooked changesets are made available in the order they are produced by + /// the changeset cooker (ChangesetCooker). + /// + /// Behaviour is undefined if the specified index is less than the index + /// (CookedProgress::changeset_index) returned by get_cooked_progress(), or + /// if it is greater than, or equal to the toal number of cooked changesets + /// (as returned by get_num_cooked_changesets()). + /// + /// The callee must append the bytes of the located cooked changeset to the + /// specified buffer, which does not have to be empty initially. + virtual void get_cooked_changeset(std::int_fast64_t index, + util::AppendBuffer&) const = 0; + +protected: + ClientHistory(const std::string& realm_path); +}; + + +/// \brief Abstract interface for changeset cookers. +/// +/// Note, it is completely up to the application to decide what a cooked +/// changeset is. History objects (instances of ClientHistory) are required to +/// treat cooked changesets as opaque entities. For an example of a concrete +/// changeset cooker, see TrivialChangesetCooker which defines the cooked +/// changesets to be identical copies of the raw changesets. +class ClientHistory::ChangesetCooker { +public: + /// \brief An opportunity to produce a cooked changeset. + /// + /// When the implementation chooses to produce a cooked changeset, it must + /// write the cooked changeset to the specified buffer, and return + /// true. When the implementation chooses not to produce a cooked changeset, + /// it must return false. The implementation is allowed to write to the + /// buffer, and return false, and in that case, the written data will be + /// ignored. + /// + /// \param prior_state The state of the local Realm on which the specified + /// raw changeset is based. + /// + /// \param changeset, changeset_size The raw changeset. + /// + /// \param buffer The buffer to which the cooked changeset must be written. + /// + /// \return True if a cooked changeset was produced. Otherwise false. + virtual bool cook_changeset(const Group& prior_state, + const char* changeset, std::size_t changeset_size, + util::AppendBuffer& buffer) = 0; +}; + + +class ClientHistory::Config { +public: + Config() {} + + /// Must be set to true if, and only if the created history object + /// represents (is owned by) the sync agent of the specified Realm file. At + /// most one such instance is allowed to participate in a Realm file access + /// session at any point in time. Ordinarily the sync agent is encapsulated + /// by the sync::Client class, and the history instance representing the + /// agent is created transparently by sync::Client (one history instance per + /// sync::Session object). + bool owner_is_sync_agent = false; + + /// If a changeset cooker is specified, then the created history object will + /// allow for a cooked changeset to be produced for each changeset of remote + /// origin; that is, for each changeset that is integrated during the + /// execution of ClientHistory::integrate_remote_changesets(). If no + /// changeset cooker is specified, then no cooked changesets will be + /// produced on behalf of the created history object. + /// + /// ClientHistory::integrate_remote_changesets() will pass each incoming + /// changeset to the cooker after operational transformation; that is, when + /// the chageset is ready to be applied to the local Realm state. + std::shared_ptr changeset_cooker; +}; + +/// \brief Create a "sync history" implementation of the realm::Replication +/// interface. +/// +/// The intended role for such an object is as a plugin for new +/// realm::SharedGroup objects. +std::unique_ptr make_client_history(const std::string& realm_path, + ClientHistory::Config = {}); + + + +// Implementation + +inline ClientHistory::ClientHistory(const std::string& realm_path): + TrivialReplication(realm_path) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_HISTORY_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/metrics.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/metrics.hpp new file mode 100644 index 0000000..1d24273 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/metrics.hpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_METRICS_HPP +#define REALM_SYNC_METRICS_HPP + +#if REALM_HAVE_DOGLESS +# include +#endif + +namespace realm { +namespace sync { + +// FIXME: Consider adding support for specification of sample rate. The Dogless +// API already supports this. +class Metrics { +public: + /// Increment the counter identified by the specified key. + virtual void increment(const char* key, int value = 1) = 0; + + /// Set value of the guage identified by the specified key. + virtual void gauge(const char* key, double value) = 0; + + /// Add the specified value to the guage identified by the specified + /// key. The value is allowed to be negative. + virtual void gauge_relative(const char* key, double value) = 0; + + /// Allow the dogless library to send each metric to multiple endpoints, as + /// required + virtual void add_endpoint(const std::string& endpoint) = 0; + + virtual ~Metrics() {} +}; + +#if REALM_HAVE_DOGLESS + +class DoglessMetrics: public sync::Metrics { +public: + DoglessMetrics(): + m_dogless(dogless::hostname_prefix("realm")) // Throws + { + m_dogless.loop_interval(1); + } + + void increment(const char* key, int value = 1) override + { + const char* metric = key; + m_dogless.increment(metric, value); // Throws + } + + void gauge(const char* key, double value) override + { + const char* metric = key; + m_dogless.gauge(metric, value); // Throws + } + + void gauge_relative(const char* key, double value) override + { + const char* metric = key; + double amount = value; + m_dogless.gauge_relative(metric, amount); // Throws + } + + void add_endpoint(const std::string& endpoint) override + { + m_dogless.add_endpoint(endpoint); + } + +private: + dogless::BufferedStatsd m_dogless; +}; + +#endif + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_METRICS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/protocol.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/protocol.hpp new file mode 100644 index 0000000..5d70298 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/protocol.hpp @@ -0,0 +1,783 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_PROTOCOL_HPP +#define REALM_SYNC_PROTOCOL_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include // Get rid of this? + + +// NOTE: The protocol specification is in `/doc/protocol.md` + + +namespace realm { +namespace sync { + +// Protocol versions: +// +// 1 Initial version. +// +// 2 Introduces the UNBOUND message (sent from server to client in +// response to a BIND message). +// +// 3 Introduces the ERROR message (sent from server to client before the +// server closes a connection). Introduces MARK message from client to +// server, and MARK response message from server to client as a way for the +// client to wait for download to complete. +// +// 4 User token and signature are now passed as a single string (see +// /doc/protocol.md for details). Also, `application_ident` parameter +// removed from IDENT message. +// +// 5 IDENT message renamed to CLIENT, and ALLOC message (client->server) +// renamed to IDENT. Also, parameter added to CLIENT +// message. Also, the protocol has been changed to make the clients +// acquisition of a server allocated file identifier pair be part of a +// session from the servers point of view. File identifier and version +// parameters moved from the BIND message to a new IDENT message sent by +// client when it has obtained the file identifier pair. Both the new IDENT +// message and the ALLOC message sent by the server are now properly +// associated with a session. +// +// 6 Server session IDs have been added to the IDENT, DOWNLOAD, and PROGRESS +// messages, and the "Divergent history" error code was added as an +// indication that a server version / session ID pair does not match the +// server's history. +// +// 7 FIXME: Who introduced version 7? Please describe what changed. +// +// 8 Error code (`bad_authentication`) moved from 200-range to 300-range +// because it is now session specific. Other error codes were renumbered. +// +// 9 New format of the DOWNLOAD message to support progress reporting on the +// client +// +// 10 Error codes reordered (now categorized as either connection or session +// level errors). +// +// 11 Bugfixes in Link List and ChangeLinkTargets merge rules, that +// make previous versions incompatible. +// +// 12 FIXME What was 12? +// +// 13 Bugfixes in Link List and ChangeLinkTargets merge rules, that +// make previous versions incompatible. +// +// 14 Further bugfixes related to primary keys and link lists. Add support for +// LinkListSwap. +// +// 15 Deleting an object with a primary key deletes all objects on other +// with the same primary key. +// +// 16 Downloadable bytes added to DOWNLOAD message. It is used for download progress +// by the client +// +// 17 Added PING and PONG messages. It is used for rtt monitoring and dead +// connection detection by both the client and the server. + +constexpr int get_current_protocol_version() noexcept +{ + return 17; +} + +/// \brief Protocol errors discovered by the server, and reported to the client +/// by way of ERROR messages. +/// +/// These errors will be reported to the client-side application via the error +/// handlers of the affected sessions. +/// +/// ATTENTION: Please remember to update is_session_level_error() when +/// adding/removing error codes. +enum class ProtocolError { + // Connection level and protocol errors + connection_closed = 100, // Connection closed (no error) + other_error = 101, // Other connection level error + unknown_message = 102, // Unknown type of input message + bad_syntax = 103, // Bad syntax in input message head + limits_exceeded = 104, // Limits exceeded in input message + wrong_protocol_version = 105, // Wrong protocol version (CLIENT) + bad_session_ident = 106, // Bad session identifier in input message + reuse_of_session_ident = 107, // Overlapping reuse of session identifier (BIND) + bound_in_other_session = 108, // Client file bound in other session (IDENT) + bad_message_order = 109, // Bad input message order + pong_timeout = 110, // Pong timeout + + // Session level errors + session_closed = 200, // Session closed (no error) + other_session_error = 201, // Other session level error + token_expired = 202, // Access token expired + bad_authentication = 203, // Bad user authentication (BIND, REFRESH) + illegal_realm_path = 204, // Illegal Realm path (BIND) + no_such_realm = 205, // No such Realm (BIND) + permission_denied = 206, // Permission denied (BIND, REFRESH) + bad_server_file_ident = 207, // Bad server file identifier (IDENT) + bad_client_file_ident = 208, // Bad client file identifier (IDENT) + bad_server_version = 209, // Bad server version (IDENT, UPLOAD) + bad_client_version = 210, // Bad client version (IDENT, UPLOAD) + diverging_histories = 211, // Diverging histories (IDENT) + bad_changeset = 212, // Bad changeset (UPLOAD) + disabled_session = 213, // Disabled session +}; + +inline constexpr bool is_session_level_error(ProtocolError error) +{ + return int(error) >= 200 && int(error) <= 299; +} + +/// Returns null if the specified protocol error code is not defined by +/// ProtocolError. +const char* get_protocol_error_message(int error_code) noexcept; + +const std::error_category& protocol_error_category() noexcept; + +std::error_code make_error_code(ProtocolError) noexcept; + +} // namespace sync +} // namespace realm + +namespace std { + +template<> struct is_error_code_enum { + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace sync { +namespace protocol { + +using OutputBuffer = util::ResettableExpandableBufferOutputStream; +using session_ident_type = uint_fast16_t; +using file_ident_type = uint_fast64_t; +using version_type = uint_fast64_t; +using timestamp_type = uint_fast64_t; +using request_ident_type = uint_fast64_t; + + + + +class ClientProtocol { +public: + util::Logger& logger; + + enum class Error { + unknown_message = 101, // Unknown type of input message + bad_syntax = 102, // Bad syntax in input message head + limits_exceeded = 103, // Limits exceeded in input message + bad_changeset_header_syntax = 108, // Bad syntax in changeset header (DOWNLOAD) + bad_changeset_size = 109, // Bad changeset size in changeset header (DOWNLOAD) + bad_server_version = 111, // Bad server version in changeset header (DOWNLOAD) + bad_error_code = 114, ///< Bad error code (ERROR) + bad_decompression = 115, // Error in decompression (DOWNLOAD) + }; + + ClientProtocol(util::Logger& logger); + + + /// Messages sent by the client. + + void make_client_message(OutputBuffer& out, const std::string& client_info); + + void make_bind_message(OutputBuffer& out, session_ident_type session_ident, + const std::string& server_path, + const std::string& signed_user_token, + bool need_file_ident_pair); + + void make_refresh_message(OutputBuffer& out, session_ident_type session_ident, + const std::string& signed_user_token); + + void make_ident_message(OutputBuffer& out, session_ident_type session_ident, + file_ident_type server_file_ident, + file_ident_type client_file_ident, + int_fast64_t client_file_ident_secret, + SyncProgress progress); + + void make_upload_message(OutputBuffer& out, session_ident_type session_ident, + version_type client_version, version_type server_version, + size_t changeset_size, timestamp_type timestamp, + const std::unique_ptr& body_buffer); + + void make_unbind_message(OutputBuffer& out, session_ident_type session_ident); + + void make_mark_message(OutputBuffer& out, session_ident_type session_ident, + request_ident_type request_ident); + + void make_ping(OutputBuffer& out, uint_fast64_t timestamp, uint_fast64_t rtt); + + + // Messages received by the client. + + // parse_pong_received takes a (WebSocket) pong and parses it. + // The result of the parsing is handled by an object of type Connection. + // Typically, Connection would be the Connection class from client.cpp + template + void parse_pong_received(Connection& connection, const char* data, size_t size) + { + util::MemoryInputStream in; + in.set_buffer(data, data + size); + in.unsetf(std::ios_base::skipws); + + uint_fast64_t timestamp; + + char newline; + in >> timestamp >> newline; + bool good_syntax = in && size_t(in.tellg()) == size && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + + connection.receive_pong(timestamp); + return; + + bad_syntax: + logger.error("Bad syntax in input message '%1'", + StringData(data, size)); + connection.handle_protocol_error(Error::bad_syntax); // Throws + return; + } + + // parse_message_received takes a (WebSocket) message and parses it. + // The result of the parsing is handled by an object of type Connection. + // Typically, Connection would be the Connection class from client.cpp + template + void parse_message_received(Connection& connection, const char* data, size_t size) + { + util::MemoryInputStream in; + in.set_buffer(data, data + size); + in.unsetf(std::ios_base::skipws); + size_t header_size = 0; + std::string message_type; + in >> message_type; + logger.debug("message_type = %1", message_type); + + if (message_type == "download") { + session_ident_type session_ident; + SyncProgress progress; + int is_body_compressed; + size_t uncompressed_body_size, compressed_body_size; + char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, sp_9, sp_10, newline; + + in >> sp_1 >> session_ident >> sp_2 >> progress.scan_server_version >> sp_3 >> + progress.scan_client_version >> sp_4 >> progress.latest_server_version >> + sp_5 >> progress.latest_server_session_ident >> sp_6 >> + progress.latest_client_version >> sp_7 >> progress.downloadable_bytes >> + sp_8 >> is_body_compressed >> sp_9 >> uncompressed_body_size >> sp_10 >> + compressed_body_size >> newline; // Throws + + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && + sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ' && + sp_7 == ' ' && sp_8 == ' ' && sp_9 == ' ' && sp_10 == ' ' && + newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + if (uncompressed_body_size > s_max_body_size) + goto limits_exceeded; + + size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size; + if (header_size + body_size != size) + goto bad_syntax; + + BinaryData body(data + header_size, body_size); + BinaryData uncompressed_body; + + std::unique_ptr uncompressed_body_buffer; + // if is_body_compressed == true, we must decompress the received body. + if (is_body_compressed) { + uncompressed_body_buffer.reset(new char[uncompressed_body_size]); + std::error_code ec = util::compression::decompress(body.data(), compressed_body_size, + uncompressed_body_buffer.get(), + uncompressed_body_size); + + if (ec) { + logger.error("compression::inflate: %1", ec.message()); + connection.handle_protocol_error(Error::bad_decompression); + return; + } + + uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size); + } + else { + uncompressed_body = body; + } + + logger.debug("Download message compression: is_body_compressed = %1, " + "compressed_body_size=%2, uncompressed_body_size=%3", + is_body_compressed, compressed_body_size, uncompressed_body_size); + + util::MemoryInputStream in; + in.unsetf(std::ios_base::skipws); + in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size); + + std::vector received_changesets; + + // Loop through the body and find the changesets. + size_t position = 0; + while (position < uncompressed_body_size) { + version_type server_version; + version_type client_version; + timestamp_type origin_timestamp; + file_ident_type origin_client_file_ident; + size_t changeset_size; + char sp_1, sp_2, sp_3, sp_4, sp_5; + + in >> server_version >> sp_1 >> client_version >> sp_2 >> origin_timestamp >> + sp_3 >> origin_client_file_ident >> sp_4 >> changeset_size >> sp_5; + + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && + sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' '; + + if (!good_syntax) { + logger.error("Bad changeset header syntax"); + connection.handle_protocol_error(Error::bad_changeset_header_syntax); + return; + } + + // Update position to the end of the change set + position = size_t(in.tellg()) + changeset_size; + + if (position > uncompressed_body_size) { + logger.error("Bad changeset size"); + connection.handle_protocol_error(Error::bad_changeset_size); + return; + } + + if (server_version == 0) { + // The received changeset can never have version 0. + logger.error("Bad server version"); + connection.handle_protocol_error(Error::bad_server_version); + return; + } + + BinaryData changeset_data(uncompressed_body.data() + size_t(in.tellg()), changeset_size); + in.seekg(position); + + if (logger.would_log(util::Logger::Level::trace)) { + logger.trace("Received: DOWNLOAD CHANGESET(server_version=%1, client_version=%2, " + "origin_timestamp=%3, origin_client_file_ident=%4, changeset_size=%5)", + server_version, client_version, origin_timestamp, + origin_client_file_ident, changeset_size); // Throws + logger.trace("Changeset: %1", util::hex_dump(changeset_data.data(), changeset_size)); // Throws + } + + Transformer::RemoteChangeset changeset_2(server_version, client_version, + changeset_data, origin_timestamp, + origin_client_file_ident); + received_changesets.push_back(changeset_2); // Throws + } + + connection.receive_download_message(session_ident, progress, received_changesets); // Throws + return; + } + if (message_type == "unbound") { + session_ident_type session_ident; + char sp_1, newline; + in >> sp_1 >> session_ident >> newline; // Throws + bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && + newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + + connection.receive_unbound_message(session_ident); // Throws + return; + } + if (message_type == "error") { + int error_code; + size_t message_size; + bool try_again; + session_ident_type session_ident; + char sp_1, sp_2, sp_3, sp_4, newline; + in >> sp_1 >> error_code >> sp_2 >> message_size >> sp_3 >> try_again >> sp_4 >> + session_ident >> newline; // Throws + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' && + sp_4 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + if (header_size + message_size != size) + goto bad_syntax; + + bool unknown_error = !get_protocol_error_message(error_code); + if (unknown_error) { + logger.error("Bad error code"); // Throws + connection.handle_protocol_error(Error::bad_error_code); + return; + } + + std::string message{data + header_size, message_size}; // Throws (copy) + + connection.receive_error_message(error_code, message_size, try_again, session_ident, message); // Throws + return; + } + if (message_type == "mark") { + session_ident_type session_ident; + request_ident_type request_ident; + char sp_1, sp_2, newline; + in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; // Throws + bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && + sp_2 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + + connection.receive_mark_message(session_ident, request_ident); // Throws + return; + } + if (message_type == "alloc") { + session_ident_type session_ident; + file_ident_type server_file_ident, client_file_ident; + int_fast64_t client_file_ident_secret; + char sp_1, sp_2, sp_3, sp_4, newline; + in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >> + client_file_ident >> sp_4 >> client_file_ident_secret >> newline; // Throws + bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && + sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + + connection.receive_alloc_message(session_ident,server_file_ident, client_file_ident, + client_file_ident_secret); // Throws + return; + } + + logger.error("Unknown input message type '%1'", + StringData(data, size)); + connection.handle_protocol_error(Error::unknown_message); + return; + bad_syntax: + logger.error("Bad syntax in input message '%1'", + StringData(data, size)); + connection.handle_protocol_error(Error::bad_syntax); + return; + limits_exceeded: + logger.error("Limits exceeded in input message '%1'", + StringData(data, header_size)); + connection.handle_protocol_error(Error::limits_exceeded); + return; + } + +private: + static constexpr size_t s_max_body_size = std::numeric_limits::max(); +}; + + +class ServerProtocol { +public: + util::Logger& logger; + + enum class Error { + unknown_message = 101, // Unknown type of input message + bad_syntax = 102, // Bad syntax in input message head + limits_exceeded = 103, // Limits exceeded in input message + }; + + ServerProtocol(util::Logger& logger); + + // Messages sent by the server to the client + + void make_alloc_message(OutputBuffer& out, session_ident_type session_ident, + file_ident_type server_file_ident, file_ident_type client_file_ident, + int_fast64_t client_file_ident_secret); + + void make_unbound_message(OutputBuffer& out, session_ident_type session_ident); + + + class ChangesetInfo { + public: + version_type server_version; + version_type client_version; + HistoryEntry entry; + + ChangesetInfo(version_type server_version, version_type client_version, HistoryEntry entry): + server_version(server_version), + client_version(client_version), + entry(entry) + {} + }; + + void make_download_message(int protocol_version, OutputBuffer& out, session_ident_type session_ident, + version_type scan_server_version, + version_type scan_client_version, + version_type latest_server_version, + int_fast64_t latest_server_session_ident, + version_type latest_client_version, + uint_fast64_t downloadable_bytes, + const std::vector& changeset_infos); + + void make_error_message(OutputBuffer& out, ProtocolError error_code, + const char* message, size_t message_size, + bool try_again, session_ident_type session_ident); + + void make_mark_message(OutputBuffer& out, session_ident_type session_ident, + request_ident_type request_ident); + + void make_pong(OutputBuffer& out, uint_fast64_t timestamp); + + // Messages received by the server. + + // parse_ping_received takes a (WebSocket) ping and parses it. + // The result of the parsing is handled by an object of type Connection. + // Typically, Connection would be the Connection class from server.cpp + template + void parse_ping_received(Connection& connection, const char* data, size_t size) + { + util::MemoryInputStream in; + in.set_buffer(data, data + size); + in.unsetf(std::ios_base::skipws); + + int_fast64_t timestamp, rtt; + + char sp_1, newline; + in >> timestamp >> sp_1 >> rtt >> newline; + bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && + newline == '\n'; + if (!good_syntax) + goto bad_syntax; + + connection.receive_ping(timestamp, rtt); + return; + + bad_syntax: + logger.error("Bad syntax in ping message '%1'", + StringData(data, size)); + connection.handle_protocol_error(Error::bad_syntax); + return; + } + + // parse_message_received takes a (WebSocket) message and parses it. + // The result of the parsing is handled by an object of type Connection. + // Typically, Connection would be the Connection class from server.cpp + template + void parse_message_received(Connection& connection, const char* data, size_t size) + { + util::MemoryInputStream in; + in.set_buffer(data, data + size); + in.unsetf(std::ios_base::skipws); + size_t header_size = 0; + std::string message_type; + in >> message_type; + + if (message_type == "upload") { + session_ident_type session_ident; + version_type client_version, server_version; + size_t changeset_size; + timestamp_type timestamp; + char sp_1, sp_2, sp_3, sp_4, sp_5, newline; + in >> sp_1 >> session_ident >> sp_2 >> client_version >> sp_3 >> + server_version >> sp_4 >> changeset_size >> sp_5 >> timestamp >> + newline; + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' && + sp_4 == ' ' && sp_5 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + if (changeset_size > s_max_changeset_size) + goto limits_exceeded; + if (header_size + changeset_size != size) + goto bad_syntax; + + BinaryData changeset(data + header_size, changeset_size); + + connection.receive_upload_message(session_ident, client_version, + server_version, changeset, + timestamp); // Throws + return; + } + if (message_type == "mark") { + session_ident_type session_ident; + request_ident_type request_ident; + char sp_1, sp_2, newline; + in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; + bool good_syntax = in && size_t(in.tellg()) == size && + sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size; + + connection.receive_mark_message(session_ident, request_ident); // Throws + return; + } + if (message_type == "bind") { + session_ident_type session_ident; + size_t path_size; + size_t signed_user_token_size; + bool need_file_ident_pair; + char sp_1, sp_2, sp_3, sp_4, newline; + in >> sp_1 >> session_ident >> sp_2 >> path_size >> sp_3 >> + signed_user_token_size >> sp_4 >> need_file_ident_pair >> + newline; + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && + sp_3 == ' ' && sp_4 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + if (path_size == 0) + goto bad_syntax; + if (path_size > s_max_path_size) + goto limits_exceeded; + if (signed_user_token_size > s_max_signed_user_token_size) + goto limits_exceeded; + if (header_size + path_size + signed_user_token_size != size) + goto bad_syntax; + + std::string path {data + header_size, path_size}; // Throws + std::string signed_user_token {data + header_size + path_size, + signed_user_token_size}; // Throws + + connection.receive_bind_message(session_ident, std::move(path), + std::move(signed_user_token), + need_file_ident_pair); // Throws + return; + } + if (message_type == "refresh") { + session_ident_type session_ident; + size_t signed_user_token_size; + char sp_1, sp_2, newline; + in >> sp_1 >> session_ident >> sp_2 >> signed_user_token_size >> + newline; + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + if (signed_user_token_size > s_max_signed_user_token_size) + goto limits_exceeded; + if (header_size + signed_user_token_size != size) + goto bad_syntax; + + std::string signed_user_token {data + header_size, signed_user_token_size}; + + connection.receive_refresh_message(session_ident, std::move(signed_user_token)); // Throws + return; + } + if (message_type == "ident") { + session_ident_type session_ident; + file_ident_type server_file_ident, client_file_ident; + int_fast64_t client_file_ident_secret; + version_type scan_server_version, scan_client_version, latest_server_version; + int_fast64_t latest_server_session_ident; + char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, newline; + in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >> + client_file_ident >> sp_4 >> client_file_ident_secret >> sp_5 >> + scan_server_version >> sp_6 >> scan_client_version >> sp_7 >> + latest_server_version >> sp_8 >> latest_server_session_ident >> + newline; + bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && + sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && + sp_6 == ' ' && sp_7 == ' ' && sp_8 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size; + + connection.receive_ident_message(session_ident, server_file_ident, client_file_ident, + client_file_ident_secret, scan_server_version, scan_client_version, + latest_server_version, latest_server_session_ident); // Throws + return; + } + if (message_type == "unbind") { + session_ident_type session_ident; + char sp_1, newline; + in >> sp_1 >> session_ident >> newline; + bool good_syntax = in && size_t(in.tellg()) == size && + sp_1 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size; + + connection.receive_unbind_message(session_ident); // Throws + return; + } + if (message_type == "client") { + int_fast64_t protocol_version; + char sp_1, sp_2, newline; + size_t client_info_size; + in >> sp_1 >> protocol_version >> sp_2 >> client_info_size >> newline; + bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; + if (!good_syntax) + goto bad_syntax; + header_size = size_t(in.tellg()); + bool limits_exceeded = (client_info_size > s_max_client_info_size); + if (limits_exceeded) + goto limits_exceeded; + if (header_size + client_info_size != size) + goto bad_syntax; + + std::string client_info {data + header_size, client_info_size}; // Throws + + connection.receive_client_message(protocol_version, std::move(client_info)); // Throws + return; + } + + // unknown message + if (size < 256) + logger.error("Unknown input message type '%1'", StringData(data, size)); // Throws + else + logger.error("Unknown input message type '%1'.......", StringData(data, 256)); // Throws + + connection.handle_protocol_error(Error::unknown_message); + return; + + bad_syntax: + logger.error("Bad syntax in input message '%1'", + StringData(data, size)); + connection.handle_protocol_error(Error::bad_syntax); // Throws + return; + limits_exceeded: + logger.error("Limits exceeded in input message '%1'", + StringData(data, header_size)); + connection.handle_protocol_error(Error::limits_exceeded); // Throws + return; + } + +private: + static constexpr size_t s_max_head_size = 256; + static constexpr size_t s_max_signed_user_token_size = 2048; + static constexpr size_t s_max_client_info_size = 1024; + static constexpr size_t s_max_path_size = 1024; + static constexpr size_t s_max_changeset_size = std::numeric_limits::max(); + + util::compression::CompressMemoryArena m_compress_memory_arena; + + // Permanent buffer to use for internal purposes such as compression. + std::vector m_buffer; + + // Outputbuffer to use for internal purposes such as creating the + // download body. + OutputBuffer m_output_buffer; + + void insert_single_changeset_download_message(OutputBuffer& out, const ChangesetInfo& changeset_info); +}; + +} // namespace protocol +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_PROTOCOL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server.hpp new file mode 100644 index 0000000..57940ed --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server.hpp @@ -0,0 +1,167 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_SERVER_HPP +#define REALM_SYNC_SERVER_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace realm { +namespace sync { + +class Server { +public: + class Clock; + + struct Config { + Config() {} + + /// The maximum number of Realm files that will be kept open + /// concurrently by this server. The server keeps a cache of open Realm + /// files for efficiency reasons. + long max_open_files = 256; + + /// An optional time provider to be used by the server. + /// If no time provider is specified, the server will use the + /// system clock. + Clock* clock = nullptr; + + /// An optional logger to be used by the server. If no logger is + /// specified, the server will use an instance of util::StderrLogger + /// with the log level threshold set to util::Logger::Level::info. The + /// server does not require a thread-safe logger, and it guarantees that + /// all logging happens on behalf of start() and run() (which are not + /// allowed to execute concurrently). + util::Logger* logger = nullptr; + + /// An optional sink for recording metrics about the internal operation + /// of the server. For the list of counters and gauges see + /// "doc/monitoring.md". + Metrics* metrics = nullptr; + + /// The address at which the listening socket is bound. + /// The address can be a name or on numerical form. + /// Use "localhost" to listen on the loopback interface. + std::string listen_address; + + /// The port at which the listening socket is bound. + /// The port can be a name or on numerical form. + /// Use the empty string to have the system assign a dynamic + /// listening port. + std::string listen_port; + + bool reuse_address = true; + + /// The listening socket accepts TLS/SSL connections if `ssl` is + /// true, and non-secure tcp connections otherwise. + bool ssl = false; + + /// The path of the certificate that will be sent to clients during + /// the SSL/TLS handshake. + /// + /// From the point of view of OpenSSL, this file will be passed to + /// `SSL_CTX_use_certificate_chain_file()`. + /// + /// This option is ignore if `ssl` is false. + std::string ssl_certificate_path; + + /// The path of the private key corresponding to the certificate. + /// + /// From the point of view of OpenSSL, this file will be passed to + /// `SSL_CTX_use_PrivateKey_file()`. + /// + /// This option is ignore if `ssl` is false. + std::string ssl_certificate_key_path; + + // A connection which has not been sending any messages or pings for + // `idle_timeout_ms` is considered idle and will be dropped by the server. + uint_fast64_t idle_timeout_ms = 1800000; + + // How often the server scans through the connection list to drop idle ones. + uint_fast64_t drop_period_ms = 60000; + }; + + Server(const std::string& root_dir, util::Optional public_key, Config = {}); + Server(Server&&) noexcept; + ~Server() noexcept; + + /// start() binds a listening socket to the address and port specified in + /// Config and starts accepting connections. + /// The resolved endpoint (including the dynamically assigned port, if requested) + /// can be obtained by calling listen_endpoint(). + /// This can be done immediately after start() returns. + void start(); + + /// A helper function, for backwards compatibility, that starts a listening + /// socket without SSL at the specified address and port. + void start(const std::string& listen_address, + const std::string& listen_port, + bool reuse_address = true); + + /// Return the resolved and bound endpoint of the listening socket. + util::network::Endpoint listen_endpoint() const; + + /// Run the internal event-loop of the server. At most one thread may + /// execute run() at any given time. It is an error if run() is called + /// before start() has been successfully executed. The call to run() will + /// not return until somebody calls stop(). + void run(); + + /// Stop any thread that is currently executing run(). This function may be + /// called by any thread. + void stop() noexcept; + + /// Must not be called while run() is executing. + uint_fast64_t errors_seen() const noexcept; + + /// Initialise the directory structure as required for correct operation of + /// the server. This is a static function, as it should be run on the \a + /// root_path prior to instantiating the \c Server object. + static void init_directory_structure(const std::string& root_path, util::Logger& logger); + + // A connection which has not been sending any messages or pings for + // `idle_timeout_ms` is considered idle and will be dropped by the server. + void set_idle_timeout_ms(uint_fast64_t idle_timeout_ms); + + +private: + class Implementation; + std::unique_ptr m_impl; +}; + + +class Server::Clock { +public: + virtual int_fast64_t now() = 0; + + virtual ~Clock() {} +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_SERVER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp new file mode 100644 index 0000000..369fffd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp @@ -0,0 +1,58 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_SERVER_CONFIGURATION_HPP +#define REALM_SYNC_SERVER_CONFIGURATION_HPP + +// Realm headers +#include +#include + +namespace realm { +namespace config { + +struct Configuration { + std::string listen_address = "127.0.0.1"; + std::string listen_port = ""; // Empty means choose default based on `ssl`. + realm::util::Optional root_dir; + std::string user_data_dir; + std::string internal_data_dir; + realm::util::Optional public_key_path; + realm::util::Optional config_file_path; + bool reuse_address = true; + bool disable_sync = false; + realm::util::Logger::Level log_level = realm::util::Logger::Level::info; + realm::util::Optional log_path; + long max_open_files = 256; + bool ssl = false; + std::string ssl_certificate_path; + std::string ssl_certificate_key_path; + std::string dashboard_stats_endpoint = "localhost:28125"; + uint_fast64_t drop_period_s = 60; + uint_fast64_t idle_timeout_s = 1800; +}; + +void show_help(const std::string& program_name); +Configuration build_configuration(int argc, char* argv[]); +Configuration load_configuration(std::string configuration_file_path); + +} // namespace config +} // namespace realm + +#endif // REALM_SYNC_SERVER_CONFIGURATION_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/transform.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/transform.hpp new file mode 100644 index 0000000..d53f5d2 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/transform.hpp @@ -0,0 +1,316 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_TRANSFORM_HPP +#define REALM_SYNC_TRANSFORM_HPP + +#include + +#include +#include +#include + +namespace realm { +namespace sync { + +class HistoryEntry { +public: + using timestamp_type = uint_fast64_t; + using file_ident_type = uint_fast64_t; + using version_type = _impl::History::version_type; + + /// The time of origination of the changes referenced by this history entry, + /// meassured as the number of milliseconds since 2015-01-01T00:00:00Z, not + /// including leap seconds. For changes of local origin, this is the local + /// time at the point when the local transaction was committed. For changes + /// of remote origin, it is the remote time of origin at the client + /// identified by `origin_client_file_ident`. + timestamp_type origin_timestamp; + + /// For changes of local origin, `origin_client_file_ident` is always + /// zero. For changes of remote origin, this history entry was produced by + /// the integration of a changeset received from a remote peer P. In some + /// cases, that changeset may itself have been produced by the integration + /// on P of a changeset received from another remote peer. In any case, as + /// long as these changes are of remote origin, `origin_client_file_ident` + /// identifies the peer on which they originated, which may or may not be P. + /// + /// More concretely, on the server-side, the remote peer is a client, and + /// and the changes always originate from that client, so + /// `origin_client_file_ident` always refer to that client. Conversely, on + /// the client-side, the remote peer is the server, and the server received + /// the original changeset from a client, so `origin_client_file_ident` + /// refers to that client. + /// + /// Note that *peer* is used colloquially here to refer to a particular + /// synchronization participant. In reality, a synchronization participant + /// is either a server-side file, or a particular client-side file + /// associated with that server-side file. To make things even more + /// confusing, a single client application may contain multiple client-side + /// files associated with the same server-side file. In the same vein, + /// *client* should be understood as client-side file, and *remote peer* as + /// any other file from the set of associated files, even other such files + /// contained within the same client application, if there are any. + file_ident_type origin_client_file_ident; + + /// For changes of local origin, `remote_version` is the version produced on + /// the remote peer by the last changeset integrated locally prior to the + /// production of the changeset referenced by this history entry, or zero if + /// no remote changeset was integrated yet. This only makes sense when there + /// is a unique remote peer, and since that is not the case on the server, + /// the server cannot be the originator of any changes. + /// + /// For changes of remote origin, this history entry was produced by the + /// integration of a changeset directly received from a remote peer P, and + /// `remote_version` is then the version produced on P by that + /// changeset. Note that such changes may have originated from a different + /// peer (not P), but `remote_version` will still be the version produced on + /// P. + /// + /// More concretely, on the server-side, the remote peer is a client, and + /// the changes always originate from that client, and `remote_version` is + /// the `` specified in an UPLOAD message of the + /// client-server communication protocol. Conversely, for changes of remote + /// origin on the client-side, the remote peer is the server, and + /// `remote_version` is the specified in a received + /// DOWNLOAD message. + version_type remote_version; + + /// Referenced memory is not owned by this class. + BinaryData changeset; +}; + + +/// The interface between the sync history and the operational transformer +/// (Transformer). +class TransformHistory { +public: + using timestamp_type = HistoryEntry::timestamp_type; + using file_ident_type = HistoryEntry::file_ident_type; + using version_type = HistoryEntry::version_type; + + /// Get the first history entry whose changeset produced a version that + /// succeeds `begin_version` and, and does not succeed `end_version`, and + /// whose changeset was not produced by integration of a changeset received + /// from the specified remote peer. + /// + /// The ownership of the memory referenced by `entry.changeset` is **not** + /// passed to the caller upon return. The callee retains ownership. + /// + /// \param begin_version, end_version The range of versions to consider. If + /// `begin_version` is equal to `end_version`, this is the empty range. If + /// `begin_version` is zero, it means that everything preceeding + /// `end_version` is to be considered, which is again the empty range if + /// `end_version` is also zero. Zero is is special value in that no + /// changeset produces that version. It is an error if `end_version` + /// preceeds `begin_version`, or if `end_version` is zero and + /// `begin_version` is not. + /// + /// \param not_from_remote_client_file_ident Skip entries whose changeset is + /// produced by integration of changesets received from this remote + /// peer. Zero if the remote peer is the server, otherwise the peer + /// identifier of a client. + /// + /// \param only_nonempty Skip entries with empty changesets. + /// + /// \return The version produced by the changeset of the located history + /// entry, or zero if no history entry exists matching the specified + /// criteria. + virtual version_type find_history_entry(version_type begin_version, version_type end_version, + file_ident_type not_from_remote_client_file_ident, + bool only_nonempty, + HistoryEntry& entry) const noexcept = 0; + + /// Copy a contiguous sequence of bytes from the specified reciprocally + /// transformed changeset into the specified buffer. The targeted history + /// entry is the one whose untransformed changeset produced the specified + /// version. Copying starts at the specified offset within the transform, + /// and will continue until the end of the transform or the end of the + /// buffer, whichever comes first. The first copied byte is always placed in + /// `buffer[0]`. The number of copied bytes is returned. + /// + /// \param remote_client_file_ident Zero if the remote peer is the server, + /// otherwise the peer identifier of a client. + virtual size_t read_reciprocal_transform(version_type version, + file_ident_type remote_client_file_ident, + size_t offset, char* buffer, size_t size) const = 0; + + /// Replace a contiguous chunk of bytes within the specified reciprocally + /// transformed changeset. The targeted history entry is the one whose + /// untransformed changeset produced the specified version. If the new chunk + /// has a different size than the on it replaces, subsequent bytes (those + /// beyond the end of the replaced chunk) are shifted to lower or higher + /// offsets accordingly. If `replaced_size` is equal to `size_t(-1)`, the + /// replaced chunk extends from `offset` to the end of the transform. Let + /// `replaced_size_2` be the actual size of the replaced chunk, then the + /// total number of bytes in the transform will increase by `size - + /// replaced_size_2`. It is an error if `replaced_size` is not `size_t(-1)` + /// and `offset + replaced_size` is greater than the size of the transform. + /// + /// \param remote_client_file_ident See read_reciprocal_transform(). + /// + /// \param offset The index of the first replaced byte relative to the + /// beginning of the transform. + /// + /// \param replaced_size The number of bytes in the replaced chunk. + /// + /// \param data The buffer holding the replacing chunk. + /// + /// \param size The number of bytes in the replacing chunk, which is also + /// the number of bytes that will be read from the specified buffer. + virtual void write_reciprocal_transform(version_type version, + file_ident_type remote_client_file_ident, + size_t offset, size_t replaced_size, + const char* data, size_t size) = 0; + + virtual ~TransformHistory() noexcept {} +}; + + +class TransformError; // Exception + + +class Transformer { +public: + using timestamp_type = HistoryEntry::timestamp_type; + using file_ident_type = HistoryEntry::file_ident_type; + using version_type = HistoryEntry::version_type; + + struct RemoteChangeset { + /// The version produced on the remote peer by this changeset. + /// + /// On the server, the remote peer is the client from which the + /// changeset originated, and `remote_version` is the client version + /// produced by the changeset on that client. + /// + /// On a client, the remote peer is the server, and `remote_version` is + /// the server version produced by this changeset on the server. Since + /// the server is never the originator of changes, this changeset must + /// in turn have been produced on the server by integration of a + /// changeset uploaded by some other client. + version_type remote_version; + + /// The last local version that has been integrated into + /// `remote_version`. + /// + /// A local version, L, has been integrated into a remote version, R, + /// when, and only when L is the latest local version such that all + /// preceeding changesets in the local history have been integrated by + /// the remote peer prior to R. + /// + /// On the server, this is the last server version integrated into the + /// client version `remote_version`. On a client, it is the last client + /// version integrated into the server version `remote_version`. + version_type last_integrated_local_version; + + /// The changeset itself. + BinaryData data; + + /// Same meaning as `HistoryEntry::origin_timestamp`. + timestamp_type origin_timestamp; + + /// Same meaning as `HistoryEntry::origin_client_file_ident`. + file_ident_type origin_client_file_ident; + + RemoteChangeset(version_type rv, version_type lv, BinaryData d, timestamp_type ot, + file_ident_type fi); + }; + + /// Produce an operationally transformed version of the specified changeset, + /// which is assumed to be of remote origin, and received from remote peer + /// P. Note that P is not necessarily the peer from which the changes + /// originated. + /// + /// Operational transformation is carried out between the specified + /// changeset and all causally unrelated changesets in the local history. A + /// changeset in the local history is causally unrelated if, and only if it + /// occurs after the local changeset that produced + /// `remote_changeset.last_integrated_local_version` and is not a produced + /// by integration of a changeset received from P. This assumes that + /// `remote_changeset.last_integrated_local_version` is set to the local + /// version produced by the last local changeset, that was integrated by P + /// before P produced the specified changeset. + /// + /// The operational transformation is reciprocal (two-way), so it also + /// transforms the causally unrelated local changesets. This process does + /// not modify the history itself (the changesets available through + /// TransformHistory::get_history_entry()), instead the reciprocally + /// transformed changesets are stored separately, and individually for each + /// remote peer, such that they can participate in transformation of the + /// next incoming changeset from P. Note that the peer identifier of P can + /// be derived from `origin_client_file_ident` and information about whether + /// the local peer is a server or a client. + /// + /// In general, if A and B are two causally unrelated (alternative) + /// changesets based on the same version V, then the operational + /// transformation between A and B produces changesets A' and B' such that + /// both of the concatenated changesets A + B' and B + A' produce the same + /// final state when applied to V. Operational transformation is meaningful + /// only when carried out between alternative changesets based on the same + /// version. + /// + /// \return The size of the transformed version of the specified + /// changeset. Upon return, the changeset itself is stored in the specified + /// output buffer. + /// + /// \throw TransformError Thrown if operational transformation fails due to + /// a problem with the specified changeset. + virtual size_t transform_remote_changeset(TransformHistory&, + version_type current_local_version, + RemoteChangeset changeset, + util::Buffer& output_buffer) = 0; + + virtual ~Transformer() noexcept {} +}; + + +/// \param local_client_file_ident The server assigned local client file +/// identifier. This must be zero on the server-side, and only on the +/// server-side. +std::unique_ptr make_transformer(Transformer::file_ident_type local_client_file_ident); + + + + +// Implementation + +class TransformError: public std::runtime_error { +public: + TransformError(const std::string& message): + std::runtime_error(message) + { + } +}; + +inline Transformer::RemoteChangeset::RemoteChangeset(version_type rv, version_type lv, + BinaryData d, timestamp_type ot, + file_ident_type fi): + remote_version(rv), + last_integrated_local_version(lv), + data(d), + origin_timestamp(ot), + origin_client_file_ident(fi) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_TRANSFORM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/version.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/version.hpp new file mode 100644 index 0000000..76513e6 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/sync/version.hpp @@ -0,0 +1,34 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2013] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_VERSION_HPP +#define REALM_SYNC_VERSION_HPP + +#include + +#define REALM_SYNC_VER_MAJOR 1 +#define REALM_SYNC_VER_MINOR 5 +#define REALM_SYNC_VER_PATCH 2 +#define REALM_SYNC_PRODUCT_NAME "realm-sync" + +#define REALM_SYNC_VER_STRING REALM_QUOTE(REALM_SYNC_VER_MAJOR) "." \ + REALM_QUOTE(REALM_SYNC_VER_MINOR) "." REALM_QUOTE(REALM_SYNC_VER_PATCH) +#define REALM_SYNC_VER_CHUNK "[" REALM_SYNC_PRODUCT_NAME "-" REALM_SYNC_VER_STRING "]" + +#endif // REALM_SYNC_VERSION_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/table.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table.hpp new file mode 100644 index 0000000..b4bd95f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table.hpp @@ -0,0 +1,2443 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_TABLE_HPP +#define REALM_TABLE_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +class BacklinkColumn; +class BinaryColumy; +class ConstTableView; +class Group; +class LinkColumn; +class LinkColumnBase; +class LinkListColumn; +class LinkView; +class SortDescriptor; +class StringIndex; +class TableView; +class TableViewBase; +class TimestampColumn; +template +class Columns; +template +class SubQuery; +struct LinkTargetInfo; + +struct SubTable { +}; +struct Link { +}; +typedef Link LinkList; +typedef Link BackLink; + +namespace _impl { +class TableFriend; +} + +class Replication; + + +/// FIXME: Table assignment (from any group to any group) could be made aliasing +/// safe as follows: Start by cloning source table into target allocator. On +/// success, assign, and then deallocate any previous structure at the target. +/// +/// FIXME: It might be desirable to have a 'table move' feature between two +/// places inside the same group (say from a subtable or a mixed column to group +/// level). This could be done in a very efficient manner. +/// +/// FIXME: When compiling in debug mode, all public non-static table functions +/// should REALM_ASSERT(is_attached()). +class Table { +public: + /// Construct a new freestanding top-level table with static + /// lifetime. + /// + /// This constructor should be used only when placing a table + /// instance on the stack, and it is then the responsibility of + /// the application that there are no objects of type TableRef or + /// ConstTableRef that refer to it, or to any of its subtables, + /// when it goes out of scope. To create a top-level table with + /// dynamic lifetime, use Table::create() instead. + Table(Allocator& = Allocator::get_default()); + + /// Construct a copy of the specified table as a new freestanding + /// top-level table with static lifetime. + /// + /// This constructor should be used only when placing a table + /// instance on the stack, and it is then the responsibility of + /// the application that there are no objects of type TableRef or + /// ConstTableRef that refer to it, or to any of its subtables, + /// when it goes out of scope. To create a top-level table with + /// dynamic lifetime, use Table::copy() instead. + Table(const Table&, Allocator& = Allocator::get_default()); + + ~Table() noexcept; + + Allocator& get_alloc() const; + + /// Construct a new freestanding top-level table with dynamic lifetime. + static TableRef create(Allocator& = Allocator::get_default()); + + /// Construct a copy of the specified table as a new freestanding top-level + /// table with dynamic lifetime. + TableRef copy(Allocator& = Allocator::get_default()) const; + + /// Returns true if, and only if this accessor is currently attached to an + /// underlying table. + /// + /// A table accessor may get detached from the underlying row for various + /// reasons (see below). When it does, it no longer refers to anything, and + /// can no longer be used, except for calling is_attached(). The + /// consequences of calling other non-static functions on a detached table + /// accessor are unspecified. Table accessors obtained by calling functions in + /// the Realm API are always in the 'attached' state immediately upon + /// return from those functions. + /// + /// A table accessor of a free-standing table never becomes detached (except + /// during its eventual destruction). A group-level table accessor becomes + /// detached if the underlying table is removed from the group, or when the + /// group accessor is destroyed. A subtable accessor becomes detached if the + /// underlying subtable is removed, or if the parent table accessor is + /// detached. A table accessor does not become detached for any other reason + /// than those mentioned here. + /// + /// FIXME: High level language bindings will probably want to be able to + /// explicitely detach a group and all tables of that group if any modifying + /// operation fails (e.g. memory allocation failure) (and something similar + /// for freestanding tables) since that leaves the group in state where any + /// further access is disallowed. This way they will be able to reliably + /// intercept any attempt at accessing such a failed group. + /// + /// FIXME: The C++ documentation must state that if any modifying operation + /// on a group (incl. tables, subtables, and specs) or on a free standing + /// table (incl. subtables and specs) fails, then any further access to that + /// group (except ~Group()) or freestanding table (except ~Table()) has + /// undefined behaviour and is considered an error on behalf of the + /// application. Note that even Table::is_attached() is disallowed in this + /// case. + bool is_attached() const noexcept; + + /// Get the name of this table, if it has one. Only group-level tables have + /// names. For a table of any other kind, this function returns the empty + /// string. + StringData get_name() const noexcept; + + // Whether or not elements can be null. + bool is_nullable(size_t col_ndx) const; + + //@{ + /// Conventience functions for inspecting the dynamic table type. + /// + /// These functions behave as if they were called on the descriptor returned + /// by get_descriptor(). + size_t get_column_count() const noexcept; + DataType get_column_type(size_t column_ndx) const noexcept; + StringData get_column_name(size_t column_ndx) const noexcept; + size_t get_column_index(StringData name) const noexcept; + //@} + + //@{ + /// Convenience functions for manipulating the dynamic table type. + /// + /// These function must be called only for tables with independent dynamic + /// type. A table has independent dynamic type if the function + /// has_shared_type() returns false. A table that is a direct member of a + /// group has independent dynamic type. So does a free-standing table, and a + /// subtable in a column of type 'mixed'. All other tables have shared + /// dynamic type. The consequences of calling any of these functions for a + /// table with shared dynamic type are undefined. + /// + /// Apart from that, these functions behave as if they were called on the + /// descriptor returned by get_descriptor(). Note especially that the + /// `_link` suffixed functions must be used when inserting link-type + /// columns. + /// + /// If you need to change the shared dynamic type of the subtables in a + /// subtable column, consider using the API offered by the Descriptor class. + /// + /// \sa has_shared_type() + /// \sa get_descriptor() + + size_t add_column(DataType type, StringData name, bool nullable = false, DescriptorRef* subdesc = nullptr); + void insert_column(size_t column_ndx, DataType type, StringData name, bool nullable = false, + DescriptorRef* subdesc = nullptr); + + // Todo, these prototypes only exist for backwards compatibility. We should remove them because they are error + // prone (optional arguments and implicit bool to null-ptr conversion) + size_t add_column(DataType type, StringData name, DescriptorRef* subdesc) + { + return add_column(type, name, false, subdesc); + } + void insert_column(size_t column_ndx, DataType type, StringData name, DescriptorRef* subdesc) + { + insert_column(column_ndx, type, name, false, subdesc); + } + + size_t add_column_link(DataType type, StringData name, Table& target, LinkType link_type = link_Weak); + void insert_column_link(size_t column_ndx, DataType type, StringData name, Table& target, + LinkType link_type = link_Weak); + void remove_column(size_t column_ndx); + void rename_column(size_t column_ndx, StringData new_name); + //@} + + //@{ + + /// has_search_index() returns true if, and only if a search index has been + /// added to the specified column. Rather than throwing, it returns false if + /// the table accessor is detached or the specified index is out of range. + /// + /// add_search_index() adds a search index to the specified column of this + /// table. It has no effect if a search index has already been added to the + /// specified column (idempotency). + /// + /// remove_search_index() removes the search index from the specified column + /// of this table. It has no effect if the specified column has no search + /// index. The search index cannot be removed from the primary key of a + /// table. + /// + /// This table must be a root table; that is, it must have an independent + /// descriptor. Freestanding tables, group-level tables, and subtables in a + /// column of type 'mixed' are all examples of root tables. See add_column() + /// for more on this. + /// + /// \param column_ndx The index of a column of this table. + + bool has_search_index(size_t column_ndx) const noexcept; + void add_search_index(size_t column_ndx); + void remove_search_index(size_t column_ndx); + + //@} + + //@{ + /// Get the dynamic type descriptor for this table. + /// + /// Every table has an associated descriptor that specifies its dynamic + /// type. For simple tables, that is, tables without subtable columns, the + /// dynamic type can be inspected and modified directly using member + /// functions such as get_column_count() and add_column(). For more complex + /// tables, the type is best managed through the associated descriptor + /// object which is returned by this function. + /// + /// \sa has_shared_type() + DescriptorRef get_descriptor(); + ConstDescriptorRef get_descriptor() const; + //@} + + //@{ + /// Get the dynamic type descriptor for the column with the + /// specified index. That column must have type 'table'. + /// + /// This is merely a shorthand for calling `get_subdescriptor(column_ndx)` + /// on the descriptor returned by `get_descriptor()`. + DescriptorRef get_subdescriptor(size_t column_ndx); + ConstDescriptorRef get_subdescriptor(size_t column_ndx) const; + //@} + + //@{ + /// Get access to an arbitrarily nested dynamic type descriptor. + /// + /// The returned descriptor is the one you would get by calling + /// Descriptor::get_subdescriptor() once for each entry in the specified + /// path, starting with the descriptor returned by get_descriptor(). The + /// path is allowed to be empty. + typedef std::vector path_vec; + DescriptorRef get_subdescriptor(const path_vec& path); + ConstDescriptorRef get_subdescriptor(const path_vec& path) const; + //@} + + //@{ + /// Convenience functions for manipulating nested table types. + /// + /// These functions behave as if they were called on the descriptor returned + /// by `get_subdescriptor(path)`. These function must be called only on + /// tables with independent dynamic type. + /// + /// \return The value returned by add_subcolumn(), is the index of + /// the added column within the descriptor referenced by the + /// specified path. + /// + /// \sa Descriptor::add_column() + /// \sa has_shared_type() + size_t add_subcolumn(const path_vec& path, DataType type, StringData name); + void insert_subcolumn(const path_vec& path, size_t column_ndx, DataType type, StringData name); + void remove_subcolumn(const path_vec& path, size_t column_ndx); + void rename_subcolumn(const path_vec& path, size_t column_ndx, StringData new_name); + //@} + + /// Does this table share its type with other tables? + /// + /// Tables that are direct members of groups have independent + /// dynamic types. The same is true for free-standing tables and + /// subtables in coulmns of type 'mixed'. For such tables, this + /// function returns false. + /// + /// When a table has a column of type 'table', the cells in that + /// column contain subtables. All those subtables have the same + /// dynamic type, and they share a single type descriptor. For all + /// such subtables, this function returns true. See + /// Descriptor::is_root() for more on this. + /// + /// Please note that Table functions that modify the dynamic type + /// directly, such as add_column(), are only allowed to be used on + /// tables with non-shared type. If you need to modify a shared + /// type, you will have to do that through the descriptor returned + /// by get_descriptor(), but note that it will then affect all the + /// tables sharing that descriptor. + /// + /// \sa get_descriptor() + /// \sa Descriptor::is_root() + bool has_shared_type() const noexcept; + + + template + Columns column(size_t column); // FIXME: Should this one have been declared noexcept? + template + Columns column(const Table& origin, size_t origin_column_ndx); + + template + SubQuery column(size_t column, Query subquery); + template + SubQuery column(const Table& origin, size_t origin_column_ndx, Query subquery); + + // Table size and deletion + bool is_empty() const noexcept; + size_t size() const noexcept; + + typedef BasicRowExpr
RowExpr; + typedef BasicRowExpr ConstRowExpr; + + RowExpr get(size_t row_ndx) noexcept; + ConstRowExpr get(size_t row_ndx) const noexcept; + + RowExpr front() noexcept; + ConstRowExpr front() const noexcept; + + RowExpr back() noexcept; + ConstRowExpr back() const noexcept; + + RowExpr operator[](size_t row_ndx) noexcept; + ConstRowExpr operator[](size_t row_ndx) const noexcept; + + + //@{ + + /// Row handling. + /// + /// remove() removes the specified row from the table and shifts all rows at + /// higher index to fill the vacated slot. This operation assumes that the + /// table is ordered, and it is therefore allowed only on tables **without** + /// link columns, as link columns are only allowed in unordered tables. + /// + /// move_last_over() removes the specified row from the table, and if it is + /// not the last row in the table, it then moves the last row into the + /// vacated slot. This operation assumes that the table is unordered, and it + /// may therfore be used on tables with link columns. + /// + /// The removal of a row from an unordered table (move_last_over()) may + /// cause other linked rows to be cascade-removed. The clearing of a table + /// may also cause linked rows to be cascade-removed, but in this respect, + /// the effect is exactly as if each row had been removed individually. See + /// Descriptor::set_link_type() for details. + + size_t add_empty_row(size_t num_rows = 1); + void insert_empty_row(size_t row_ndx, size_t num_rows = 1); + void remove(size_t row_ndx); + void remove_last(); + void move_last_over(size_t row_ndx); + void clear(); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2); + //@} + + /// Replaces all links to \a row_ndx with links to \a new_row_ndx. + /// + /// This operation is usually followed by Table::move_last_over() + /// as part of Table::set_int_unique() or Table::set_string_unique() + /// or Table::set_null_unique() detecting a collision. + /// + /// \sa Table::move_last_over() + /// \sa Table::set_int_unique() + /// \sa Table::set_string_unique() + /// \sa Table::set_null_unique() + void merge_rows(size_t row_ndx, size_t new_row_ndx); + + // Get cell values. Will assert if the requested type does not match the column type + int64_t get_int(size_t column_ndx, size_t row_ndx) const noexcept; + bool get_bool(size_t column_ndx, size_t row_ndx) const noexcept; + OldDateTime get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept; + float get_float(size_t column_ndx, size_t row_ndx) const noexcept; + double get_double(size_t column_ndx, size_t row_ndx) const noexcept; + StringData get_string(size_t column_ndx, size_t row_ndx) const noexcept; + BinaryData get_binary(size_t column_ndx, size_t row_ndx) const noexcept; + Mixed get_mixed(size_t column_ndx, size_t row_ndx) const noexcept; + DataType get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept; + Timestamp get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept; + + /// Return data from position 'pos' and onwards. If the blob is distributed + /// across multiple arrays, you will only get data from one array. 'pos' + /// will be updated to be an index to next available data. It will be 0 + /// if no more data. + BinaryData get_binary_at(size_t col_ndx, size_t ndx, size_t& pos) const noexcept; + + template + T get(size_t c, size_t r) const noexcept; + + size_t get_link(size_t column_ndx, size_t row_ndx) const noexcept; + bool is_null_link(size_t column_ndx, size_t row_ndx) const noexcept; + LinkViewRef get_linklist(size_t column_ndx, size_t row_ndx); + ConstLinkViewRef get_linklist(size_t column_ndx, size_t row_ndx) const; + size_t get_link_count(size_t column_ndx, size_t row_ndx) const noexcept; + bool linklist_is_empty(size_t column_ndx, size_t row_ndx) const noexcept; + bool is_null(size_t column_ndx, size_t row_ndx) const noexcept; + + TableRef get_link_target(size_t column_ndx) noexcept; + ConstTableRef get_link_target(size_t column_ndx) const noexcept; + + //@{ + + /// Set cell values. + /// + /// It is an error to specify a column index, row index, or string position + /// that is out of range. + /// + /// The number of bytes in a string value must not exceed `max_string_size`, + /// and the number of bytes in a binary data value must not exceed + /// `max_binary_size`. String must also contain valid UTF-8 encodings. These + /// requirements also apply when modifying a string with insert_substring() + /// and remove_substring(), and for strings in a mixed columnt. Passing, or + /// producing an oversized string or binary data value will cause an + /// exception to be thrown. + /// + /// The "unique" variants (set_int_unique(), set_string_unique(), set_null_unique()) + /// are intended to be used in the implementation of primary key support. They + /// check if the given column already contains one or more values that are + /// equal to \a value, and if there are conflicts, it calls + /// Table::merge_rows() for the row_ndx to be replaced by the + /// existing row, followed by a Table::move_last_over() of row_ndx. The + /// return value is always a row index of a row that contains \a value in + /// the specified column, possibly different from \a row_ndx if a conflict + /// occurred. Users intending to implement primary keys must therefore + /// manually check for duplicates if they want to raise an error instead. + /// + /// NOTE: It is an error to call either function after adding elements to a + /// linklist in the object. In general, calling set_int_unique() or + /// set_string_unique() or set_null_unique() should be the first thing that + /// happens after creating a row. These limitations are imposed by limitations + /// in the Realm Object Server and may be relaxed in the future. A violation of + /// these rules results in a LogicError being thrown. + /// + /// add_int() adds a 64-bit signed integer to the current value of the + /// cell. If the addition would cause signed integer overflow or + /// underflow, the addition "wraps around" with semantics similar to + /// unsigned integer arithmetic, such that Table::max_integer + 1 == + /// Table::min_integer and Table::min_integer - 1 == Table::max_integer. + /// Note that the wrapping is platform-independent (all platforms wrap in + /// the same way regardless of integer representation). If the existing + /// value in the cell is null, a LogicError exception is thrown. + /// + /// insert_substring() inserts the specified string into the currently + /// stored string at the specified position. The position must be less than + /// or equal to the size of the currently stored string. + /// + /// remove_substring() removes the specified byte range from the currently + /// stored string. The beginning of the range (\a pos) must be less than or + /// equal to the size of the currently stored string. If the specified range + /// extends beyond the end of the currently stored string, it will be + /// silently clamped. + /// + /// String level modifications performed via insert_substring() and + /// remove_substring() are mergable and subject to operational + /// transformation. That is, the effect of two causally unrelated + /// modifications will in general both be retained during synchronization. + + static const size_t max_string_size = 0xFFFFF8 - Array::header_size - 1; + static const size_t max_binary_size = 0xFFFFF8 - Array::header_size; + + // FIXME: These limits should be chosen independently of the underlying + // platform's choice to define int64_t and independent of the integer + // representation. The current values only work for 2's complement, which is + // not guaranteed by the standard. + static constexpr int_fast64_t max_integer = std::numeric_limits::max(); + static constexpr int_fast64_t min_integer = std::numeric_limits::min(); + + void set_int(size_t column_ndx, size_t row_ndx, int_fast64_t value, bool is_default = false); + size_t set_int_unique(size_t column_ndx, size_t row_ndx, int_fast64_t value); + void set_bool(size_t column_ndx, size_t row_ndx, bool value, bool is_default = false); + void set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value, bool is_default = false); + void set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value, bool is_default = false); + template + void set_enum(size_t column_ndx, size_t row_ndx, E value); + void set_float(size_t column_ndx, size_t row_ndx, float value, bool is_default = false); + void set_double(size_t column_ndx, size_t row_ndx, double value, bool is_default = false); + void set_string(size_t column_ndx, size_t row_ndx, StringData value, bool is_default = false); + size_t set_string_unique(size_t column_ndx, size_t row_ndx, StringData value); + void set_binary(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false); + void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value, bool is_default = false); + void set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx, bool is_default = false); + void nullify_link(size_t column_ndx, size_t row_ndx); + void set_null(size_t column_ndx, size_t row_ndx, bool is_default = false); + void set_null_unique(size_t col_ndx, size_t row_ndx); + + // Sync needs to store blobs bigger than 16 M. This function can be used for that. Data should be read + // out again using the get_binary_at() function. Should not be used for user data as normal get_binary() + // will just return null if the data is bigger than the limit. + void set_binary_big(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false); + + void add_int(size_t column_ndx, size_t row_ndx, int_fast64_t value); + + void insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData); + void remove_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t substring_size = realm::npos); + + //@} + + /// Assumes that the specified column is a subtable column (in + /// particular, not a mixed column) and that the specified table + /// has a spec that is compatible with that column, that is, the + /// number of columns must be the same, and corresponding columns + /// must have identical data types (as returned by + /// get_column_type()). + void set_subtable(size_t col_ndx, size_t row_ndx, const Table*); + void set_mixed_subtable(size_t col_ndx, size_t row_ndx, const Table*); + + + // Sub-tables (works on columns whose type is either 'subtable' or + // 'mixed', for a value in a mixed column that is not a subtable, + // get_subtable() returns null, get_subtable_size() returns zero, + // and clear_subtable() replaces the value with an empty table.) + TableRef get_subtable(size_t column_ndx, size_t row_ndx); + ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const; + size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept; + void clear_subtable(size_t column_ndx, size_t row_ndx); + + // Backlinks + size_t get_backlink_count(size_t row_ndx, const Table& origin, size_t origin_col_ndx) const noexcept; + size_t get_backlink(size_t row_ndx, const Table& origin, size_t origin_col_ndx, size_t backlink_ndx) const + noexcept; + + + //@{ + + /// If this accessor is attached to a subtable, then that subtable has a + /// parent table, and the subtable either resides in a column of type + /// `table` or of type `mixed` in that parent. In that case + /// get_parent_table() returns a reference to the accessor associated with + /// the parent, and get_parent_row_index() returns the index of the row in + /// which the subtable resides. In all other cases (free-standing and + /// group-level tables), get_parent_table() returns null and + /// get_parent_row_index() returns realm::npos. + /// + /// If this accessor is attached to a subtable, and \a column_ndx_out is + /// specified, then `*column_ndx_out` is set to the index of the column of + /// the parent table in which the subtable resides. If this accessor is not + /// attached to a subtable, then `*column_ndx_out` will retain its original + /// value upon return. + + TableRef get_parent_table(size_t* column_ndx_out = nullptr) noexcept; + ConstTableRef get_parent_table(size_t* column_ndx_out = nullptr) const noexcept; + size_t get_parent_row_index() const noexcept; + + //@} + + + /// Only group-level unordered tables can be used as origins or targets of + /// links. + bool is_group_level() const noexcept; + + /// If this table is a group-level table, then this function returns the + /// index of this table within the group. Otherwise it returns realm::npos. + size_t get_index_in_group() const noexcept; + + // Aggregate functions + size_t count_int(size_t column_ndx, int64_t value) const; + size_t count_string(size_t column_ndx, StringData value) const; + size_t count_float(size_t column_ndx, float value) const; + size_t count_double(size_t column_ndx, double value) const; + + int64_t sum_int(size_t column_ndx) const; + double sum_float(size_t column_ndx) const; + double sum_double(size_t column_ndx) const; + int64_t maximum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + float maximum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + double maximum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + OldDateTime maximum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + int64_t minimum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + float minimum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + double minimum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + OldDateTime minimum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + double average_int(size_t column_ndx, size_t* value_count = nullptr) const; + double average_float(size_t column_ndx, size_t* value_count = nullptr) const; + double average_double(size_t column_ndx, size_t* value_count = nullptr) const; + + // Searching + size_t find_first_link(size_t target_row_index) const; + size_t find_first_int(size_t column_ndx, int64_t value) const; + size_t find_first_bool(size_t column_ndx, bool value) const; + size_t find_first_olddatetime(size_t column_ndx, OldDateTime value) const; + size_t find_first_timestamp(size_t column_ndx, Timestamp value) const; + size_t find_first_float(size_t column_ndx, float value) const; + size_t find_first_double(size_t column_ndx, double value) const; + size_t find_first_string(size_t column_ndx, StringData value) const; + size_t find_first_binary(size_t column_ndx, BinaryData value) const; + size_t find_first_null(size_t column_ndx) const; + + TableView find_all_link(size_t target_row_index); + ConstTableView find_all_link(size_t target_row_index) const; + TableView find_all_int(size_t column_ndx, int64_t value); + ConstTableView find_all_int(size_t column_ndx, int64_t value) const; + TableView find_all_bool(size_t column_ndx, bool value); + ConstTableView find_all_bool(size_t column_ndx, bool value) const; + TableView find_all_olddatetime(size_t column_ndx, OldDateTime value); + ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const; + TableView find_all_float(size_t column_ndx, float value); + ConstTableView find_all_float(size_t column_ndx, float value) const; + TableView find_all_double(size_t column_ndx, double value); + ConstTableView find_all_double(size_t column_ndx, double value) const; + TableView find_all_string(size_t column_ndx, StringData value); + ConstTableView find_all_string(size_t column_ndx, StringData value) const; + TableView find_all_binary(size_t column_ndx, BinaryData value); + ConstTableView find_all_binary(size_t column_ndx, BinaryData value) const; + TableView find_all_null(size_t column_ndx); + ConstTableView find_all_null(size_t column_ndx) const; + + /// The following column types are supported: String, Integer, OldDateTime, Bool + TableView get_distinct_view(size_t column_ndx); + ConstTableView get_distinct_view(size_t column_ndx) const; + + TableView get_sorted_view(size_t column_ndx, bool ascending = true); + ConstTableView get_sorted_view(size_t column_ndx, bool ascending = true) const; + + TableView get_sorted_view(SortDescriptor order); + ConstTableView get_sorted_view(SortDescriptor order) const; + + TableView get_range_view(size_t begin, size_t end); + ConstTableView get_range_view(size_t begin, size_t end) const; + + TableView get_backlink_view(size_t row_ndx, Table* src_table, size_t src_col_ndx); + + + // Pivot / aggregate operation types. Experimental! Please do not document method publicly. + enum AggrType { + aggr_count, + aggr_sum, + aggr_avg, + aggr_min, + aggr_max, + }; + + // Simple pivot aggregate method. Experimental! Please do not document method publicly. + void aggregate(size_t group_by_column, size_t aggr_column, AggrType op, Table& result, + const IntegerColumn* viewrefs = nullptr) const; + + /// Report the current versioning counter for the table. The versioning counter is guaranteed to + /// change when the contents of the table changes after advance_read() or promote_to_write(), or + /// immediately after calls to methods which change the table. The term "change" means "change of + /// value": The storage layout of the table may change, for example due to optimization, but this + /// is not considered a change of a value. This means that you *cannot* use a non-changing version + /// count to indicate that object addresses (e.g. strings, binary data) remain the same. + /// The versioning counter *may* change (but is not required to do so) when another table linked + /// from this table, or linking to this table, is changed. The version counter *may* also change + /// without any apparent reason. + uint_fast64_t get_version_counter() const noexcept; + +private: + template + size_t find_first(size_t column_ndx, T value) const; // called by above methods + template + TableView find_all(size_t column_ndx, T value); + +public: + //@{ + /// Find the lower/upper bound according to a column that is + /// already sorted in ascending order. + /// + /// For an integer column at index 0, and an integer value '`v`', + /// lower_bound_int(0,v) returns the index '`l`' of the first row + /// such that `get_int(0,l) ≥ v`, and upper_bound_int(0,v) + /// returns the index '`u`' of the first row such that + /// `get_int(0,u) > v`. In both cases, if no such row is found, + /// the returned value is the number of rows in the table. + /// + /// 3 3 3 4 4 4 5 6 7 9 9 9 + /// ^ ^ ^ ^ ^ + /// | | | | | + /// | | | | -- Lower and upper bound of 15 + /// | | | | + /// | | | -- Lower and upper bound of 8 + /// | | | + /// | | -- Upper bound of 4 + /// | | + /// | -- Lower bound of 4 + /// | + /// -- Lower and upper bound of 1 + /// + /// These functions are similar to std::lower_bound() and + /// std::upper_bound(). + /// + /// The string versions assume that the column is sorted according + /// to StringData::operator<(). + size_t lower_bound_int(size_t column_ndx, int64_t value) const noexcept; + size_t upper_bound_int(size_t column_ndx, int64_t value) const noexcept; + size_t lower_bound_bool(size_t column_ndx, bool value) const noexcept; + size_t upper_bound_bool(size_t column_ndx, bool value) const noexcept; + size_t lower_bound_float(size_t column_ndx, float value) const noexcept; + size_t upper_bound_float(size_t column_ndx, float value) const noexcept; + size_t lower_bound_double(size_t column_ndx, double value) const noexcept; + size_t upper_bound_double(size_t column_ndx, double value) const noexcept; + size_t lower_bound_string(size_t column_ndx, StringData value) const noexcept; + size_t upper_bound_string(size_t column_ndx, StringData value) const noexcept; + //@} + + // Queries + // Using where(tv) is the new method to perform queries on TableView. The 'tv' can have any order; it does not + // need to be sorted, and, resulting view retains its order. + Query where(TableViewBase* tv = nullptr) + { + return Query(*this, tv); + } + + // FIXME: We need a ConstQuery class or runtime check against modifications in read transaction. + Query where(TableViewBase* tv = nullptr) const + { + return Query(*this, tv); + } + + // Perform queries on a LinkView. The returned Query holds a reference to lv. + Query where(const LinkViewRef& lv) + { + return Query(*this, lv); + } + + Table& link(size_t link_column); + Table& backlink(const Table& origin, size_t origin_col_ndx); + + // Optimizing. enforce == true will enforce enumeration of all string columns; + // enforce == false will auto-evaluate if they should be enumerated or not + void optimize(bool enforce = false); + + /// Write this table (or a slice of this table) to the specified + /// output stream. + /// + /// The output will have the same format as any other Realm + /// database file, such as those produced by Group::write(). In + /// this case, however, the resulting database file will contain + /// exactly one table, and that table will contain only the + /// specified slice of the source table (this table). + /// + /// The new table will always have the same dynamic type (see + /// Descriptor) as the source table (this table), and unless it is + /// overridden (\a override_table_name), the new table will have + /// the same name as the source table (see get_name()). Indexes + /// (see add_search_index()) will not be carried over to the new + /// table. + /// + /// \param out The destination output stream buffer. + /// + /// \param offset Index of first row to include (if `slice_size > + /// 0`). Must be less than, or equal to size(). + /// + /// \param slice_size Number of rows to include. May be zero. If + /// `slice_size > size() - offset`, then the effective size of + /// the written slice will be `size() - offset`. + /// + /// \param override_table_name Custom name to write out instead of + /// the actual table name. + /// + /// \throw std::out_of_range If `offset > size()`. + /// + /// FIXME: While this function does provided a maximally efficient + /// way of serializing part of a table, it offers little in terms + /// of general utility. This is unfortunate, because it pulls + /// quite a large amount of code into the core library to support + /// it. + void write(std::ostream& out, size_t offset = 0, size_t slice_size = npos, + StringData override_table_name = StringData()) const; + + // Conversion + void to_json(std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const; + void to_string(std::ostream& out, size_t limit = 500) const; + void row_to_string(size_t row_ndx, std::ostream& out) const; + + // Get a reference to this table + TableRef get_table_ref() + { + return TableRef(this); + } + ConstTableRef get_table_ref() const + { + return ConstTableRef(this); + } + + /// \brief Compare two tables for equality. + /// + /// Two tables are equal if they have equal descriptors + /// (`Descriptor::operator==()`) and equal contents. Equal descriptors imply + /// that the two tables have the same columns in the same order. Equal + /// contents means that the two tables must have the same number of rows, + /// and that for each row index, the two rows must have the same values in + /// each column. + /// + /// In mixed columns, both the value types and the values are required to be + /// equal. + /// + /// For a particular row and column, if the two values are themselves tables + /// (subtable and mixed columns) value equality implies a recursive + /// invocation of `Table::operator==()`. + bool operator==(const Table&) const; + + /// \brief Compare two tables for inequality. + /// + /// See operator==(). + bool operator!=(const Table& t) const; + + /// A subtable in a column of type 'table' (which shares descriptor with + /// other subtables in the same column) is initially in a degenerate state + /// where it takes up a minimal amout of space. This function returns true + /// if, and only if the table accessor is attached to such a subtable. This + /// function is mainly intended for debugging purposes. + bool is_degenerate() const noexcept; + + // Debug + void verify() const; +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; + void print() const; + MemStats stats() const; + void dump_node_structure() const; // To std::cerr (for GDB) + void dump_node_structure(std::ostream&, int level) const; +#endif + + class Parent; + using HandoverPatch = TableHandoverPatch; + static void generate_patch(const Table* ref, std::unique_ptr& patch); + static TableRef create_from_and_consume_patch(std::unique_ptr& patch, Group& group); + +protected: + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx); + + /// See non-const get_subtable_ptr(). + const Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx) const; + + /// Compare the rows of two tables under the assumption that the two tables + /// have the same number of columns, and the same data type at each column + /// index (as expressed through the DataType enum). + bool compare_rows(const Table&) const; + + void set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const; + + void check_lists_are_empty(size_t row_ndx) const; + +private: + class SliceWriter; + + // Number of rows in this table + size_t m_size; + + // Underlying array structure. `m_top` is in use only for root tables; that + // is, for tables with independent descriptor. `m_columns` contains a ref + // for each column and search index in order of the columns. A search index + // ref always occurs immediately after the ref of the column to which the + // search index belongs. + // + // A subtable column (a column of type `type_table`) is essentially just a + // column of 'refs' pointing to the root node of each subtable. + // + // To save space in the database file, a subtable in such a column always + // starts out in a degenerate form where nothing is allocated on its behalf, + // and a null 'ref' is stored in the corresponding slot of the column. A + // subtable remains in this degenerate state until the first row is added to + // the subtable. + // + // For this scheme to work, it must be (and is) possible to create a table + // accessor that refers to a degenerate subtable. A table accessor (instance + // of `Table`) refers to a degenerate subtable if, and only if `m_columns` + // is unattached. + // + // FIXME: The fact that `m_columns` may be detached means that many + // functions (even non-modifying functions) need to check for that before + // accessing the contents of the table. This incurs a runtime + // overhead. Consider whether this overhead can be eliminated by having + // `Table::m_columns` always attached to something, and then detect the + // degenerate state in a different way. + Array m_top; + Array m_columns; // 2nd slot in m_top (for root tables) + Spec m_spec; // 1st slot in m_top (for root tables) + + // Is guaranteed to be empty for a detached accessor. Otherwise it is empty + // when the table accessor is attached to a degenerate subtable (unattached + // `m_columns`), otherwise it contains precisely one column accessor for + // each column in the table, in order. + // + // In some cases an entry may be null. This is currently possible only in + // connection with Group::advance_transact(), but it means that several + // member functions must be prepared to handle these null entries; in + // particular, detach(), ~Table(), functions called on behalf of detach() + // and ~Table(), and functiones called on behalf of + // Group::advance_transact(). + typedef std::vector column_accessors; + column_accessors m_cols; + + mutable std::atomic m_ref_count; + + // If this table is a root table (has independent descriptor), + // then Table::m_descriptor refers to the accessor of its + // descriptor when, and only when the descriptor accessor + // exists. This is used to ensure that at most one descriptor + // accessor exists for each underlying descriptor at any given + // point in time. Subdescriptors are kept unique by means of a + // registry in the parent descriptor. Table::m_descriptor is + // always null for tables with shared descriptor. + mutable std::weak_ptr m_descriptor; + + // Table view instances + // Access needs to be protected by m_accessor_mutex + typedef std::vector views; + mutable views m_views; + + // Points to first bound row accessor, or is null if there are none. + mutable RowBase* m_row_accessors = nullptr; + + // Mutex which must be locked any time the row accessor chain or m_views is used + mutable util::Mutex m_accessor_mutex; + + // Used for queries: Items are added with link() method during buildup of query + mutable std::vector m_link_chain; + + /// Used only in connection with Group::advance_transact() and + /// Table::refresh_accessor_tree(). + mutable bool m_mark; + + mutable uint_fast64_t m_version; + + void erase_row(size_t row_ndx, bool is_move_last_over); + void batch_erase_rows(const IntegerColumn& row_indexes, bool is_move_last_over); + void do_remove(size_t row_ndx, bool broken_reciprocal_backlinks); + void do_move_last_over(size_t row_ndx, bool broken_reciprocal_backlinks); + void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void do_merge_rows(size_t row_ndx, size_t new_row_ndx); + void do_clear(bool broken_reciprocal_backlinks); + size_t do_set_link(size_t col_ndx, size_t row_ndx, size_t target_row_ndx); + template + size_t do_find_unique(ColType& col, size_t ndx, T&& value, bool& conflict); + template + size_t do_set_unique_null(ColType& col, size_t ndx, bool& conflict); + template + size_t do_set_unique(ColType& column, size_t row_ndx, T&& value, bool& conflict); + + void upgrade_file_format(size_t target_file_format_version); + + // Upgrades OldDateTime columns to Timestamp columns + void upgrade_olddatetime(); + + /// Update the version of this table and all tables which have links to it. + /// This causes all views referring to those tables to go out of sync, so that + /// calls to sync_if_needed() will bring the view up to date by reexecuting the + /// query. + /// + /// \param bump_global chooses whether the global versioning counter must be + /// bumped first as part of the update. This is the normal mode of operation, + /// when a change is made to the table. When calling recursively (following links + /// or going to the parent table), the parameter should be set to false to correctly + /// prune traversal. + void bump_version(bool bump_global = true) const noexcept; + + /// Disable copying assignment. + /// + /// It could easily be implemented by calling assign(), but the + /// non-checking nature of the low-level dynamically typed API + /// makes it too risky to offer this feature as an + /// operator. + /// + /// FIXME: assign() has not yet been implemented, but the + /// intention is that it will copy the rows of the argument table + /// into this table after clearing the original contents, and for + /// target tables without a shared spec, it would also copy the + /// spec. For target tables with shared spec, it would be an error + /// to pass an argument table with an incompatible spec, but + /// assign() would not check for spec compatibility. This would + /// make it ideal as a basis for implementing operator=() for + /// typed tables. + Table& operator=(const Table&) = delete; + + /// Used when constructing an accessor whose lifetime is going to be managed + /// by reference counting. The lifetime of accessors of free-standing tables + /// allocated on the stack by the application is not managed by reference + /// counting, so that is a case where this tag must **not** be specified. + class ref_count_tag { + }; + + /// Create an uninitialized accessor whose lifetime is managed by reference + /// counting. + Table(ref_count_tag, Allocator&); + + void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool skip_create_column_accessors = false); + void init(ConstSubspecRef shared_spec, ArrayParent* parent_column, size_t parent_row_ndx); + + static void do_insert_column(Descriptor&, size_t col_ndx, DataType type, StringData name, + LinkTargetInfo& link_target_info, bool nullable = false); + static void do_insert_column_unless_exists(Descriptor&, size_t col_ndx, DataType type, StringData name, + LinkTargetInfo& link, bool nullable = false, + bool* was_inserted = nullptr); + static void do_erase_column(Descriptor&, size_t col_ndx); + static void do_rename_column(Descriptor&, size_t col_ndx, StringData name); + static void do_move_column(Descriptor&, size_t col_ndx_1, size_t col_ndx_2); + + struct InsertSubtableColumns; + struct EraseSubtableColumns; + struct RenameSubtableColumns; + struct MoveSubtableColumns; + + void insert_root_column(size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link_target, + bool nullable = false); + void erase_root_column(size_t col_ndx); + void move_root_column(size_t from, size_t to); + void do_insert_root_column(size_t col_ndx, ColumnType, StringData name, bool nullable = false); + void do_erase_root_column(size_t col_ndx); + void do_move_root_column(size_t from, size_t to); + void do_set_link_type(size_t col_ndx, LinkType); + void insert_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx, size_t backlink_col_ndx); + void erase_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx); + void update_link_target_tables(size_t old_col_ndx_begin, size_t new_col_ndx_begin); + void update_link_target_tables_after_column_move(size_t moved_from, size_t moved_to); + + struct SubtableUpdater { + virtual void update(const SubtableColumn&, Array& subcolumns) = 0; + virtual void update_accessor(Table&) = 0; + virtual ~SubtableUpdater() + { + } + }; + static void update_subtables(Descriptor&, SubtableUpdater*); + void update_subtables(const size_t* col_path_begin, const size_t* col_path_end, SubtableUpdater*); + + struct AccessorUpdater { + virtual void update(Table&) = 0; + virtual void update_parent(Table&) = 0; + virtual ~AccessorUpdater() + { + } + }; + void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, AccessorUpdater&); + + void create_degen_subtab_columns(); + ColumnBase* create_column_accessor(ColumnType, size_t col_ndx, size_t ndx_in_parent); + void destroy_column_accessors() noexcept; + + /// Called in the context of Group::commit() to ensure that + /// attached table accessors stay valid across a commit. Please + /// note that this works only for non-transactional commits. Table + /// accessors obtained during a transaction are always detached + /// when the transaction ends. + void update_from_parent(size_t old_baseline) noexcept; + + // Support function for conversions + void to_string_header(std::ostream& out, std::vector& widths) const; + void to_string_row(size_t row_ndx, std::ostream& out, const std::vector& widths) const; + + // recursive methods called by to_json, to follow links + void to_json(std::ostream& out, size_t link_depth, std::map& renames, + std::vector& followed) const; + void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth, + std::map& renames, std::vector& followed) const; + void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const; + + // Detach accessor from underlying table. Caller must ensure that + // a reference count exists upon return, for example by obtaining + // an extra reference count before the call. + // + // This function puts this table accessor into the detached + // state. This detaches it from the underlying structure of array + // nodes. It also recursively detaches accessors for subtables, + // and the type descriptor accessor. When this function returns, + // is_attached() will return false. + // + // This function may be called for a table accessor that is + // already in the detached state (idempotency). + // + // It is also valid to call this function for a table accessor + // that has not yet been detached, but whose underlying structure + // of arrays have changed in an unpredictable/unknown way. This + // kind of change generally happens when a modifying table + // operation fails, and also when one transaction is ended and a + // new one is started. + void detach() noexcept; + + /// Detach and remove all attached row, link list, and subtable + /// accessors. This function does not discard the descriptor accessor, if + /// any, and it does not discard column accessors either. + void discard_child_accessors() noexcept; + + void discard_row_accessors() noexcept; + + // Detach the type descriptor accessor if it exists. + void discard_desc_accessor() noexcept; + + void bind_ptr() const noexcept; + void unbind_ptr() const noexcept; + + void register_view(const TableViewBase* view); + void unregister_view(const TableViewBase* view) noexcept; + void move_registered_view(const TableViewBase* old_addr, const TableViewBase* new_addr) noexcept; + void discard_views() noexcept; + + void register_row_accessor(RowBase*) const noexcept; + void unregister_row_accessor(RowBase*) const noexcept; + void do_unregister_row_accessor(RowBase*) const noexcept; + + class UnbindGuard; + + ColumnType get_real_column_type(size_t column_ndx) const noexcept; + + /// If this table is a group-level table, the parent group is returned, + /// otherwise null is returned. + Group* get_parent_group() const noexcept; + + const ColumnBase& get_column_base(size_t column_ndx) const noexcept; + ColumnBase& get_column_base(size_t column_ndx); + + const ColumnBaseWithIndex& get_column_base_indexed(size_t ndx) const noexcept; + ColumnBaseWithIndex& get_column_base_indexed(size_t ndx); + + template + T& get_column(size_t ndx); + + template + const T& get_column(size_t ndx) const noexcept; + + IntegerColumn& get_column(size_t column_ndx); + const IntegerColumn& get_column(size_t column_ndx) const noexcept; + IntNullColumn& get_column_int_null(size_t column_ndx); + const IntNullColumn& get_column_int_null(size_t column_ndx) const noexcept; + FloatColumn& get_column_float(size_t column_ndx); + const FloatColumn& get_column_float(size_t column_ndx) const noexcept; + DoubleColumn& get_column_double(size_t column_ndx); + const DoubleColumn& get_column_double(size_t column_ndx) const noexcept; + StringColumn& get_column_string(size_t column_ndx); + const StringColumn& get_column_string(size_t column_ndx) const noexcept; + BinaryColumn& get_column_binary(size_t column_ndx); + const BinaryColumn& get_column_binary(size_t column_ndx) const noexcept; + StringEnumColumn& get_column_string_enum(size_t column_ndx); + const StringEnumColumn& get_column_string_enum(size_t column_ndx) const noexcept; + SubtableColumn& get_column_table(size_t column_ndx); + const SubtableColumn& get_column_table(size_t column_ndx) const noexcept; + MixedColumn& get_column_mixed(size_t column_ndx); + const MixedColumn& get_column_mixed(size_t column_ndx) const noexcept; + TimestampColumn& get_column_timestamp(size_t column_ndx); + const TimestampColumn& get_column_timestamp(size_t column_ndx) const noexcept; + const LinkColumnBase& get_column_link_base(size_t ndx) const noexcept; + LinkColumnBase& get_column_link_base(size_t ndx); + const LinkColumn& get_column_link(size_t ndx) const noexcept; + LinkColumn& get_column_link(size_t ndx); + const LinkListColumn& get_column_link_list(size_t ndx) const noexcept; + LinkListColumn& get_column_link_list(size_t ndx); + const BacklinkColumn& get_column_backlink(size_t ndx) const noexcept; + BacklinkColumn& get_column_backlink(size_t ndx); + + void verify_column(size_t col_ndx, const ColumnBase* col) const; + + void instantiate_before_change(); + void validate_column_type(const ColumnBase& col, ColumnType expected_type, size_t ndx) const; + + static size_t get_size_from_ref(ref_type top_ref, Allocator&) noexcept; + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&) noexcept; + + const Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) const noexcept; + Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) noexcept; + + /// Create an empty table with independent spec and return just + /// the reference to the underlying memory. + static ref_type create_empty_table(Allocator&); + + /// Create a column of the specified type, fill it with the + /// specified number of default values, and return just the + /// reference to the underlying memory. + static ref_type create_column(ColumnType column_type, size_t num_default_values, bool nullable, Allocator&); + + /// Construct a copy of the columns array of this table using the + /// specified allocator and return just the ref to that array. + /// + /// In the clone, no string column will be of the enumeration + /// type. + ref_type clone_columns(Allocator&) const; + + /// Construct a complete copy of this table (including its spec) + /// using the specified allocator and return just the ref to the + /// new top array. + ref_type clone(Allocator&) const; + + /// True for `col_type_Link` and `col_type_LinkList`. + static bool is_link_type(ColumnType) noexcept; + + void connect_opposite_link_columns(size_t link_col_ndx, Table& target_table, size_t backlink_col_ndx) noexcept; + + size_t get_num_strong_backlinks(size_t row_ndx) const noexcept; + + //@{ + + /// Cascading removal of strong links. + /// + /// cascade_break_backlinks_to() removes all backlinks pointing to the row + /// at \a row_ndx. Additionally, if this causes the number of **strong** + /// backlinks originating from a particular opposite row (target row of + /// corresponding forward link) to drop to zero, and that row is not already + /// in \a state.rows, then that row is added to \a state.rows, and + /// cascade_break_backlinks_to() is called recursively for it. This + /// operation is the first half of the cascading row removal operation. The + /// second half is performed by passing the resulting contents of \a + /// state.rows to remove_backlink_broken_rows(). + /// + /// Operations that trigger cascading row removal due to explicit removal of + /// one or more rows (the *initiating rows*), should add those rows to \a + /// rows initially, and then call cascade_break_backlinks_to() once for each + /// of them in turn. This is opposed to carrying out the explicit row + /// removals independently, which is also possible, but does require that + /// any initiating rows, that end up in \a state.rows due to link cycles, + /// are removed before passing \a state.rows to + /// remove_backlink_broken_rows(). In the case of clear(), where all rows of + /// a table are explicitly removed, it is better to use + /// cascade_break_backlinks_to_all_rows(), and then carry out the table + /// clearing as an independent step. For operations that trigger cascading + /// row removal for other reasons than explicit row removal, \a state.rows + /// must be empty initially, but cascade_break_backlinks_to() must still be + /// called for each of the initiating rows. + /// + /// When the last non-recursive invocation of cascade_break_backlinks_to() + /// returns, all forward links originating from a row in \a state.rows have + /// had their reciprocal backlinks removed, so remove_backlink_broken_rows() + /// does not perform reciprocal backlink removal at all. Additionally, all + /// remaining backlinks originating from rows in \a state.rows are + /// guaranteed to point to rows that are **not** in \a state.rows. This is + /// true because any backlink that was pointing to a row in \a state.rows + /// has been removed by one of the invocations of + /// cascade_break_backlinks_to(). The set of forward links, that correspond + /// to these remaining backlinks, is precisely the set of forward links that + /// need to be removed/nullified by remove_backlink_broken_rows(), which it + /// does by way of reciprocal forward link removal. Note also, that while + /// all the rows in \a state.rows can have remaining **weak** backlinks + /// originating from them, only the initiating rows in \a state.rows can + /// have remaining **strong** backlinks originating from them. This is true + /// because a non-initiating row is added to \a state.rows only when the + /// last backlink originating from it is lost. + /// + /// Each row removal is replicated individually (as opposed to one + /// replication instruction for the entire cascading operation). This is + /// done because it provides an easy way for Group::advance_transact() to + /// know which tables are affected by the cascade. Note that this has + /// several important consequences: First of all, the replication log + /// receiver must execute the row removal instructions in a non-cascading + /// fashion, meaning that there will be an asymmetry between the two sides + /// in how the effect of the cascade is brought about. While this is fine + /// for simple 1-to-1 replication, it may end up interfering badly with + /// *transaction merging*, when that feature is introduced. Imagine for + /// example that the cascade initiating operation gets canceled during + /// conflict resolution, but some, or all of the induced row removals get to + /// stay. That would break causal consistency. It is important, however, for + /// transaction merging that the cascaded row removals are explicitly + /// mentioned in the replication log, such that they can be used to adjust + /// row indexes during the *operational transform*. + /// + /// cascade_break_backlinks_to_all_rows() has the same affect as calling + /// cascade_break_backlinks_to() once for each row in the table. When + /// calling this function, \a state.stop_on_table must be set to the origin + /// table (origin table of corresponding forward links), and \a + /// state.stop_on_link_list_column must be null. + /// + /// It is immaterial which table remove_backlink_broken_rows() is called on, + /// as long it that table is in the same group as the removed rows. + + void cascade_break_backlinks_to(size_t row_ndx, CascadeState& state); + void cascade_break_backlinks_to_all_rows(CascadeState& state); + void remove_backlink_broken_rows(const CascadeState&); + + //@} + + /// Used by query. Follows chain of link columns and returns final target table + const Table* get_link_chain_target(const std::vector& link_chain) const; + + /// Remove the specified row by the 'move last over' method. + void do_move_last_over(size_t row_ndx); + + // Precondition: 1 <= end - begin + size_t* record_subtable_path(size_t* begin, size_t* end) const noexcept; + + /// Check if an accessor exists for the specified subtable. If it does, + /// return a pointer to it, otherwise return null. This function assumes + /// that the specified column index in a valid index into `m_cols` but does + /// not otherwise assume more than minimal accessor consistency (see + /// AccessorConsistencyLevels.) + Table* get_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; + + /// Unless the column accessor is missing, this function returns the + /// accessor for the target table of the specified link-type column. The + /// column accessor is said to be missing if `m_cols[col_ndx]` is null, and + /// this can happen only during certain operations such as the updating of + /// the accessor tree when a read transaction is advanced. Note that for + /// link type columns, the target table accessor exists when, and only when + /// the origin table accessor exists. This function assumes that the + /// specified column index in a valid index into `m_cols` and that the + /// column is a link-type column. Beyond that, it assume nothing more than + /// minimal accessor consistency (see AccessorConsistencyLevels.) + Table* get_link_target_table_accessor(size_t col_ndx) noexcept; + + void discard_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; + + void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + void adj_acc_erase_row(size_t row_ndx) noexcept; + void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + + /// Adjust this table accessor and its subordinates after move_last_over() + /// (or its inverse). + /// + /// First, any row, subtable, or link list accessors registered as being at + /// \a to_row_ndx will be detached, as that row is assumed to have been + /// replaced. Next, any row, subtable, or link list accessors registered as + /// being at \a from_row_ndx, will be reregistered as being at \a + /// to_row_ndx, as the row at \a from_row_ndx is assumed to have been moved + /// to \a to_row_ndx. + /// + /// Crucially, if \a to_row_ndx is equal to \a from_row_ndx, then row, + /// subtable, or link list accessors at that row are **still detached**. + /// + /// Additionally, this function causes all link-adjacent tables to be marked + /// (dirty). Two tables are link-adjacent if one is the target table of a + /// link column of the other table. Note that this marking follows these + /// relations in both directions, but only to a depth of one. + /// + /// When this function is used in connection with move_last_over(), set \a + /// to_row_ndx to the index of the row to be removed, and set \a + /// from_row_ndx to the index of the last row in the table. As mentioned + /// earlier, this function can also be used in connection with the **inverse + /// of** move_last_over(), which is an operation that vacates a row by + /// moving its contents into a new last row of the table. In that case, set + /// \a to_row_ndx to one plus the index of the last row in the table, and + /// set \a from_row_ndx to the index of the row to be vacated. + /// + /// This function is used as part of Table::refresh_accessor_tree() to + /// promote the state of the accessors from Minimal Consistency into + /// Structural Correspondence, so it must be able to execute without + /// accessing the underlying array nodes. + void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + void adj_acc_clear_root_table() noexcept; + void adj_acc_clear_nonroot_table() noexcept; + void adj_row_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + void adj_row_acc_erase_row(size_t row_ndx) noexcept; + void adj_row_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_row_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + + /// Called by adj_acc_move_over() to adjust row accessors. + void adj_row_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + void adj_insert_column(size_t col_ndx); + void adj_erase_column(size_t col_ndx) noexcept; + void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; + + bool is_marked() const noexcept; + void mark() noexcept; + void unmark() noexcept; + void recursive_mark() noexcept; + void mark_link_target_tables(size_t col_ndx_begin) noexcept; + void mark_opposite_link_tables() noexcept; + + Replication* get_repl() noexcept; + + void set_ndx_in_parent(size_t ndx_in_parent) noexcept; + + /// Refresh the part of the accessor tree that is rooted at this + /// table. Subtable accessors will be refreshed only if they are marked + /// (Table::m_mark), and this applies recursively to subtables of + /// subtables. All refreshed table accessors (including this one) will be + /// unmarked upon return. + /// + /// The following conditions are necessary and sufficient for the proper + /// operation of this function: + /// + /// - This table must be a group-level table, or a subtable. It must not be + /// a free-standing table (because a free-standing table has no parent). + /// + /// - The `index in parent` property is correct. The `index in parent` + /// property of the table is the `index in parent` property of + /// `m_columns` for subtables with shared descriptor, and the `index in + /// parent` property of `m_top` for all other tables. + /// + /// - If this table has shared descriptor, then the `index in parent` + /// property of the contained spec accessor is correct. + /// + /// - The parent accessor is in a valid state (already refreshed). If the + /// parent is a group, then the group accessor (excluding its table + /// accessors) must be in a valid state. If the parent is a table, then + /// the table accessor (excluding its subtable accessors) must be in a + /// valid state. + /// + /// - Every descendant subtable accessor is marked if it needs to be + /// refreshed, or if it has a descendant accessor that needs to be + /// refreshed. + /// + /// - This table accessor, as well as all its descendant accessors, are in + /// structural correspondence with the underlying node hierarchy whose + /// root ref is stored in the parent (see AccessorConsistencyLevels). + void refresh_accessor_tree(); + + void refresh_column_accessors(size_t col_ndx_begin = 0); + + // Look for link columns starting from col_ndx_begin. + // If a link column is found, follow the link and update it's + // backlink column accessor if it is in different table. + void refresh_link_target_accessors(size_t col_ndx_begin = 0); + + bool is_cross_table_link_target() const noexcept; + +#ifdef REALM_DEBUG + void to_dot_internal(std::ostream&) const; +#endif + + friend class SubtableNode; + friend class _impl::TableFriend; + friend class Query; + template + friend class util::bind_ptr; + template + friend class SimpleQuerySupport; + friend class LangBindHelper; + friend class TableViewBase; + template + friend class Columns; + friend class Columns; + friend class ParentNode; + template + friend class SequentialGetter; + friend class RowBase; + friend class LinksToNode; + friend class LinkMap; + friend class LinkView; + friend class Group; +}; + +class Table::Parent : public ArrayParent { +public: + ~Parent() noexcept override + { + } + +protected: + virtual StringData get_child_name(size_t child_ndx) const noexcept; + + /// If children are group-level tables, then this function returns the + /// group. Otherwise it returns null. + virtual Group* get_parent_group() noexcept; + + /// If children are subtables, then this function returns the + /// parent table. Otherwise it returns null. + /// + /// If \a column_ndx_out is not null, this function must assign the index of + /// the column within the parent table to `*column_ndx_out` when , and only + /// when this table parent is a column in a parent table. + virtual Table* get_parent_table(size_t* column_ndx_out = nullptr) noexcept; + + /// Must be called whenever a child table accessor is about to be destroyed. + /// + /// Note that the argument is a pointer to the child Table rather than its + /// `ndx_in_parent` property. This is because only minimal accessor + /// consistency can be assumed by this function. + virtual void child_accessor_destroyed(Table* child) noexcept = 0; + + virtual size_t* record_subtable_path(size_t* begin, size_t* end) noexcept; + + friend class Table; +}; + + +// Implementation: + + +inline uint_fast64_t Table::get_version_counter() const noexcept +{ + return m_version; +} + +inline void Table::bump_version(bool bump_global) const noexcept +{ + if (bump_global) { + // This is only set on initial entry through an operation on the same + // table. recursive calls (via parent or via backlinks) must be done + // with bump_global=false. + m_top.get_alloc().bump_global_version(); + } + if (m_top.get_alloc().should_propagate_version(m_version)) { + if (const Table* parent = get_parent_table_ptr()) + parent->bump_version(false); + // Recurse through linked tables, use m_mark to avoid infinite recursion + for (auto& column_ptr : m_cols) { + // We may meet a null pointer in place of a backlink column, pending + // replacement with a new one. This can happen ONLY when creation of + // the corresponding forward link column in the origin table is + // pending as well. In this case it is ok to just ignore the zeroed + // backlink column, because the origin table is guaranteed to also + // be refreshed/marked dirty and hence have it's version bumped. + if (column_ptr != nullptr) + column_ptr->bump_link_origin_table_version(); + } + } +} + +inline void Table::remove(size_t row_ndx) +{ + bool is_move_last_over = false; + erase_row(row_ndx, is_move_last_over); // Throws +} + +inline void Table::move_last_over(size_t row_ndx) +{ + bool is_move_last_over = true; + erase_row(row_ndx, is_move_last_over); // Throws +} + +inline void Table::remove_last() +{ + if (!is_empty()) + remove(size() - 1); +} + +// A good place to start if you want to understand the memory ordering +// chosen for the operations below is http://preshing.com/20130922/acquire-and-release-fences/ +inline void Table::bind_ptr() const noexcept +{ + m_ref_count.fetch_add(1, std::memory_order_relaxed); +} + +inline void Table::unbind_ptr() const noexcept +{ + // The delete operation runs the destructor, and the destructor + // must always see all changes to the object being deleted. + // Within each thread, we know that unbind_ptr will always happen after + // any changes, so it is a convenient place to do a release. + // The release will then be observed by the acquire fence in + // the case where delete is actually called (the count reaches 0) + if (m_ref_count.fetch_sub(1, std::memory_order_release) != 1) + return; + + std::atomic_thread_fence(std::memory_order_acquire); + delete this; +} + +inline void Table::register_view(const TableViewBase* view) +{ + util::LockGuard lock(m_accessor_mutex); + // Casting away constness here - operations done on tableviews + // through m_views are all internal and preserving "some" kind + // of logical constness. + m_views.push_back(const_cast(view)); +} + +inline bool Table::is_attached() const noexcept +{ + // Note that it is not possible to tie the state of attachment of a table to + // the state of attachment of m_top, because tables with shared spec do not + // have a 'top' array. Neither is it possible to tie it to the state of + // attachment of m_columns, because subtables with shared spec start out in + // a degenerate form where they do not have a 'columns' array. For these + // reasons, it is neccessary to define the notion of attachment for a table + // as follows: A table is attached if, and ony if m_column stores a non-null + // parent pointer. This works because even for degenerate subtables, + // m_columns is initialized with the correct parent pointer. + return m_columns.has_parent(); +} + +inline StringData Table::get_name() const noexcept +{ + REALM_ASSERT(is_attached()); + const Array& real_top = m_top.is_attached() ? m_top : m_columns; + ArrayParent* parent = real_top.get_parent(); + if (!parent) + return StringData(""); + size_t index_in_parent = real_top.get_ndx_in_parent(); + REALM_ASSERT(dynamic_cast(parent)); + return static_cast(parent)->get_child_name(index_in_parent); +} + +inline size_t Table::get_column_count() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec.get_public_column_count(); +} + +inline StringData Table::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, get_column_count()); + return m_spec.get_column_name(ndx); +} + +inline size_t Table::get_column_index(StringData name) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec.get_column_index(name); +} + +inline ColumnType Table::get_real_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); + return m_spec.get_column_type(ndx); +} + +inline DataType Table::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); + return m_spec.get_public_column_type(ndx); +} + +template +inline Col& Table::get_column(size_t ndx) +{ + ColumnBase& col = get_column_base(ndx); +#ifdef REALM_DEBUG + validate_column_type(col, col_type, ndx); +#endif + REALM_ASSERT(typeid(Col) == typeid(col)); + return static_cast(col); +} + +template +inline const Col& Table::get_column(size_t ndx) const noexcept +{ + const ColumnBase& col = get_column_base(ndx); +#ifdef REALM_DEBUG + validate_column_type(col, col_type, ndx); +#endif + REALM_ASSERT(typeid(Col) == typeid(col)); + return static_cast(col); +} + +inline bool Table::has_shared_type() const noexcept +{ + REALM_ASSERT(is_attached()); + return !m_top.is_attached(); +} + +inline void Table::verify_column(size_t col_ndx, const ColumnBase* col) const +{ + // Check if the column exists at the expected location + if (REALM_LIKELY(col_ndx < m_cols.size() && m_cols[col_ndx] == col)) + return; + // The column might be elsewhere in the list + for (auto c : m_cols) { + if (c == col) + return; + } + throw LogicError(LogicError::column_does_not_exist); +} + +class Table::UnbindGuard { +public: + UnbindGuard(Table* table) noexcept + : m_table(table) + { + } + + ~UnbindGuard() noexcept + { + if (m_table) + m_table->unbind_ptr(); + } + + Table& operator*() const noexcept + { + return *m_table; + } + + Table* operator->() const noexcept + { + return m_table; + } + + Table* get() const noexcept + { + return m_table; + } + + Table* release() noexcept + { + Table* table = m_table; + m_table = nullptr; + return table; + } + +private: + Table* m_table; +}; + + +inline Table::Table(Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 1; // Explicitely managed lifetime + + ref_type ref = create_empty_table(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + init(ref, parent, ndx_in_parent); +} + +inline Table::Table(const Table& t, Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 1; // Explicitely managed lifetime + + ref_type ref = t.clone(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + init(ref, parent, ndx_in_parent); +} + +inline Table::Table(ref_count_tag, Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 0; // Lifetime managed by reference counting +} + +inline Allocator& Table::get_alloc() const +{ + return m_top.get_alloc(); +} + +inline TableRef Table::create(Allocator& alloc) +{ + std::unique_ptr
table(new Table(ref_count_tag(), alloc)); // Throws + ref_type ref = create_empty_table(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + table->init(ref, parent, ndx_in_parent); // Throws + return table.release()->get_table_ref(); +} + +inline TableRef Table::copy(Allocator& alloc) const +{ + std::unique_ptr
table(new Table(ref_count_tag(), alloc)); // Throws + ref_type ref = clone(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + table->init(ref, parent, ndx_in_parent); // Throws + return table.release()->get_table_ref(); +} + +// For use by queries +template +inline Columns Table::column(size_t column_ndx) +{ + std::vector link_chain = std::move(m_link_chain); + m_link_chain.clear(); + + // Check if user-given template type equals Realm type. Todo, we should clean up and reuse all our + // type traits (all the is_same() cases below). + const Table* table = get_link_chain_target(link_chain); + + realm::DataType ct = table->get_column_type(column_ndx); + if (std::is_same::value && ct != type_Int) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Bool) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_OldDateTime) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Float) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Double) + throw(LogicError::type_mismatch); + + if (std::is_same::value || std::is_same::value || std::is_same::value) { + link_chain.push_back(column_ndx); + } + + return Columns(column_ndx, this, std::move(link_chain)); +} + +template +inline Columns Table::column(const Table& origin, size_t origin_col_ndx) +{ + static_assert(std::is_same::value, ""); + + size_t origin_table_ndx = origin.get_index_in_group(); + const Table& current_target_table = *get_link_chain_target(m_link_chain); + size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + + std::vector link_chain = std::move(m_link_chain); + m_link_chain.clear(); + link_chain.push_back(backlink_col_ndx); + + return Columns(backlink_col_ndx, this, std::move(link_chain)); +} + +template +SubQuery Table::column(size_t column_ndx, Query subquery) +{ + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(column_ndx), std::move(subquery)); +} + +template +SubQuery Table::column(const Table& origin, size_t origin_col_ndx, Query subquery) +{ + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(origin, origin_col_ndx), std::move(subquery)); +} + +// For use by queries +inline Table& Table::link(size_t link_column) +{ + m_link_chain.push_back(link_column); + return *this; +} + +inline Table& Table::backlink(const Table& origin, size_t origin_col_ndx) +{ + size_t origin_table_ndx = origin.get_index_in_group(); + const Table& current_target_table = *get_link_chain_target(m_link_chain); + size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + return link(backlink_col_ndx); +} + +inline bool Table::is_empty() const noexcept +{ + return m_size == 0; +} + +inline size_t Table::size() const noexcept +{ + return m_size; +} + +inline Table::RowExpr Table::get(size_t row_ndx) noexcept +{ + REALM_ASSERT_3(row_ndx, <, size()); + return RowExpr(this, row_ndx); +} + +inline Table::ConstRowExpr Table::get(size_t row_ndx) const noexcept +{ + REALM_ASSERT_3(row_ndx, <, size()); + return ConstRowExpr(this, row_ndx); +} + +inline Table::RowExpr Table::front() noexcept +{ + return get(0); +} + +inline Table::ConstRowExpr Table::front() const noexcept +{ + return get(0); +} + +inline Table::RowExpr Table::back() noexcept +{ + return get(m_size - 1); +} + +inline Table::ConstRowExpr Table::back() const noexcept +{ + return get(m_size - 1); +} + +inline Table::RowExpr Table::operator[](size_t row_ndx) noexcept +{ + return get(row_ndx); +} + +inline Table::ConstRowExpr Table::operator[](size_t row_ndx) const noexcept +{ + return get(row_ndx); +} + +inline size_t Table::add_empty_row(size_t num_rows) +{ + size_t row_ndx = m_size; + insert_empty_row(row_ndx, num_rows); // Throws + return row_ndx; // Return index of first new row +} + +inline const Table* Table::get_subtable_ptr(size_t col_ndx, size_t row_ndx) const +{ + return const_cast(this)->get_subtable_ptr(col_ndx, row_ndx); // Throws +} + +inline bool Table::is_null_link(size_t col_ndx, size_t row_ndx) const noexcept +{ + return get_link(col_ndx, row_ndx) == realm::npos; +} + +inline ConstTableRef Table::get_link_target(size_t col_ndx) const noexcept +{ + return const_cast(this)->get_link_target(col_ndx); +} + +template +inline void Table::set_enum(size_t column_ndx, size_t row_ndx, E value) +{ + set_int(column_ndx, row_ndx, value); +} + +inline void Table::nullify_link(size_t col_ndx, size_t row_ndx) +{ + set_link(col_ndx, row_ndx, realm::npos); +} + +inline TableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) +{ + return TableRef(get_subtable_ptr(column_ndx, row_ndx)); +} + +inline ConstTableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) const +{ + return ConstTableRef(get_subtable_ptr(column_ndx, row_ndx)); +} + +inline ConstTableRef Table::get_parent_table(size_t* column_ndx_out) const noexcept +{ + return ConstTableRef(get_parent_table_ptr(column_ndx_out)); +} + +inline TableRef Table::get_parent_table(size_t* column_ndx_out) noexcept +{ + return TableRef(get_parent_table_ptr(column_ndx_out)); +} + +inline bool Table::is_group_level() const noexcept +{ + return bool(get_parent_group()); +} + +inline bool Table::operator==(const Table& t) const +{ + return m_spec == t.m_spec && compare_rows(t); // Throws +} + +inline bool Table::operator!=(const Table& t) const +{ + return !(*this == t); // Throws +} + +inline bool Table::is_degenerate() const noexcept +{ + return !m_columns.is_attached(); +} + +inline void Table::set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const +{ + parent->set_mixed_subtable(col_ndx, row_ndx, this); +} + +inline size_t Table::get_size_from_ref(ref_type top_ref, Allocator& alloc) noexcept +{ + const char* top_header = alloc.translate(top_ref); + std::pair p = Array::get_two(top_header, 0); + ref_type spec_ref = to_ref(p.first), columns_ref = to_ref(p.second); + return get_size_from_ref(spec_ref, columns_ref, alloc); +} + +inline Table* Table::get_parent_table_ptr(size_t* column_ndx_out) noexcept +{ + const Table* parent = const_cast(this)->get_parent_table_ptr(column_ndx_out); + return const_cast(parent); +} + +inline bool Table::is_link_type(ColumnType col_type) noexcept +{ + return col_type == col_type_Link || col_type == col_type_LinkList; +} + +inline size_t* Table::record_subtable_path(size_t* begin, size_t* end) const noexcept +{ + const Array& real_top = m_top.is_attached() ? m_top : m_columns; + size_t index_in_parent = real_top.get_ndx_in_parent(); + REALM_ASSERT_3(begin, <, end); + *begin++ = index_in_parent; + ArrayParent* parent = real_top.get_parent(); + REALM_ASSERT(parent); + REALM_ASSERT(dynamic_cast(parent)); + return static_cast(parent)->record_subtable_path(begin, end); +} + +inline size_t* Table::Parent::record_subtable_path(size_t* begin, size_t*) noexcept +{ + return begin; +} + +inline bool Table::is_marked() const noexcept +{ + return m_mark; +} + +inline void Table::mark() noexcept +{ + m_mark = true; +} + +inline void Table::unmark() noexcept +{ + m_mark = false; +} + +inline Replication* Table::get_repl() noexcept +{ + return m_top.get_alloc().get_replication(); +} + +inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + if (m_top.is_attached()) { + // Root table (independent descriptor) + m_top.set_ndx_in_parent(ndx_in_parent); + } + else { + // Subtable with shared descriptor + m_columns.set_ndx_in_parent(ndx_in_parent); + } +} + +// This class groups together information about the target of a link column +// This is not a valid link if the target table == nullptr +struct LinkTargetInfo { + LinkTargetInfo(Table* target = nullptr, size_t backlink_ndx = realm::npos) + : m_target_table(target) + , m_backlink_col_ndx(backlink_ndx) + { + } + bool is_valid() const + { + return (m_target_table != nullptr); + } + Table* m_target_table; + size_t m_backlink_col_ndx; // a value of npos indicates the backlink should be appended +}; + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Table class. +class _impl::TableFriend { +public: + typedef Table::UnbindGuard UnbindGuard; + + static ref_type create_empty_table(Allocator& alloc) + { + return Table::create_empty_table(alloc); // Throws + } + + static ref_type clone(const Table& table, Allocator& alloc) + { + return table.clone(alloc); // Throws + } + + static ref_type clone_columns(const Table& table, Allocator& alloc) + { + return table.clone_columns(alloc); // Throws + } + + static Table* create_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent, size_t ndx_in_parent) + { + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + table->init(top_ref, parent, ndx_in_parent); // Throws + return table.release(); + } + + static Table* create_accessor(ConstSubspecRef shared_spec, Table::Parent* parent_column, size_t parent_row_ndx) + { + Allocator& alloc = shared_spec.get_alloc(); + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + table->init(shared_spec, parent_column, parent_row_ndx); // Throws + return table.release(); + } + + // Intended to be used only by Group::create_table_accessor() + static Table* create_incomplete_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent, + size_t ndx_in_parent) + { + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + bool skip_create_column_accessors = true; + table->init(top_ref, parent, ndx_in_parent, skip_create_column_accessors); // Throws + return table.release(); + } + + // Intended to be used only by Group::create_table_accessor() + static void complete_accessor(Table& table) + { + table.refresh_column_accessors(); // Throws + } + + static void set_top_parent(Table& table, ArrayParent* parent, size_t ndx_in_parent) noexcept + { + table.m_top.set_parent(parent, ndx_in_parent); + } + + static void update_from_parent(Table& table, size_t old_baseline) noexcept + { + table.update_from_parent(old_baseline); + } + + static void detach(Table& table) noexcept + { + table.detach(); + } + + static void discard_row_accessors(Table& table) noexcept + { + table.discard_row_accessors(); + } + + static void discard_child_accessors(Table& table) noexcept + { + table.discard_child_accessors(); + } + + static void discard_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept + { + table.discard_subtable_accessor(col_ndx, row_ndx); + } + + static void bind_ptr(Table& table) noexcept + { + table.bind_ptr(); + } + + static void unbind_ptr(Table& table) noexcept + { + table.unbind_ptr(); + } + + static bool compare_rows(const Table& a, const Table& b) + { + return a.compare_rows(b); // Throws + } + + static size_t get_size_from_ref(ref_type ref, Allocator& alloc) noexcept + { + return Table::get_size_from_ref(ref, alloc); + } + + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator& alloc) noexcept + { + return Table::get_size_from_ref(spec_ref, columns_ref, alloc); + } + + static Spec& get_spec(Table& table) noexcept + { + return table.m_spec; + } + + static const Spec& get_spec(const Table& table) noexcept + { + return table.m_spec; + } + + static ColumnBase& get_column(const Table& table, size_t col_ndx) + { + return *table.m_cols[col_ndx]; + } + + static void do_remove(Table& table, size_t row_ndx) + { + bool broken_reciprocal_backlinks = false; + table.do_remove(row_ndx, broken_reciprocal_backlinks); // Throws + } + + static void do_move_last_over(Table& table, size_t row_ndx) + { + bool broken_reciprocal_backlinks = false; + table.do_move_last_over(row_ndx, broken_reciprocal_backlinks); // Throws + } + + static void do_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) + { + table.do_swap_rows(row_ndx_1, row_ndx_2); // Throws + } + + static void do_merge_rows(Table& table, size_t row_ndx, size_t new_row_ndx) + { + table.do_merge_rows(row_ndx, new_row_ndx); // Throws + } + + static void do_clear(Table& table) + { + bool broken_reciprocal_backlinks = false; + table.do_clear(broken_reciprocal_backlinks); // Throws + } + + static void do_set_link(Table& table, size_t col_ndx, size_t row_ndx, size_t target_row_ndx) + { + table.do_set_link(col_ndx, row_ndx, target_row_ndx); // Throws + } + + static size_t get_num_strong_backlinks(const Table& table, size_t row_ndx) noexcept + { + return table.get_num_strong_backlinks(row_ndx); + } + + static void cascade_break_backlinks_to(Table& table, size_t row_ndx, CascadeState& state) + { + table.cascade_break_backlinks_to(row_ndx, state); // Throws + } + + static void remove_backlink_broken_rows(Table& table, const CascadeState& rows) + { + table.remove_backlink_broken_rows(rows); // Throws + } + + static size_t* record_subtable_path(const Table& table, size_t* begin, size_t* end) noexcept + { + return table.record_subtable_path(begin, end); + } + + static void insert_column(Descriptor& desc, size_t column_ndx, DataType type, StringData name, + LinkTargetInfo& link, bool nullable = false) + { + Table::do_insert_column(desc, column_ndx, type, name, link, nullable); // Throws + } + + static void insert_column_unless_exists(Descriptor& desc, size_t column_ndx, DataType type, StringData name, + LinkTargetInfo link, bool nullable = false, bool* was_inserted = nullptr) + { + Table::do_insert_column_unless_exists(desc, column_ndx, type, name, link, nullable, was_inserted); // Throws + } + + static void erase_column(Descriptor& desc, size_t column_ndx) + { + Table::do_erase_column(desc, column_ndx); // Throws + } + + static void rename_column(Descriptor& desc, size_t column_ndx, StringData name) + { + Table::do_rename_column(desc, column_ndx, name); // Throws + } + + static void move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) + { + Table::do_move_column(desc, col_ndx_1, col_ndx_2); // Throws + } + + static void set_link_type(Table& table, size_t column_ndx, LinkType link_type) + { + table.do_set_link_type(column_ndx, link_type); // Throws + } + + static void erase_row(Table& table, size_t row_ndx, bool is_move_last_over) + { + table.erase_row(row_ndx, is_move_last_over); // Throws + } + + static void batch_erase_rows(Table& table, const IntegerColumn& row_indexes, bool is_move_last_over) + { + table.batch_erase_rows(row_indexes, is_move_last_over); // Throws + } + + static Table* get_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept + { + return table.get_subtable_accessor(col_ndx, row_ndx); + } + + static const Table* get_link_target_table_accessor(const Table& table, size_t col_ndx) noexcept + { + return const_cast(table).get_link_target_table_accessor(col_ndx); + } + + static Table* get_link_target_table_accessor(Table& table, size_t col_ndx) noexcept + { + return table.get_link_target_table_accessor(col_ndx); + } + + static void adj_acc_insert_rows(Table& table, size_t row_ndx, size_t num_rows) noexcept + { + table.adj_acc_insert_rows(row_ndx, num_rows); + } + + static void adj_acc_erase_row(Table& table, size_t row_ndx) noexcept + { + table.adj_acc_erase_row(row_ndx); + } + + static void adj_acc_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept + { + table.adj_acc_swap_rows(row_ndx_1, row_ndx_2); + } + + static void adj_acc_merge_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept + { + table.adj_acc_merge_rows(row_ndx_1, row_ndx_2); + } + + static void adj_acc_move_over(Table& table, size_t from_row_ndx, size_t to_row_ndx) noexcept + { + table.adj_acc_move_over(from_row_ndx, to_row_ndx); + } + + static void adj_acc_clear_root_table(Table& table) noexcept + { + table.adj_acc_clear_root_table(); + } + + static void adj_acc_clear_nonroot_table(Table& table) noexcept + { + table.adj_acc_clear_nonroot_table(); + } + + static void adj_insert_column(Table& table, size_t col_ndx) + { + table.adj_insert_column(col_ndx); // Throws + } + + static void adj_add_column(Table& table) + { + size_t num_cols = table.m_cols.size(); + table.adj_insert_column(num_cols); // Throws + } + + static void adj_erase_column(Table& table, size_t col_ndx) noexcept + { + table.adj_erase_column(col_ndx); + } + + static void adj_move_column(Table& table, size_t col_ndx_1, size_t col_ndx_2) noexcept + { + table.adj_move_column(col_ndx_1, col_ndx_2); + } + + static bool is_marked(const Table& table) noexcept + { + return table.is_marked(); + } + + static void mark(Table& table) noexcept + { + table.mark(); + } + + static void unmark(Table& table) noexcept + { + table.unmark(); + } + + static void recursive_mark(Table& table) noexcept + { + table.recursive_mark(); + } + + static void mark_link_target_tables(Table& table, size_t col_ndx_begin) noexcept + { + table.mark_link_target_tables(col_ndx_begin); + } + + static void mark_opposite_link_tables(Table& table) noexcept + { + table.mark_opposite_link_tables(); + } + + static DescriptorRef get_root_table_desc_accessor(Table& root_table) noexcept + { + return root_table.m_descriptor.lock(); + } + + typedef Table::AccessorUpdater AccessorUpdater; + static void update_accessors(Table& table, const size_t* col_path_begin, const size_t* col_path_end, + AccessorUpdater& updater) + { + table.update_accessors(col_path_begin, col_path_end, updater); // Throws + } + + static void refresh_accessor_tree(Table& table) + { + table.refresh_accessor_tree(); // Throws + } + + static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept + { + table.set_ndx_in_parent(ndx_in_parent); + } + + static void set_shared_subspec_ndx_in_parent(Table& table, size_t spec_ndx_in_parent) noexcept + { + table.m_spec.set_ndx_in_parent(spec_ndx_in_parent); + } + + static bool is_link_type(ColumnType type) noexcept + { + return Table::is_link_type(type); + } + + static void bump_version(Table& table, bool bump_global = true) noexcept + { + table.bump_version(bump_global); + } + + static bool is_cross_table_link_target(const Table& table) + { + return table.is_cross_table_link_target(); + } + + static Group* get_parent_group(const Table& table) noexcept + { + return table.get_parent_group(); + } + + static Replication* get_repl(Table& table) noexcept + { + return table.get_repl(); + } + + static void register_view(Table& table, const TableViewBase* view) + { + table.register_view(view); // Throws + } + + static void unregister_view(Table& table, const TableViewBase* view) noexcept + { + table.unregister_view(view); + } +}; + + +} // namespace realm + +#endif // REALM_TABLE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_ref.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_ref.hpp new file mode 100644 index 0000000..bef2074 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_ref.hpp @@ -0,0 +1,480 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_TABLE_REF_HPP +#define REALM_TABLE_REF_HPP + +#include +#include + +#include + +namespace realm { + + +class Table; + + +/// A reference-counting "smart pointer" for referring to table +/// accessors. +/// +/// The purpose of this smart pointer is to keep the referenced table +/// accessor alive for as long as anybody is referring to it, however, +/// for stack allocated table accessors, the lifetime is necessarily +/// determined by scope (see below). +/// +/// Please take note of the distinction between a "table" and a "table +/// accessor" here. A table accessor is an instance of `Table`, +/// and it may, or may not be attached to an +/// actual table at any specific point in time, but this state of +/// attachment of the accessor has nothing to do with the function of +/// the smart pointer. Also, in the rest of the documentation of this +/// class, whenever you see `Table::%foo`, you are supposed to read it +/// as, `Table::%foo`. +/// +/// +/// Table accessors are either created directly by an application via +/// a call to one of the public table constructors, or they are +/// created internally by the Realm library, such as when the +/// application calls Group::get_table(), Table::get_subtable(), or +/// Table::create(). +/// +/// Applications can safely assume that all table accessors, created +/// internally by the Realm library, have a lifetime that is managed +/// by reference counting. This means that the application can prolong +/// the lifetime of *such* table accessors indefinitely by holding on +/// to at least one smart pointer, but note that the guarantee of the +/// continued existence of the accessor, does not imply that the +/// accessor remains attached to the underlying table (see +/// Table::is_attached() for details). Accessors whose lifetime are +/// controlled by reference counting are destroyed exactly when the +/// reference count drops to zero. +/// +/// When an application creates a new table accessor by a direct call +/// to one of the public constructors, the lifetime of that table +/// accessor is *not*, and cannot be managed by reference +/// counting. This is true regardless of the way the accessor is +/// created (i.e., regardless of whether it is an automatic variable +/// on the stack, or created on the heap using `new`). However, for +/// convenience, but with one important caveat, it is still possible +/// to use smart pointers to refer to such accessors. The caveat is +/// that no smart pointers are allowed to refer to the accessor at the +/// point in time when its destructor is called. It is entirely the +/// responsibility of the application to ensure that this requirement +/// is met. Failing to do so, will result in undefined +/// behavior. Finally, please note that an application is always free +/// to use Table::create() as an alternative to creating free-standing +/// top-level tables on the stack, and that this is indeed neccessary +/// when fully reference counted lifetimes are required. +/// +/// So, at any time, and for any table accessor, an application can +/// call Table::get_table_ref() to obtain a smart pointer that refers +/// to that table, however, while that is always possible and safe, it +/// is not always possible to extend the lifetime of an accessor by +/// holding on to a smart pointer. The question of whether that is +/// possible, depends directly on the way the accessor was created. +/// +/// +/// Apart from keeping track of the number of references, these smart +/// pointers behaves almost exactly like regular pointers. In +/// particular, it is possible to dereference a TableRef and get a +/// `Table&` out of it, however, if you are not careful, this can +/// easily lead to dangling references: +/// +/// \code{.cpp} +/// +/// Table& sub_1 = *(table.get_subtable(0,0)); +/// sub_1.add_empty_row(); // Oops, sub_1 may be dangling! +/// +/// \endcode +/// +/// Whether `sub_1` is actually dangling in the example above will +/// depend on whether other references to the same subtable accessor +/// already exist, but it is never wise to rely in this. Here is a +/// safe and proper alternative: +/// +/// \code{.cpp} +/// +/// TableRef sub_2 = table.get_subtable(0,0); +/// sub_2.add_empty_row(); // Safe! +/// +/// void do_something(Table&); +/// do_something(*(table.get_subtable(0,0))); // Also safe! +/// +/// \endcode +/// +/// +/// \sa Table +/// \sa TableRef +template +class BasicTableRef : util::bind_ptr { +public: + constexpr BasicTableRef() noexcept + { + } + ~BasicTableRef() noexcept + { + } + + // Copy construct + BasicTableRef(const BasicTableRef& r) noexcept + : util::bind_ptr(r) + { + } + template + BasicTableRef(const BasicTableRef& r) noexcept + : util::bind_ptr(r) + { + } + + // Copy assign + BasicTableRef& operator=(const BasicTableRef&) noexcept; + template + BasicTableRef& operator=(const BasicTableRef&) noexcept; + + // Move construct + BasicTableRef(BasicTableRef&& r) noexcept + : util::bind_ptr(std::move(r)) + { + } + template + BasicTableRef(BasicTableRef&& r) noexcept + : util::bind_ptr(std::move(r)) + { + } + + // Move assign + BasicTableRef& operator=(BasicTableRef&&) noexcept; + template + BasicTableRef& operator=(BasicTableRef&&) noexcept; + + //@{ + /// Comparison + template + bool operator==(const BasicTableRef&) const noexcept; + + template + bool operator==(U*) const noexcept; + + template + bool operator!=(const BasicTableRef&) const noexcept; + + template + bool operator!=(U*) const noexcept; + + template + bool operator<(const BasicTableRef&) const noexcept; + + template + bool operator<(U*) const noexcept; + + template + bool operator>(const BasicTableRef&) const noexcept; + + template + bool operator>(U*) const noexcept; + + template + bool operator<=(const BasicTableRef&) const noexcept; + + template + bool operator<=(U*) const noexcept; + + template + bool operator>=(const BasicTableRef&) const noexcept; + + template + bool operator>=(U*) const noexcept; +//@} + +// Dereference +#ifdef __clang__ + // Clang has a bug that causes it to effectively ignore the 'using' declaration. + T& operator*() const noexcept + { + return util::bind_ptr::operator*(); + } +#else + using util::bind_ptr::operator*; +#endif + using util::bind_ptr::operator->; + + using util::bind_ptr::operator bool; + + T* get() const noexcept + { + return util::bind_ptr::get(); + } + void reset() noexcept + { + util::bind_ptr::reset(); + } + void reset(T* t) noexcept + { + util::bind_ptr::reset(t); + } + + void swap(BasicTableRef& r) noexcept + { + this->util::bind_ptr::swap(r); + } + friend void swap(BasicTableRef& a, BasicTableRef& b) noexcept + { + a.swap(b); + } + + template + friend BasicTableRef unchecked_cast(BasicTableRef
) noexcept; + + template + friend BasicTableRef unchecked_cast(BasicTableRef) noexcept; + +private: + template + struct GetRowAccType { + typedef void type; + }; + + typedef typename GetRowAccType::type RowAccessor; + +public: + /// Same as 'table[i]' where 'table' is the referenced table. + RowAccessor operator[](size_t i) const noexcept + { + return (*this->get())[i]; + } + + explicit BasicTableRef(T* t) noexcept + : util::bind_ptr(t) + { + } + +private: + friend class SubtableColumnBase; + friend class Table; + friend class Group; + + template + friend class BasicTableRef; + + typedef typename util::bind_ptr::casting_move_tag casting_move_tag; + template + BasicTableRef(BasicTableRef* r, casting_move_tag) noexcept + : util::bind_ptr(r, casting_move_tag()) + { + } +}; + + +typedef BasicTableRef
TableRef; +typedef BasicTableRef ConstTableRef; + + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const BasicTableRef& p) +{ + out << static_cast(&*p); + return out; +} + +template +inline BasicTableRef unchecked_cast(TableRef t) noexcept +{ + return BasicTableRef(&t, typename BasicTableRef::casting_move_tag()); +} + +template +inline BasicTableRef unchecked_cast(ConstTableRef t) noexcept +{ + return BasicTableRef(&t, typename BasicTableRef::casting_move_tag()); +} + + +//@{ +/// Comparison +template +bool operator==(T*, const BasicTableRef&) noexcept; +template +bool operator!=(T*, const BasicTableRef&) noexcept; +template +bool operator<(T*, const BasicTableRef&) noexcept; +template +bool operator>(T*, const BasicTableRef&) noexcept; +template +bool operator<=(T*, const BasicTableRef&) noexcept; +template +bool operator>=(T*, const BasicTableRef&) noexcept; +//@} + + +// Implementation: + +template +inline BasicTableRef& BasicTableRef::operator=(const BasicTableRef& r) noexcept +{ + this->util::bind_ptr::operator=(r); + return *this; +} + +template +template +inline BasicTableRef& BasicTableRef::operator=(const BasicTableRef& r) noexcept +{ + this->util::bind_ptr::operator=(r); + return *this; +} + +template +inline BasicTableRef& BasicTableRef::operator=(BasicTableRef&& r) noexcept +{ + this->util::bind_ptr::operator=(std::move(r)); + return *this; +} + +template +template +inline BasicTableRef& BasicTableRef::operator=(BasicTableRef&& r) noexcept +{ + this->util::bind_ptr::operator=(std::move(r)); + return *this; +} + +template +template +bool BasicTableRef::operator==(const BasicTableRef& p) const noexcept +{ + return get() == p.get(); +} + +template +template +bool BasicTableRef::operator==(U* p) const noexcept +{ + return get() == p; +} + +template +template +bool BasicTableRef::operator!=(const BasicTableRef& p) const noexcept +{ + return get() != p.get(); +} + +template +template +bool BasicTableRef::operator!=(U* p) const noexcept +{ + return get() != p; +} + +template +template +bool BasicTableRef::operator<(const BasicTableRef& p) const noexcept +{ + return get() < p.get(); +} + +template +template +bool BasicTableRef::operator<(U* p) const noexcept +{ + return get() < p; +} + +template +template +bool BasicTableRef::operator>(const BasicTableRef& p) const noexcept +{ + return get() > p.get(); +} + +template +template +bool BasicTableRef::operator>(U* p) const noexcept +{ + return get() > p; +} + +template +template +bool BasicTableRef::operator<=(const BasicTableRef& p) const noexcept +{ + return get() <= p.get(); +} + +template +template +bool BasicTableRef::operator<=(U* p) const noexcept +{ + return get() <= p; +} + +template +template +bool BasicTableRef::operator>=(const BasicTableRef& p) const noexcept +{ + return get() >= p.get(); +} + +template +template +bool BasicTableRef::operator>=(U* p) const noexcept +{ + return get() >= p; +} + +template +bool operator==(T* a, const BasicTableRef& b) noexcept +{ + return b == a; +} + +template +bool operator!=(T* a, const BasicTableRef& b) noexcept +{ + return b != a; +} + +template +bool operator<(T* a, const BasicTableRef& b) noexcept +{ + return b > a; +} + +template +bool operator>(T* a, const BasicTableRef& b) noexcept +{ + return b < a; +} + +template +bool operator<=(T* a, const BasicTableRef& b) noexcept +{ + return b >= a; +} + +template +bool operator>=(T* a, const BasicTableRef& b) noexcept +{ + return b <= a; +} + + +} // namespace realm + +#endif // REALM_TABLE_REF_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_view.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_view.hpp new file mode 100644 index 0000000..6f4d0c9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/table_view.hpp @@ -0,0 +1,1572 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_TABLE_VIEW_HPP +#define REALM_TABLE_VIEW_HPP + +#include +#include +#include +#include +#include + +namespace realm { + +// Views, tables and synchronization between them: +// +// Views are built through queries against either tables or another view. +// Views may be restricted to only hold entries provided by another view. +// this other view is called the "restricting view". +// Views may be sorted in ascending or descending order of values in one ore more columns. +// +// Views remember the query from which it was originally built. +// Views remember the table from which it was originally built. +// Views remember a restricting view if one was used when it was originally built. +// Views remember the sorting criteria (columns and direction) +// +// A view may be operated in one of two distinct modes: *reflective* and *imperative*. +// Sometimes the term "reactive" is used instead of "reflective" with the same meaning. +// +// Reflective views: +// - A reflective view *always* *reflect* the result of running the query. +// If the underlying tables or tableviews change, the reflective view changes as well. +// A reflective view may need to rerun the query it was generated from, a potentially +// costly operation which happens on demand. +// - It does not matter whether changes are explicitly done within the transaction, or +// occur implicitly as part of advance_read() or promote_to_write(). +// +// Imperative views: +// - An imperative view only *initially* holds the result of the query. An imperative +// view *never* reruns the query. To force the view to match it's query (by rerunning it), +// the view must be operated in reflective mode. +// An imperative view can be modified explicitly. References can be added, removed or +// changed. +// +// - In imperative mode, the references in the view tracks movement of the referenced data: +// If you delete an entry which is referenced from a view, said reference is detached, +// not removed. +// - It does not matter whether the delete is done in-line (as part of the current transaction), +// or if it is done implicitly as part of advance_read() or promote_to_write(). +// +// The choice between reflective and imperative views might eventually be represented by a +// switch on the tableview, but isn't yet. For now, clients (bindings) must call sync_if_needed() +// to get reflective behavior. +// +// Use cases: +// +// 1. Presenting data +// The first use case (and primary motivator behind the reflective view) is to just track +// and present the state of the database. In this case, the view is operated in reflective +// mode, it is not modified within the transaction, and it is not used to modify data in +// other parts of the database. +// +// 2. Handover +// The second use case is "handover." The implicit rerun of the query in our first use case +// may be too costly to be acceptable on the main thread. Instead you want to run the query +// on a worker thread, but display it on the main thread. To achieve this, you need two +// SharedGroups locked on to the same version of the database. If you have that, you can +// *handover* a view from one thread/SharedGroup to the other. +// +// Handover is a two-step procedure. First, the accessors are *exported* from one SharedGroup, +// called the sourcing group, then it is *imported* into another SharedGroup, called the +// receiving group. The thread associated with the sourcing SharedGroup will be +// responsible for the export operation, while the thread associated with the receiving +// SharedGroup will do the import operation. +// +// 3. Iterating a view and changing data +// The third use case (and a motivator behind the imperative view) is when you want +// to make changes to the database in accordance with a query result. Imagine you want to +// find all employees with a salary below a limit and raise their salaries to the limit (pseudocode): +// +// promote_to_write(); +// view = table.where().less_than(salary_column,limit).find_all(); +// for (size_t i = 0; i < view.size(); ++i) { +// view.set_int(salary_column, i, limit); +// // add this to get reflective mode: view.sync_if_needed(); +// } +// commit_and_continue_as_read(); +// +// This is idiomatic imperative code and it works if the view is operated in imperative mode. +// +// If the view is operated in reflective mode, the behaviour surprises most people: When the +// first salary is changed, the entry no longer fullfills the query, so it is dropped from the +// view implicitly. view[0] is removed, view[1] moves to view[0] and so forth. But the next +// loop iteration has i=1 and refers to view[1], thus skipping view[0]. The end result is that +// every other employee get a raise, while the others don't. +// +// 4. Iterating intermixed with implicit updates +// This leads us to use case 4, which is similar to use case 3, but uses promote_to_write() +// intermixed with iterating a view. This is actually quite important to some, who do not want +// to end up with a large write transaction. +// +// view = table.where().less_than(salary_column,limit).find_all(); +// for (size_t i = 0; i < view.size(); ++i) { +// promote_to_write(); +// view.set_int(salary_column, i, limit); +// commit_and_continue_as_write(); +// } +// +// Anything can happen at the call to promote_to_write(). The key question then becomes: how +// do we support a safe way of realising the original goal (raising salaries) ? +// +// using the imperative operating mode: +// +// view = table.where().less_than(salary_column,limit).find_all(); +// for (size_t i = 0; i < view.size(); ++i) { +// promote_to_write(); +// // add r.sync_if_needed(); to get reflective mode +// if (r.is_row_attached(i)) { +// Row r = view[i]; +// r.set_int(salary_column, limit); +// } +// commit_and_continue_as_write(); +// } +// +// This is safe, and we just aim for providing low level safety: is_row_attached() can tell +// if the reference is valid, and the references in the view continue to point to the +// same object at all times, also following implicit updates. The rest is up to the +// application logic. +// +// It is important to see, that there is no guarantee that all relevant employees get +// their raise in cases whith concurrent updates. At every call to promote_to_write() new +// employees may be added to the underlying table, but as the view is in imperative mode, +// these new employees are not added to the view. Also at promote_to_write() an existing +// employee could recieve a (different, larger) raise which would then be overwritten and lost. +// However, these are problems that you should expect, since the activity is spread over multiple +// transactions. + + +/// Common base class for TableView and ConstTableView. +class TableViewBase : public RowIndexes { +public: + // - not in use / implemented yet: ... explicit calls to sync_if_needed() must be used + // to get 'reflective' mode. + // enum mode { mode_Reflective, mode_Imperative }; + // void set_operating_mode(mode); + // mode get_operating_mode(); + bool is_empty() const noexcept; + + // Tells if the table that this TableView points at still exists or has been deleted. + bool is_attached() const noexcept; + + bool is_row_attached(size_t row_ndx) const noexcept; + size_t size() const noexcept; + size_t num_attached_rows() const noexcept; + + // Get the query used to create this TableView + // The query will have a null source table if this tv was not created from + // a query + const Query& get_query() const noexcept; + + // Column information + const ColumnBase& get_column_base(size_t index) const; + + size_t get_column_count() const noexcept; + StringData get_column_name(size_t column_ndx) const noexcept; + size_t get_column_index(StringData name) const; + DataType get_column_type(size_t column_ndx) const noexcept; + + // Getting values + int64_t get_int(size_t column_ndx, size_t row_ndx) const noexcept; + bool get_bool(size_t column_ndx, size_t row_ndx) const noexcept; + OldDateTime get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept; + Timestamp get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept; + float get_float(size_t column_ndx, size_t row_ndx) const noexcept; + double get_double(size_t column_ndx, size_t row_ndx) const noexcept; + StringData get_string(size_t column_ndx, size_t row_ndx) const noexcept; + BinaryData get_binary(size_t column_ndx, size_t row_ndx) const noexcept; + Mixed get_mixed(size_t column_ndx, size_t row_ndx) const noexcept; + DataType get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept; + size_t get_link(size_t column_ndx, size_t row_ndx) const noexcept; + + // Links + bool is_null_link(size_t column_ndx, size_t row_ndx) const noexcept; + + // Subtables + size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept; + + // Searching (Int and String) + size_t find_first_int(size_t column_ndx, int64_t value) const; + size_t find_first_bool(size_t column_ndx, bool value) const; + size_t find_first_olddatetime(size_t column_ndx, OldDateTime value) const; + size_t find_first_float(size_t column_ndx, float value) const; + size_t find_first_double(size_t column_ndx, double value) const; + size_t find_first_string(size_t column_ndx, StringData value) const; + size_t find_first_binary(size_t column_ndx, BinaryData value) const; + + // Aggregate functions. count_target is ignored by all except Count. Hack because of bug in optional + // arguments in clang and vs2010 (fixed in 2012) + template + R aggregate(R (ColType::*aggregateMethod)(size_t, size_t, size_t, size_t*) const, size_t column_ndx, + T count_target, size_t* return_ndx = nullptr) const; + + int64_t sum_int(size_t column_ndx) const; + int64_t maximum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + int64_t minimum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + double average_int(size_t column_ndx, size_t* value_count = nullptr) const; + size_t count_int(size_t column_ndx, int64_t target) const; + + double sum_float(size_t column_ndx) const; + float maximum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + float minimum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + double average_float(size_t column_ndx, size_t* value_count = nullptr) const; + size_t count_float(size_t column_ndx, float target) const; + + double sum_double(size_t column_ndx) const; + double maximum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + double minimum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + double average_double(size_t column_ndx, size_t* value_count = nullptr) const; + size_t count_double(size_t column_ndx, double target) const; + + OldDateTime maximum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + OldDateTime minimum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + + Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + size_t count_timestamp(size_t column_ndx, Timestamp target) const; + + // Simple pivot aggregate method. Experimental! Please do not + // document method publicly. + void aggregate(size_t group_by_column, size_t aggr_column, Table::AggrType op, Table& result) const; + + // Get row index in the source table this view is "looking" at. + size_t get_source_ndx(size_t row_ndx) const noexcept; + + /// Search this view for the specified source table row (specified by its + /// index in the source table). If found, the index of that row within this + /// view is returned, otherwise `realm::not_found` is returned. + size_t find_by_source_ndx(size_t source_ndx) const noexcept; + + // Conversion + void to_json(std::ostream&) const; + void to_string(std::ostream&, size_t limit = 500) const; + void row_to_string(size_t row_ndx, std::ostream&) const; + + // Determine if the view is 'in sync' with the underlying table + // as well as other views used to generate the view. Note that updates + // through views maintains synchronization between view and table. + // It doesnt by itself maintain other views as well. So if a view + // is generated from another view (not a table), updates may cause + // that view to be outdated, AND as the generated view depends upon + // it, it too will become outdated. + bool is_in_sync() const; + + // Tells if this TableView depends on a LinkList or row that has been deleted. + bool depends_on_deleted_object() const; + + // Synchronize a view to match a table or tableview from which it + // has been derived. Synchronization is achieved by rerunning the + // query used to generate the view. If derived from another view, that + // view will be synchronized as well. + // + // "live" or "reactive" views are implemented by calling sync_if_needed + // before any of the other access-methods whenever the view may have become + // outdated. + // + // This will make the TableView empty and in sync with the highest possible table version + // if the TableView depends on an object (LinkView or row) that has been deleted. + uint_fast64_t sync_if_needed() const; + + // Sort m_row_indexes according to one column + void sort(size_t column, bool ascending = true); + + // Sort m_row_indexes according to multiple columns + void sort(SortDescriptor order); + + // Remove rows that are duplicated with respect to the column set passed as argument. + // distinct() will preserve the original order of the row pointers, also if the order is a result of sort() + // If two rows are indentical (for the given set of distinct-columns), then the last row is removed. + // You can call sync_if_needed() to update the distinct view, just like you can for a sorted view. + // Each time you call distinct() it will first fetch the full original TableView contents and then apply + // distinct() on that. So it distinct() does not filter the result of the previous distinct(). + void distinct(size_t column); + void distinct(SortDescriptor columns); + + // Returns whether the rows are guaranteed to be in table order. + // This is true only of unsorted TableViews created from either: + // - Table::find_all() + // - Query::find_all() when the query is not restricted to a view. + bool is_in_table_order() const; + + virtual ~TableViewBase() noexcept; + + virtual std::unique_ptr clone() const = 0; + +protected: + // This TableView can be "born" from 4 different sources: + // - LinkView + // - Query::find_all() + // - Table::get_distinct_view() + // - Table::get_backlink_view() + // Return the version of the source it was created from. + uint64_t outside_version() const; + + void do_sync(); + + // Null if, and only if, the view is detached. + mutable TableRef m_table; + + // The link column that this view contain backlinks for. + const BacklinkColumn* m_linked_column = nullptr; + // The target row that rows in this view link to. + ConstRow m_linked_row; + + // If this TableView was created from a LinkView, then this reference points to it. Otherwise it's 0 + mutable ConstLinkViewRef m_linkview_source; + + // m_distinct_column_source != npos if this view was created from distinct values in a column of m_table. + size_t m_distinct_column_source = npos; + + // If not empty, this TableView has had TableView::distinct() called and must + // only contain unique rows with respect to that column set of the parent table + SortDescriptor m_distinct_predicate; + + SortDescriptor m_sorting_predicate; // Stores sorting criterias (columns + ascending) + + + // A valid query holds a reference to its table which must match our m_table. + // hence we can use a query with a null table reference to indicate that the view + // was NOT generated by a query, but follows a table directly. + Query m_query; + // parameters for findall, needed to rerun the query + size_t m_start; + size_t m_end; + size_t m_limit; + + mutable util::Optional m_last_seen_version; + + size_t m_num_detached_refs = 0; + /// Construct null view (no memory allocated). + TableViewBase(); + + /// Construct empty view, ready for addition of row indices. + TableViewBase(Table* parent); + TableViewBase(Table* parent, Query& query, size_t start, size_t end, size_t limit); + TableViewBase(Table* parent, size_t column, BasicRowExpr row); + TableViewBase(Table* parent, ConstLinkViewRef link_view); + + enum DistinctViewTag { DistinctView }; + TableViewBase(DistinctViewTag, Table* parent, size_t column_ndx); + + /// Copy constructor. + TableViewBase(const TableViewBase&); + + /// Move constructor. + TableViewBase(TableViewBase&&) noexcept; + + TableViewBase& operator=(const TableViewBase&); + TableViewBase& operator=(TableViewBase&&) noexcept; + + template + static R find_all_integer(V*, size_t, int64_t); + + template + static R find_all_float(V*, size_t, float); + + template + static R find_all_double(V*, size_t, double); + + template + static R find_all_string(V*, size_t, StringData); + + using HandoverPatch = TableViewHandoverPatch; + + // handover machinery entry points based on dynamic type. These methods: + // a) forward their calls to the static type entry points. + // b) new/delete patch data structures. + virtual std::unique_ptr clone_for_handover(std::unique_ptr& patch, + ConstSourcePayload mode) const = 0; + + virtual std::unique_ptr clone_for_handover(std::unique_ptr& patch, + MutableSourcePayload mode) = 0; + + void apply_and_consume_patch(std::unique_ptr& patch, Group& group) + { + apply_patch(*patch, group); + patch.reset(); + } + // handover machinery entry points based on static type + void apply_patch(HandoverPatch& patch, Group& group); + TableViewBase(const TableViewBase& source, HandoverPatch& patch, ConstSourcePayload mode); + TableViewBase(TableViewBase& source, HandoverPatch& patch, MutableSourcePayload mode); + +private: + void allocate_row_indexes(); + void detach() const noexcept; // may have to remove const + size_t find_first_integer(size_t column_ndx, int64_t value) const; + template + Timestamp minmax_timestamp(size_t column_ndx, size_t* return_ndx) const; + + friend class Table; + friend class Query; + friend class SharedGroup; + + // Called by table to adjust any row references: + void adj_row_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + void adj_row_acc_erase_row(size_t row_ndx) noexcept; + void adj_row_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + void adj_row_acc_clear() noexcept; +}; + + +inline void TableViewBase::detach() const noexcept // may have to remove const +{ + m_table = TableRef(); +} + + +class ConstTableView; + + +enum class RemoveMode { ordered, unordered }; + + +/// A TableView gives read and write access to the parent table. +/// +/// A 'const TableView' cannot be changed (e.g. sorted), nor can the +/// parent table be modified through it. +/// +/// A TableView is both copyable and movable. +class TableView : public TableViewBase { +public: + using TableViewBase::TableViewBase; + + TableView() = default; + + // Rows + typedef BasicRowExpr
RowExpr; + typedef BasicRowExpr ConstRowExpr; + RowExpr get(size_t row_ndx) noexcept; + ConstRowExpr get(size_t row_ndx) const noexcept; + RowExpr front() noexcept; + ConstRowExpr front() const noexcept; + RowExpr back() noexcept; + ConstRowExpr back() const noexcept; + RowExpr operator[](size_t row_ndx) noexcept; + ConstRowExpr operator[](size_t row_ndx) const noexcept; + + // Setting values + void set_int(size_t column_ndx, size_t row_ndx, int64_t value); + void set_bool(size_t column_ndx, size_t row_ndx, bool value); + void set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value); + void set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value); + template + void set_enum(size_t column_ndx, size_t row_ndx, E value); + void set_float(size_t column_ndx, size_t row_ndx, float value); + void set_double(size_t column_ndx, size_t row_ndx, double value); + void set_string(size_t column_ndx, size_t row_ndx, StringData value); + void set_binary(size_t column_ndx, size_t row_ndx, BinaryData value); + void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value); + void set_subtable(size_t column_ndx, size_t row_ndx, const Table* table); + void set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx); + + // Subtables + TableRef get_subtable(size_t column_ndx, size_t row_ndx); + ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const; + void clear_subtable(size_t column_ndx, size_t row_ndx); + + // Links + TableRef get_link_target(size_t column_ndx) noexcept; + ConstTableRef get_link_target(size_t column_ndx) const noexcept; + void nullify_link(size_t column_ndx, size_t row_ndx); + + /// \defgroup table_view_removes + //@{ + /// \brief Remove the specified row (or rows) from the underlying table. + /// + /// remove() removes the specified row from the underlying table, + /// remove_last() removes the last row in the table view from the underlying + /// table, and clear removes all the rows in the table view from the + /// underlying table. + /// + /// When rows are removed from the underlying table, they will by necessity + /// also be removed from the table view. + /// + /// The order of the remaining rows in the the table view will be maintained + /// regardless of the value passed for \a underlying_mode. + /// + /// \param row_ndx The index within this table view of the row to be + /// removed. + /// + /// \param underlying_mode If set to RemoveMode::ordered (the default), the + /// rows will be removed from the underlying table in a way that maintains + /// the order of the remaining rows in the underlying table. If set to + /// RemoveMode::unordered, the order of the remaining rows in the underlying + /// table will not in general be maintaind, but the operation will generally + /// be much faster. In any case, the order of remaining rows in the table + /// view will not be affected. + void remove(size_t row_ndx, RemoveMode underlying_mode = RemoveMode::ordered); + void remove_last(RemoveMode underlying_mode = RemoveMode::ordered); + void clear(RemoveMode underlying_mode = RemoveMode::ordered); + //@} + + // Searching (Int and String) + TableView find_all_int(size_t column_ndx, int64_t value); + ConstTableView find_all_int(size_t column_ndx, int64_t value) const; + TableView find_all_bool(size_t column_ndx, bool value); + ConstTableView find_all_bool(size_t column_ndx, bool value) const; + TableView find_all_olddatetime(size_t column_ndx, OldDateTime value); + ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const; + TableView find_all_float(size_t column_ndx, float value); + ConstTableView find_all_float(size_t column_ndx, float value) const; + TableView find_all_double(size_t column_ndx, double value); + ConstTableView find_all_double(size_t column_ndx, double value) const; + TableView find_all_string(size_t column_ndx, StringData value); + ConstTableView find_all_string(size_t column_ndx, StringData value) const; + // FIXME: Need: TableView find_all_binary(size_t column_ndx, BinaryData value); + // FIXME: Need: ConstTableView find_all_binary(size_t column_ndx, BinaryData value) const; + + Table& get_parent() noexcept; + const Table& get_parent() const noexcept; + + std::unique_ptr clone() const override + { + return std::unique_ptr(new TableView(*this)); + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, + ConstSourcePayload mode) const override + { + patch.reset(new HandoverPatch); + std::unique_ptr retval(new TableView(*this, *patch, mode)); + return retval; + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, + MutableSourcePayload mode) override + { + patch.reset(new HandoverPatch); + std::unique_ptr retval(new TableView(*this, *patch, mode)); + return retval; + } + +private: + TableView(Table& parent); + TableView(Table& parent, Query& query, size_t start, size_t end, size_t limit); + TableView(Table& parent, ConstLinkViewRef); + + TableView(DistinctViewTag, Table& parent, size_t column_ndx); + + TableView find_all_integer(size_t column_ndx, int64_t value); + ConstTableView find_all_integer(size_t column_ndx, int64_t value) const; + + friend class ConstTableView; + friend class Table; + friend class Query; + friend class TableViewBase; + friend class LinkView; +}; + + +/// A ConstTableView gives read access to the parent table, but no +/// write access. The view itself, though, can be changed, for +/// example, it can be sorted. +/// +/// Note that methods are declared 'const' if, and only if they leave +/// the view unmodified, and this is irrespective of whether they +/// modify the parent table. +/// +/// A ConstTableView has both copy and move semantics. See TableView +/// for more on this. +class ConstTableView : public TableViewBase { +public: + using TableViewBase::TableViewBase; + + ConstTableView() = default; + + ConstTableView(const TableView&); + ConstTableView(TableView&&); + ConstTableView& operator=(const TableView&); + ConstTableView& operator=(TableView&&); + + // Rows + typedef BasicRowExpr ConstRowExpr; + ConstRowExpr get(size_t row_ndx) const noexcept; + ConstRowExpr front() const noexcept; + ConstRowExpr back() const noexcept; + ConstRowExpr operator[](size_t row_ndx) const noexcept; + + // Subtables + ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const; + + // Links + ConstTableRef get_link_target(size_t column_ndx) const noexcept; + + // Searching (Int and String) + ConstTableView find_all_int(size_t column_ndx, int64_t value) const; + ConstTableView find_all_bool(size_t column_ndx, bool value) const; + ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const; + ConstTableView find_all_float(size_t column_ndx, float value) const; + ConstTableView find_all_double(size_t column_ndx, double value) const; + ConstTableView find_all_string(size_t column_ndx, StringData value) const; + + const Table& get_parent() const noexcept; + + std::unique_ptr clone() const override + { + return std::unique_ptr(new ConstTableView(*this)); + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, + ConstSourcePayload mode) const override + { + patch.reset(new HandoverPatch); + std::unique_ptr retval(new ConstTableView(*this, *patch, mode)); + return retval; + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, + MutableSourcePayload mode) override + { + patch.reset(new HandoverPatch); + std::unique_ptr retval(new ConstTableView(*this, *patch, mode)); + return retval; + } + +private: + ConstTableView(const Table& parent); + + ConstTableView find_all_integer(size_t column_ndx, int64_t value) const; + + friend class TableView; + friend class Table; + friend class Query; + friend class TableViewBase; +}; + + +// ================================================================================================ +// TableViewBase Implementation: + +inline const Query& TableViewBase::get_query() const noexcept +{ + return m_query; +} + +inline bool TableViewBase::is_empty() const noexcept +{ + return m_row_indexes.is_empty(); +} + +inline bool TableViewBase::is_attached() const noexcept +{ + return bool(m_table); +} + +inline bool TableViewBase::is_row_attached(size_t row_ndx) const noexcept +{ + return m_row_indexes.get(row_ndx) != detached_ref; +} + +inline size_t TableViewBase::size() const noexcept +{ + return m_row_indexes.size(); +} + +inline size_t TableViewBase::num_attached_rows() const noexcept +{ + return m_row_indexes.size() - m_num_detached_refs; +} + +inline size_t TableViewBase::get_source_ndx(size_t row_ndx) const noexcept +{ + return to_size_t(m_row_indexes.get(row_ndx)); +} + +inline size_t TableViewBase::find_by_source_ndx(size_t source_ndx) const noexcept +{ + REALM_ASSERT(source_ndx < m_table->size()); + return m_row_indexes.find_first(source_ndx); +} + +inline void TableViewBase::allocate_row_indexes() +{ + // FIXME: This code is unreasonably complicated because it uses `IntegerColumn` as + // a free-standing container, and beause `IntegerColumn` does not conform to the + // RAII idiom (nor should it). + Allocator& alloc = m_row_indexes.get_alloc(); + _impl::DeepArrayRefDestroyGuard ref_guard(alloc); + ref_guard.reset(IntegerColumn::create(alloc)); // Throws + m_table->register_view(this); // Throws + m_row_indexes.init_from_ref(alloc, ref_guard.release()); +} + +inline TableViewBase::TableViewBase() + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws +{ + ref_type ref = IntegerColumn::create(m_row_indexes.get_alloc()); // Throws + m_row_indexes.get_root_array()->init_from_ref(ref); +} + +inline TableViewBase::TableViewBase(Table* parent) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) + , m_table(parent->get_table_ref()) // Throws + , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none) +{ + allocate_row_indexes(); +} + +inline TableViewBase::TableViewBase(Table* parent, Query& query, size_t start, size_t end, size_t limit) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws + , m_table(parent->get_table_ref()) + , m_query(query) + , m_start(start) + , m_end(end) + , m_limit(limit) + , m_last_seen_version(outside_version()) +{ + allocate_row_indexes(); +} + +inline TableViewBase::TableViewBase(Table* parent, size_t column, BasicRowExpr row) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) + , m_table(parent->get_table_ref()) // Throws + , m_linked_column(&parent->get_column_link_base(column).get_backlink_column()) + , m_linked_row(row) + , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none) +{ + allocate_row_indexes(); +} + +inline TableViewBase::TableViewBase(DistinctViewTag, Table* parent, size_t column_ndx) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) + , m_table(parent->get_table_ref()) // Throws + , m_distinct_column_source(column_ndx) + , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none) +{ + REALM_ASSERT(m_distinct_column_source != npos); + + allocate_row_indexes(); +} + +inline TableViewBase::TableViewBase(Table* parent, ConstLinkViewRef link_view) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) + , m_table(parent->get_table_ref()) // Throws + , m_linkview_source(std::move(link_view)) + , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none) +{ + REALM_ASSERT(m_linkview_source); + + allocate_row_indexes(); +} + +inline TableViewBase::TableViewBase(const TableViewBase& tv) + : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) + , m_table(tv.m_table) + , m_linked_column(tv.m_linked_column) + , m_linked_row(tv.m_linked_row) + , m_linkview_source(tv.m_linkview_source) + , m_distinct_column_source(tv.m_distinct_column_source) + , m_distinct_predicate(std::move(tv.m_distinct_predicate)) + , m_sorting_predicate(std::move(tv.m_sorting_predicate)) + , m_query(tv.m_query) + , m_start(tv.m_start) + , m_end(tv.m_end) + , m_limit(tv.m_limit) + , m_last_seen_version(tv.m_last_seen_version) + , m_num_detached_refs(tv.m_num_detached_refs) +{ + // FIXME: This code is unreasonably complicated because it uses `IntegerColumn` as + // a free-standing container, and because `IntegerColumn` does not conform to the + // RAII idiom (nor should it). + Allocator& alloc = m_row_indexes.get_alloc(); + MemRef mem = tv.m_row_indexes.get_root_array()->clone_deep(alloc); // Throws + _impl::DeepArrayRefDestroyGuard ref_guard(mem.get_ref(), alloc); + if (m_table) + m_table->register_view(this); // Throws + m_row_indexes.init_from_mem(alloc, mem); + ref_guard.release(); +} + +inline TableViewBase::TableViewBase(TableViewBase&& tv) noexcept + : RowIndexes(std::move(tv.m_row_indexes)) + , m_table(std::move(tv.m_table)) + , m_linked_column(tv.m_linked_column) + , m_linked_row(tv.m_linked_row) + , m_linkview_source(std::move(tv.m_linkview_source)) + , m_distinct_column_source(tv.m_distinct_column_source) + , m_distinct_predicate(std::move(tv.m_distinct_predicate)) + , m_sorting_predicate(std::move(tv.m_sorting_predicate)) + , m_query(std::move(tv.m_query)) + , m_start(tv.m_start) + , m_end(tv.m_end) + , m_limit(tv.m_limit) + , + // if we are created from a table view which is outdated, take care to use the outdated + // version number so that we can later trigger a sync if needed. + m_last_seen_version(tv.m_last_seen_version) + , m_num_detached_refs(tv.m_num_detached_refs) +{ + if (m_table) + m_table->move_registered_view(&tv, this); +} + +inline TableViewBase::~TableViewBase() noexcept +{ + if (m_table) { + m_table->unregister_view(this); + m_table = TableRef(); + } + m_row_indexes.destroy(); // Shallow +} + +inline TableViewBase& TableViewBase::operator=(TableViewBase&& tv) noexcept +{ + if (m_table) + m_table->unregister_view(this); + m_table = std::move(tv.m_table); + if (m_table) + m_table->move_registered_view(&tv, this); + + m_row_indexes.move_assign(tv.m_row_indexes); + m_query = std::move(tv.m_query); + m_num_detached_refs = tv.m_num_detached_refs; + m_last_seen_version = tv.m_last_seen_version; + m_start = tv.m_start; + m_end = tv.m_end; + m_limit = tv.m_limit; + m_linked_column = tv.m_linked_column; + m_linked_row = tv.m_linked_row; + m_linkview_source = std::move(tv.m_linkview_source); + m_distinct_predicate = std::move(tv.m_distinct_predicate); + m_distinct_column_source = tv.m_distinct_column_source; + m_sorting_predicate = std::move(tv.m_sorting_predicate); + + return *this; +} + +inline TableViewBase& TableViewBase::operator=(const TableViewBase& tv) +{ + if (this == &tv) + return *this; + + if (m_table != tv.m_table) { + if (m_table) + m_table->unregister_view(this); + m_table = tv.m_table; + if (m_table) + m_table->register_view(this); + } + + Allocator& alloc = m_row_indexes.get_alloc(); + MemRef mem = tv.m_row_indexes.get_root_array()->clone_deep(alloc); // Throws + _impl::DeepArrayRefDestroyGuard ref_guard(mem.get_ref(), alloc); + m_row_indexes.destroy(); + m_row_indexes.get_root_array()->init_from_mem(mem); + ref_guard.release(); + + m_query = tv.m_query; + m_num_detached_refs = tv.m_num_detached_refs; + m_last_seen_version = tv.m_last_seen_version; + m_start = tv.m_start; + m_end = tv.m_end; + m_limit = tv.m_limit; + m_linked_column = tv.m_linked_column; + m_linked_row = tv.m_linked_row; + m_linkview_source = tv.m_linkview_source; + m_distinct_predicate = tv.m_distinct_predicate; + m_distinct_column_source = tv.m_distinct_column_source; + m_sorting_predicate = tv.m_sorting_predicate; + + return *this; +} + +#define REALM_ASSERT_COLUMN(column_ndx) \ + REALM_ASSERT(m_table); \ + REALM_ASSERT(column_ndx < m_table->get_column_count()) + +#define REALM_ASSERT_ROW(row_ndx) \ + REALM_ASSERT(m_table); \ + REALM_ASSERT(row_ndx < m_row_indexes.size()) + +#define REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, column_type) \ + REALM_ASSERT_COLUMN(column_ndx); \ + REALM_DIAG_PUSH(); \ + REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE(); \ + REALM_ASSERT(m_table->get_column_type(column_ndx) == column_type || \ + (m_table->get_column_type(column_ndx) == type_OldDateTime && column_type == type_Int)); \ + REALM_DIAG_POP() + +#define REALM_ASSERT_INDEX(column_ndx, row_ndx) \ + REALM_ASSERT_COLUMN(column_ndx); \ + REALM_ASSERT(row_ndx < m_row_indexes.size()) + +#define REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, column_type) \ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, column_type); \ + REALM_ASSERT(row_ndx < m_row_indexes.size()) + +#define REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx) \ + REALM_ASSERT_COLUMN(column_ndx); \ + REALM_DIAG_PUSH(); \ + REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE(); \ + REALM_ASSERT(m_table->get_column_type(column_ndx) == type_Table || \ + (m_table->get_column_type(column_ndx) == type_Mixed)); \ + REALM_DIAG_POP(); \ + REALM_ASSERT(row_ndx < m_row_indexes.size()) + +// Column information + +inline const ColumnBase& TableViewBase::get_column_base(size_t index) const +{ + return m_table->get_column_base(index); +} + +inline size_t TableViewBase::get_column_count() const noexcept +{ + REALM_ASSERT(m_table); + return m_table->get_column_count(); +} + +inline StringData TableViewBase::get_column_name(size_t column_ndx) const noexcept +{ + REALM_ASSERT(m_table); + return m_table->get_column_name(column_ndx); +} + +inline size_t TableViewBase::get_column_index(StringData name) const +{ + REALM_ASSERT(m_table); + return m_table->get_column_index(name); +} + +inline DataType TableViewBase::get_column_type(size_t column_ndx) const noexcept +{ + REALM_ASSERT(m_table); + return m_table->get_column_type(column_ndx); +} + + +// Getters + + +inline int64_t TableViewBase::get_int(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_int(column_ndx, to_size_t(real_ndx)); +} + +inline bool TableViewBase::get_bool(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Bool); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_bool(column_ndx, to_size_t(real_ndx)); +} + +inline OldDateTime TableViewBase::get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_OldDateTime); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_olddatetime(column_ndx, to_size_t(real_ndx)); +} + +inline Timestamp TableViewBase::get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Timestamp); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_timestamp(column_ndx, to_size_t(real_ndx)); +} + +inline float TableViewBase::get_float(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Float); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_float(column_ndx, to_size_t(real_ndx)); +} + +inline double TableViewBase::get_double(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Double); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_double(column_ndx, to_size_t(real_ndx)); +} + +inline StringData TableViewBase::get_string(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_String); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_string(column_ndx, to_size_t(real_ndx)); +} + +inline BinaryData TableViewBase::get_binary(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Binary); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_binary(column_ndx, to_size_t(real_ndx)); +} + +inline Mixed TableViewBase::get_mixed(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_mixed(column_ndx, to_size_t(real_ndx)); +} + +inline DataType TableViewBase::get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_mixed_type(column_ndx, to_size_t(real_ndx)); +} + +inline size_t TableViewBase::get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_subtable_size(column_ndx, to_size_t(real_ndx)); +} + +inline size_t TableViewBase::get_link(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_link(column_ndx, to_size_t(real_ndx)); +} + +inline TableRef TableView::get_link_target(size_t column_ndx) noexcept +{ + return m_table->get_link_target(column_ndx); +} + +inline ConstTableRef TableView::get_link_target(size_t column_ndx) const noexcept +{ + return m_table->get_link_target(column_ndx); +} + +inline ConstTableRef ConstTableView::get_link_target(size_t column_ndx) const noexcept +{ + return m_table->get_link_target(column_ndx); +} + +inline bool TableViewBase::is_null_link(size_t column_ndx, size_t row_ndx) const noexcept +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->is_null_link(column_ndx, to_size_t(real_ndx)); +} + + +// Searching + + +inline size_t TableViewBase::find_first_int(size_t column_ndx, int64_t value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int); + return find_first_integer(column_ndx, value); +} + +inline size_t TableViewBase::find_first_bool(size_t column_ndx, bool value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool); + return find_first_integer(column_ndx, value ? 1 : 0); +} + +inline size_t TableViewBase::find_first_olddatetime(size_t column_ndx, OldDateTime value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime); + return find_first_integer(column_ndx, int64_t(value.get_olddatetime())); +} + + +template +R TableViewBase::find_all_integer(V* view, size_t column_ndx, int64_t value) +{ + typedef typename std::remove_const::type TNonConst; + return view->m_table->where(const_cast(view)).equal(column_ndx, value).find_all(); +} + +template +R TableViewBase::find_all_float(V* view, size_t column_ndx, float value) +{ + typedef typename std::remove_const::type TNonConst; + return view->m_table->where(const_cast(view)).equal(column_ndx, value).find_all(); +} + +template +R TableViewBase::find_all_double(V* view, size_t column_ndx, double value) +{ + typedef typename std::remove_const::type TNonConst; + return view->m_table->where(const_cast(view)).equal(column_ndx, value).find_all(); +} + +template +R TableViewBase::find_all_string(V* view, size_t column_ndx, StringData value) +{ + typedef typename std::remove_const::type TNonConst; + return view->m_table->where(const_cast(view)).equal(column_ndx, value).find_all(); +} + + +//-------------------------- TableView, ConstTableView implementation: + +inline ConstTableView::ConstTableView(const TableView& tv) + : TableViewBase(tv) +{ +} + +inline ConstTableView::ConstTableView(TableView&& tv) + : TableViewBase(std::move(tv)) +{ +} + +inline void TableView::remove_last(RemoveMode underlying_mode) +{ + if (!is_empty()) + remove(size() - 1, underlying_mode); +} + +inline Table& TableView::get_parent() noexcept +{ + return *m_table; +} + +inline const Table& TableView::get_parent() const noexcept +{ + return *m_table; +} + +inline const Table& ConstTableView::get_parent() const noexcept +{ + return *m_table; +} + +inline TableView::TableView(Table& parent) + : TableViewBase(&parent) +{ +} + +inline TableView::TableView(Table& parent, Query& query, size_t start, size_t end, size_t limit) + : TableViewBase(&parent, query, start, end, limit) +{ +} + +inline TableView::TableView(Table& parent, ConstLinkViewRef link_view) +: TableViewBase(&parent, std::move(link_view)) +{ +} + +inline TableView::TableView(TableViewBase::DistinctViewTag, Table& parent, size_t column_ndx) + : TableViewBase(TableViewBase::DistinctView, &parent, column_ndx) +{ +} + +inline ConstTableView::ConstTableView(const Table& parent) + : TableViewBase(const_cast(&parent)) +{ +} + +inline ConstTableView& ConstTableView::operator=(const TableView& tv) +{ + TableViewBase::operator=(tv); + return *this; +} + +inline ConstTableView& ConstTableView::operator=(TableView&& tv) +{ + TableViewBase::operator=(std::move(tv)); + return *this; +} + + +// - string +inline TableView TableView::find_all_string(size_t column_ndx, StringData value) +{ + return TableViewBase::find_all_string(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_string(size_t column_ndx, StringData value) const +{ + return TableViewBase::find_all_string(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_string(size_t column_ndx, StringData value) const +{ + return TableViewBase::find_all_string(this, column_ndx, value); +} + +// - float +inline TableView TableView::find_all_float(size_t column_ndx, float value) +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_float(size_t column_ndx, float value) const +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_float(size_t column_ndx, float value) const +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + + +// - double +inline TableView TableView::find_all_double(size_t column_ndx, double value) +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_double(size_t column_ndx, double value) const +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_double(size_t column_ndx, double value) const +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + + +// -- 3 variants of the 3 find_all_{int, bool, date} all based on integer + +inline TableView TableView::find_all_integer(size_t column_ndx, int64_t value) +{ + return TableViewBase::find_all_integer(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_integer(size_t column_ndx, int64_t value) const +{ + return TableViewBase::find_all_integer(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_integer(size_t column_ndx, int64_t value) const +{ + return TableViewBase::find_all_integer(this, column_ndx, value); +} + + +inline TableView TableView::find_all_int(size_t column_ndx, int64_t value) +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int); + return find_all_integer(column_ndx, value); +} + +inline TableView TableView::find_all_bool(size_t column_ndx, bool value) +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool); + return find_all_integer(column_ndx, value ? 1 : 0); +} + +inline TableView TableView::find_all_olddatetime(size_t column_ndx, OldDateTime value) +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime); + return find_all_integer(column_ndx, int64_t(value.get_olddatetime())); +} + + +inline ConstTableView TableView::find_all_int(size_t column_ndx, int64_t value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int); + return find_all_integer(column_ndx, value); +} + +inline ConstTableView TableView::find_all_bool(size_t column_ndx, bool value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool); + return find_all_integer(column_ndx, value ? 1 : 0); +} + +inline ConstTableView TableView::find_all_olddatetime(size_t column_ndx, OldDateTime value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime); + return find_all_integer(column_ndx, int64_t(value.get_olddatetime())); +} + + +inline ConstTableView ConstTableView::find_all_int(size_t column_ndx, int64_t value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int); + return find_all_integer(column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_bool(size_t column_ndx, bool value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool); + return find_all_integer(column_ndx, value ? 1 : 0); +} + +inline ConstTableView ConstTableView::find_all_olddatetime(size_t column_ndx, OldDateTime value) const +{ + REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime); + return find_all_integer(column_ndx, int64_t(value.get_olddatetime())); +} + + +// Rows + + +inline TableView::RowExpr TableView::get(size_t row_ndx) noexcept +{ + REALM_ASSERT_ROW(row_ndx); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get(to_size_t(real_ndx)); +} + +inline TableView::ConstRowExpr TableView::get(size_t row_ndx) const noexcept +{ + REALM_ASSERT_ROW(row_ndx); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get(to_size_t(real_ndx)); +} + +inline ConstTableView::ConstRowExpr ConstTableView::get(size_t row_ndx) const noexcept +{ + REALM_ASSERT_ROW(row_ndx); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get(to_size_t(real_ndx)); +} + +inline TableView::RowExpr TableView::front() noexcept +{ + return get(0); +} + +inline TableView::ConstRowExpr TableView::front() const noexcept +{ + return get(0); +} + +inline ConstTableView::ConstRowExpr ConstTableView::front() const noexcept +{ + return get(0); +} + +inline TableView::RowExpr TableView::back() noexcept +{ + size_t last_row_ndx = size() - 1; + return get(last_row_ndx); +} + +inline TableView::ConstRowExpr TableView::back() const noexcept +{ + size_t last_row_ndx = size() - 1; + return get(last_row_ndx); +} + +inline ConstTableView::ConstRowExpr ConstTableView::back() const noexcept +{ + size_t last_row_ndx = size() - 1; + return get(last_row_ndx); +} + +inline TableView::RowExpr TableView::operator[](size_t row_ndx) noexcept +{ + return get(row_ndx); +} + +inline TableView::ConstRowExpr TableView::operator[](size_t row_ndx) const noexcept +{ + return get(row_ndx); +} + +inline ConstTableView::ConstRowExpr ConstTableView::operator[](size_t row_ndx) const noexcept +{ + return get(row_ndx); +} + + +// Subtables + + +inline TableRef TableView::get_subtable(size_t column_ndx, size_t row_ndx) +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_subtable(column_ndx, to_size_t(real_ndx)); +} + +inline ConstTableRef TableView::get_subtable(size_t column_ndx, size_t row_ndx) const +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_subtable(column_ndx, to_size_t(real_ndx)); +} + +inline ConstTableRef ConstTableView::get_subtable(size_t column_ndx, size_t row_ndx) const +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->get_subtable(column_ndx, to_size_t(real_ndx)); +} + +inline void TableView::clear_subtable(size_t column_ndx, size_t row_ndx) +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + return m_table->clear_subtable(column_ndx, to_size_t(real_ndx)); +} + + +// Setters + + +inline void TableView::set_int(size_t column_ndx, size_t row_ndx, int64_t value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Int); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_int(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_bool(size_t column_ndx, size_t row_ndx, bool value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Bool); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_bool(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_OldDateTime); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_olddatetime(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Timestamp); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_timestamp(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_float(size_t column_ndx, size_t row_ndx, float value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Float); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_float(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_double(size_t column_ndx, size_t row_ndx, double value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Double); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_double(column_ndx, to_size_t(real_ndx), value); +} + +template +inline void TableView::set_enum(size_t column_ndx, size_t row_ndx, E value) +{ + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_int(column_ndx, real_ndx, value); +} + +inline void TableView::set_string(size_t column_ndx, size_t row_ndx, StringData value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_String); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_string(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_binary(size_t column_ndx, size_t row_ndx, BinaryData value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Binary); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_binary(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_mixed(size_t column_ndx, size_t row_ndx, Mixed value) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed); + + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_mixed(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_subtable(size_t column_ndx, size_t row_ndx, const Table* value) +{ + REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_subtable(column_ndx, to_size_t(real_ndx), value); +} + +inline void TableView::set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->set_link(column_ndx, to_size_t(real_ndx), target_row_ndx); +} + +inline void TableView::nullify_link(size_t column_ndx, size_t row_ndx) +{ + REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link); + const int64_t real_ndx = m_row_indexes.get(row_ndx); + REALM_ASSERT(real_ndx != detached_ref); + m_table->nullify_link(column_ndx, to_size_t(real_ndx)); +} + +} // namespace realm + +#endif // REALM_TABLE_VIEW_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/timestamp.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/timestamp.hpp new file mode 100644 index 0000000..e5046d9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/timestamp.hpp @@ -0,0 +1,166 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_TIMESTAMP_HPP +#define REALM_TIMESTAMP_HPP + +#include +#include +#include + +namespace realm { + +class Timestamp { +public: + // Construct from the number of seconds and nanoseconds since the UNIX epoch: 00:00:00 UTC on 1 January 1970 + // + // To split a native nanosecond representation, only division and modulo are necessary: + // + // s = native_nano / nanoseconds_per_second + // n = native_nano % nanoseconds_per_second + // Timestamp ts(s, n); + // + // To convert back into native nanosecond representation, simple multiply and add: + // + // native_nano = ts.s * nanoseconds_per_second + ts.n + // + // Specifically this allows the nanosecond part to become negative (only) for Timestamps before the UNIX epoch. + // Usually this will not need special attention, but for reference, valid Timestamps will have one of the + // following sign combinations: + // + // s | n + // ----- + // + | + + // + | 0 + // 0 | + + // 0 | 0 + // 0 | - + // - | 0 + // - | - + // + // Examples: + // The UNIX epoch is constructed by Timestamp(0, 0) + // Relative times are constructed as follows: + // +1 second is constructed by Timestamp(1, 0) + // +1 nanosecond is constructed by Timestamp(0, 1) + // +1.1 seconds (1100 milliseconds after the epoch) is constructed by Timestamp(1, 100000000) + // -1.1 seconds (1100 milliseconds before the epoch) is constructed by Timestamp(-1, -100000000) + // + Timestamp(int64_t seconds, int32_t nanoseconds) + : m_seconds(seconds) + , m_nanoseconds(nanoseconds) + , m_is_null(false) + { + REALM_ASSERT_EX(-nanoseconds_per_second < nanoseconds && nanoseconds < nanoseconds_per_second, nanoseconds); + const bool both_non_negative = seconds >= 0 && nanoseconds >= 0; + const bool both_non_positive = seconds <= 0 && nanoseconds <= 0; + REALM_ASSERT_EX(both_non_negative || both_non_positive, both_non_negative, both_non_positive); + } + Timestamp(realm::null) + : m_is_null(true) + { + } + Timestamp() + : Timestamp(null{}) + { + } + + bool is_null() const + { + return m_is_null; + } + + int64_t get_seconds() const noexcept + { + REALM_ASSERT(!m_is_null); + return m_seconds; + } + + int32_t get_nanoseconds() const noexcept + { + REALM_ASSERT(!m_is_null); + return m_nanoseconds; + } + + // Note that only == and != operators work if one of the Timestamps are null! Else use realm::Greater, + // realm::Less, etc, instead. This is in order to collect all treatment of null behaviour in a single place for all + // types (query_conditions.hpp) to ensure that all types sort and compare null vs. non-null in the same manner, + // especially for int/float where we cannot override operators. This design is open for discussion, though, + // because it has usability drawbacks + bool operator==(const Timestamp& rhs) const + { + if (is_null() && rhs.is_null()) + return true; + + if (is_null() != rhs.is_null()) + return false; + + return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds; + } + bool operator!=(const Timestamp& rhs) const + { + return !(*this == rhs); + } + bool operator>(const Timestamp& rhs) const + { + REALM_ASSERT(!is_null()); + REALM_ASSERT(!rhs.is_null()); + return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds); + } + bool operator<(const Timestamp& rhs) const + { + REALM_ASSERT(!is_null()); + REALM_ASSERT(!rhs.is_null()); + return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds); + } + bool operator<=(const Timestamp& rhs) const + { + REALM_ASSERT(!is_null()); + REALM_ASSERT(!rhs.is_null()); + return *this < rhs || *this == rhs; + } + bool operator>=(const Timestamp& rhs) const + { + REALM_ASSERT(!is_null()); + REALM_ASSERT(!rhs.is_null()); + return *this > rhs || *this == rhs; + } + Timestamp& operator=(const Timestamp& rhs) = default; + + template + friend std::basic_ostream& operator<<(std::basic_ostream& out, const Timestamp&); + static constexpr int32_t nanoseconds_per_second = 1000000000; + +private: + int64_t m_seconds; + int32_t m_nanoseconds; + bool m_is_null; +}; + +// LCOV_EXCL_START +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const Timestamp& d) +{ + out << "Timestamp(" << d.m_seconds << ", " << d.m_nanoseconds << ")"; + return out; +} +// LCOV_EXCL_STOP + +} // namespace realm + +#endif // REALM_TIMESTAMP_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/unicode.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/unicode.hpp new file mode 100644 index 0000000..55bd8cc --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/unicode.hpp @@ -0,0 +1,175 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UNICODE_HPP +#define REALM_UNICODE_HPP + +#include +#include +#include + +#include +#include +#include + + +namespace realm { + +enum string_compare_method_t { + STRING_COMPARE_CORE, + STRING_COMPARE_CPP11, + STRING_COMPARE_CALLBACK, + STRING_COMPARE_CORE_SIMILAR +}; + +extern StringCompareCallback string_compare_callback; +extern string_compare_method_t string_compare_method; + +// Description for set_string_compare_method(): +// +// Short summary: iOS language binding: call +// set_string_compare_method() for fast but slightly inaccurate sort in some countries, or +// set_string_compare_method(2, callbackptr) for slow but precise sort (see callbackptr below) +// +// Different countries ('locales') have different sorting order for strings and letters. Because there unfortunatly +// doesn't exist any unified standardized way to compare strings in C++ on multiple platforms, we need this method. +// +// It determins how sorting a TableView by a String column must take place. The 'method' argument can be: +// +// 0: Fast core-only compare (no OS/framework calls). LIMITATIONS: Works only upto 'Latin Extended 2' (unicodes +// 0...591). Also, sorting order is according to 'en_US' so it may be slightly inaccurate for some countries. +// 'callback' argument is ignored. +// +// Return value: Always 'true' +// +// 1: Native C++11 method if core is compiled as C++11. Gives precise sorting according +// to user's current locale. LIMITATIONS: Currently works only on Windows and on Linux with clang. Does NOT work on +// iOS (due to only 'C' locale being available in CoreFoundation, which puts 'Z' before 'a'). Unknown if works on +// Windows Phone / Android. Furthermore it does NOT work on Linux with gcc 4.7 or 4.8 (lack of c++11 feature that +// can convert utf8->wstring without calls to setlocale()). +// +// Return value: 'true' if supported, otherwise 'false' (if so, then previous setting, if any, is preserved). +// +// 2: Callback method. Language binding / C++ user must provide a utf-8 callback method of prototype: +// bool callback(const char* string1, const char* string2) where 'callback' must return bool(string1 < string2). +// +// Return value: Always 'true' +// +// Default is method = 0 if the function is never called +// +// NOT THREAD SAFE! Call once during initialization or make sure it's not called simultaneously with different +// arguments. The setting is remembered per-process; it does NOT need to be called prior to each sort +bool set_string_compare_method(string_compare_method_t method, StringCompareCallback callback); + + +// Return size in bytes of utf8 character. No error checking +size_t sequence_length(char lead); + +// Limitations for case insensitive string search +// Case insensitive search (equal, begins_with, ends_with, like and contains) +// only works for unicodes 0...0x7f which is the same as the 0...127 +// ASCII character set (letters a-z and A-Z). + +// In does *not* work for the 0...255 ANSI character set that contains +// characters from many European countries like Germany, France, Denmark, +// etc. + +// It also does not work for characters from non-western countries like +// Japan, Russia, Arabia, etc. + +// If there exists characters outside the ASCII range either in the text +// to be searched for, or in the Realm string column which is searched +// in, then the compare yields a random result such that the row may or +// may not be included in the result set. + +// Return bool(string1 < string2) +bool utf8_compare(StringData string1, StringData string2); + +// Return unicode value of character. +uint32_t utf8value(const char* character); + +inline bool equal_sequence(const char*& begin, const char* end, const char* begin2); + +// FIXME: The current approach to case insensitive comparison requires +// that case mappings can be done in a way that does not change he +// number of bytes used to encode the individual Unicode +// character. This is not generally the case, so, as far as I can see, +// this approach has no future. +// +// FIXME: The current approach to case insensitive comparison relies +// on checking each "haystack" character against the corresponding +// character in both a lower cased and an upper cased version of the +// "needle". While this leads to efficient comparison, it ignores the +// fact that "case folding" is the only correct approach to case +// insensitive comparison in a locale agnostic Unicode +// environment. +// +// See +// http://www.w3.org/International/wiki/Case_folding +// http://userguide.icu-project.org/transforms/casemappings#TOC-Case-Folding. +// +// The ideal API would probably be something like this: +// +// case_fold: utf_8 -> case_folded +// equal_case_fold: (needle_case_folded, single_haystack_entry_utf_8) -> found +// search_case_fold: (needle_case_folded, huge_haystack_string_utf_8) -> found_at_position +// +// The case folded form would probably be using UTF-32 or UTF-16. + + +/// If successful, returns a string of the same size as \a source. +/// Returns none if invalid UTF-8 encoding was encountered. +util::Optional case_map(StringData source, bool upper); + +#if REALM_UWP +// Converts unicodes 0...last_greek_unicode to their respective lower case characters using a popular +// UnicodeData.txtfile (http://www.opensource.apple.com/source/Heimdal/Heimdal-247.9/lib/wind/UnicodeData.txt) that +// contains case conversion information. The conversion does not take your current locale in count; it can be +// slightly wrong in some countries! If the input is already lower case, or outside range 0...last_greek_unicode, then input value +// is returned untouched. +// +// The method is called from case_map() on Windows 10 UWP only, because CharLowerW() / CharUpperW() is not supported +// on early Windows 10 devices (it's supported on newer versions though). +unsigned int unicode_case_convert(unsigned int unicode, bool upper); +#endif + +enum IgnoreErrorsTag { IgnoreErrors }; +std::string case_map(StringData source, bool upper, IgnoreErrorsTag); + +/// Assumes that the sizes of \a needle_upper and \a needle_lower are +/// identical to the size of \a haystack. Returns false if the needle +/// is different from the haystack. +bool equal_case_fold(StringData haystack, const char* needle_upper, const char* needle_lower); + +/// Assumes that the sizes of \a needle_upper and \a needle_lower are +/// both equal to \a needle_size. Returns haystack.size() if the +/// needle was not found. +size_t search_case_fold(StringData haystack, const char* needle_upper, const char* needle_lower, size_t needle_size); + +/// Assumes that the sizes of \a needle_upper and \a needle_lower are +/// both equal to \a needle_size. Returns false if the +/// needle was not found. +bool contains_ins(StringData haystack, const char* needle_upper, const char* needle_lower, size_t needle_size, const std::array &charmap); + +/// Case insensitive wildcard matching ('?' for single char, '*' for zero or more chars) +bool string_like_ins(StringData text, StringData pattern) noexcept; +bool string_like_ins(StringData text, StringData upper, StringData lower) noexcept; + +} // namespace realm + +#endif // REALM_UNICODE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/assert.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/assert.hpp new file mode 100644 index 0000000..5e75066 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/assert.hpp @@ -0,0 +1,107 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_ASSERT_HPP +#define REALM_UTIL_ASSERT_HPP + +#include +#include + +#if REALM_ENABLE_ASSERTIONS || defined(REALM_DEBUG) +#define REALM_ASSERTIONS_ENABLED 1 +#else +#define REALM_ASSERTIONS_ENABLED 0 +#endif + +#define REALM_ASSERT_RELEASE(condition) \ + (REALM_LIKELY(condition) ? static_cast(0) \ + : realm::util::terminate("Assertion failed: " #condition, __FILE__, __LINE__)) + +#if REALM_ASSERTIONS_ENABLED +#define REALM_ASSERT(condition) REALM_ASSERT_RELEASE(condition) +#else +#define REALM_ASSERT(condition) static_cast(sizeof bool(condition)) +#endif + +#ifdef REALM_DEBUG +#define REALM_ASSERT_DEBUG(condition) REALM_ASSERT_RELEASE(condition) +#else +#define REALM_ASSERT_DEBUG(condition) static_cast(sizeof bool(condition)) +#endif + +#define REALM_STRINGIFY(X) #X + +#define REALM_ASSERT_RELEASE_EX(condition, ...) \ + (REALM_LIKELY(condition) ? static_cast(0) \ + : realm::util::terminate_with_info("Assertion failed: " #condition, __LINE__, __FILE__, \ + REALM_STRINGIFY((__VA_ARGS__)), __VA_ARGS__)) + +#ifdef REALM_DEBUG +#define REALM_ASSERT_DEBUG_EX REALM_ASSERT_RELEASE_EX +#else +#define REALM_ASSERT_DEBUG_EX(condition, ...) static_cast(sizeof bool(condition)) +#endif + +// Becase the assert is used in noexcept methods, it's a bad idea to allocate +// buffer space for the message so therefore we must pass it to terminate which +// will 'cerr' it for us without needing any buffer +#if REALM_ENABLE_ASSERTIONS || defined(REALM_DEBUG) + +#define REALM_ASSERT_EX REALM_ASSERT_RELEASE_EX + +#define REALM_ASSERT_3(left, cmp, right) \ + (REALM_LIKELY((left)cmp(right)) ? static_cast(0) \ + : realm::util::terminate("Assertion failed: " \ + "" #left " " #cmp " " #right, \ + __FILE__, __LINE__, left, right)) + +#define REALM_ASSERT_7(left1, cmp1, right1, logical, left2, cmp2, right2) \ + (REALM_LIKELY(((left1)cmp1(right1))logical((left2)cmp2(right2))) \ + ? static_cast(0) \ + : realm::util::terminate("Assertion failed: " \ + "" #left1 " " #cmp1 " " #right1 " " #logical " " \ + "" #left2 " " #cmp2 " " #right2, \ + __FILE__, __LINE__, left1, right1, left2, right2)) + +#define REALM_ASSERT_11(left1, cmp1, right1, logical1, left2, cmp2, right2, logical2, left3, cmp3, right3) \ + (REALM_LIKELY(((left1)cmp1(right1))logical1((left2)cmp2(right2)) logical2((left3)cmp3(right3))) \ + ? static_cast(0) \ + : realm::util::terminate("Assertion failed: " \ + "" #left1 " " #cmp1 " " #right1 " " #logical1 " " \ + "" #left2 " " #cmp2 " " #right2 " " #logical2 " " \ + "" #left3 " " #cmp3 " " #right3, \ + __FILE__, __LINE__, left1, right1, left2, right2, left3, right3)) +#else +#define REALM_ASSERT_EX(condition, ...) static_cast(sizeof bool(condition)) +#define REALM_ASSERT_3(left, cmp, right) static_cast(sizeof bool((left)cmp(right))) +#define REALM_ASSERT_7(left1, cmp1, right1, logical, left2, cmp2, right2) \ + static_cast(sizeof bool(((left1)cmp1(right1))logical((left2)cmp2(right2)))) +#define REALM_ASSERT_11(left1, cmp1, right1, logical1, left2, cmp2, right2, logical2, left3, cmp3, right3) \ + static_cast(sizeof bool(((left1)cmp1(right1))logical1((left2)cmp2(right2)) logical2((left3)cmp3(right3)))) +#endif + +#define REALM_UNREACHABLE() realm::util::terminate("Unreachable code", __FILE__, __LINE__) +#ifdef REALM_COVER +#define REALM_COVER_NEVER(x) false +#define REALM_COVER_ALWAYS(x) true +#else +#define REALM_COVER_NEVER(x) (x) +#define REALM_COVER_ALWAYS(x) (x) +#endif + +#endif // REALM_UTIL_ASSERT_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/base64.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/base64.hpp new file mode 100644 index 0000000..946fffd --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/base64.hpp @@ -0,0 +1,73 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BASE64_HPP +#define REALM_UTIL_BASE64_HPP + +#include +#include + +namespace realm { +namespace util { + + +/// base64_encode() encodes the bnary data in \param in_buffer of size \param in_buffer_size. +/// The encoded data is placed in \param out_buffer. The size of \param \out_buffer is passed in +/// \param out_buffer_size. The output buffer \param out_buffer must be +/// large enough to hold the base64 encoded data. The size can be obtained from the function +/// base64_encoded_size. \param out_buffer_size is only used to assert that the output buffer is +/// large enough. +size_t base64_encode(const char *in_buffer, size_t in_buffer_size, char* out_buffer, size_t out_buffer_size) noexcept; + +/// base64_encoded_size() returns the exact size of the base64 encoded +/// data as a function of the size of the input data. +inline size_t base64_encoded_size(size_t in_buffer_size) noexcept +{ + return 4 * ((in_buffer_size + 2) / 3); +} + + +/// Decode base64-encoded string in input, and places the result in out_buffer. +/// The length of the out_buffer must be at least 3 * input.size() / 4. +/// +/// The input must be padded base64 (i.e. the number of non-whitespace +/// characters in the input must be a multiple of 4). Whitespace (spaces, tabs, +/// newlines) is ignored. +/// +/// The algorithm stops when the first character not in the base64 character +/// set is encountered, or when the end of the input is reached. +/// +/// \returns the number of successfully decoded bytes written to out_buffer, or +/// none if the whole input was not valid base64. +Optional base64_decode(StringData input, char* out_buffer, size_t out_buffer_len) noexcept; + +/// Return an upper bound on the decoded size of a Base64-encoded data +/// stream of length \a base64_size. The returned value is suitable for +/// allocation of buffers containing decoded data. +inline size_t base64_decoded_size(size_t base64_size) noexcept +{ + return (base64_size * 3 + 3) / 4; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BASE64_HPP + diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp new file mode 100644 index 0000000..8f7a626 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp @@ -0,0 +1,89 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BASIC_SYSTEM_ERRORS_HPP +#define REALM_UTIL_BASIC_SYSTEM_ERRORS_HPP + +#include +#include + + +namespace realm { +namespace util { +namespace error { + +enum basic_system_errors { + /// Address family not supported by protocol. + address_family_not_supported = EAFNOSUPPORT, + + /// Invalid argument. + invalid_argument = EINVAL, + + /// Cannot allocate memory. + no_memory = ENOMEM, + + /// Operation cancelled. + operation_aborted = ECANCELED, + + /// Connection aborted. + connection_aborted = ECONNABORTED, + + /// Connection reset by peer + connection_reset = ECONNRESET, + + /// Broken pipe + broken_pipe = EPIPE, + + /// Resource temporarily unavailable + resource_unavailable_try_again = EAGAIN, +}; + +std::error_code make_error_code(basic_system_errors) noexcept; + +} // namespace error +} // namespace util +} // namespace realm + +namespace std { + +template <> +class is_error_code_enum { +public: + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace util { + +std::error_code make_basic_system_error_code(int) noexcept; + + +// implementation + +inline std::error_code make_basic_system_error_code(int err) noexcept +{ + using namespace error; + return make_error_code(basic_system_errors(err)); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BASIC_SYSTEM_ERRORS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp new file mode 100644 index 0000000..e5b8a99 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp @@ -0,0 +1,484 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BIND_PTR_HPP +#define REALM_UTIL_BIND_PTR_HPP + +#include +#include +#include +#include + +#include +#include + + +namespace realm { +namespace util { + +class bind_ptr_base { +public: + struct adopt_tag { + }; +}; + + +/// A generic intrusive smart pointer that binds itself explicitely to +/// the target object. +/// +/// This class is agnostic towards what 'binding' means for the target +/// object, but a common use is 'reference counting'. See RefCountBase +/// for an example of that. +/// +/// This smart pointer implementation assumes that the target object +/// destructor never throws. +template +class bind_ptr : public bind_ptr_base { +public: + constexpr bind_ptr() noexcept + : m_ptr(nullptr) + { + } + ~bind_ptr() noexcept + { + unbind(); + } + + explicit bind_ptr(T* p) noexcept + { + bind(p); + } + template + explicit bind_ptr(U* p) noexcept + { + bind(p); + } + + bind_ptr(T* p, adopt_tag) noexcept + { + m_ptr = p; + } + template + bind_ptr(U* p, adopt_tag) noexcept + { + m_ptr = p; + } + + // Copy construct + bind_ptr(const bind_ptr& p) noexcept + { + bind(p.m_ptr); + } + template + bind_ptr(const bind_ptr& p) noexcept + { + bind(p.m_ptr); + } + + // Copy assign + bind_ptr& operator=(const bind_ptr& p) noexcept + { + bind_ptr(p).swap(*this); + return *this; + } + template + bind_ptr& operator=(const bind_ptr& p) noexcept + { + bind_ptr(p).swap(*this); + return *this; + } + + // Move construct + bind_ptr(bind_ptr&& p) noexcept + : m_ptr(p.release()) + { + } + template + bind_ptr(bind_ptr&& p) noexcept + : m_ptr(p.release()) + { + } + + // Move assign + bind_ptr& operator=(bind_ptr&& p) noexcept + { + bind_ptr(std::move(p)).swap(*this); + return *this; + } + template + bind_ptr& operator=(bind_ptr&& p) noexcept + { + bind_ptr(std::move(p)).swap(*this); + return *this; + } + + //@{ + // Comparison + template + bool operator==(const bind_ptr&) const noexcept; + + template + bool operator==(U*) const noexcept; + + template + bool operator!=(const bind_ptr&) const noexcept; + + template + bool operator!=(U*) const noexcept; + + template + bool operator<(const bind_ptr&) const noexcept; + + template + bool operator<(U*) const noexcept; + + template + bool operator>(const bind_ptr&) const noexcept; + + template + bool operator>(U*) const noexcept; + + template + bool operator<=(const bind_ptr&) const noexcept; + + template + bool operator<=(U*) const noexcept; + + template + bool operator>=(const bind_ptr&) const noexcept; + + template + bool operator>=(U*) const noexcept; + //@} + + // Dereference + T& operator*() const noexcept + { + return *m_ptr; + } + T* operator->() const noexcept + { + return m_ptr; + } + + explicit operator bool() const noexcept + { + return m_ptr != 0; + } + + T* get() const noexcept + { + return m_ptr; + } + void reset() noexcept + { + bind_ptr().swap(*this); + } + void reset(T* p) noexcept + { + bind_ptr(p).swap(*this); + } + template + void reset(U* p) noexcept + { + bind_ptr(p).swap(*this); + } + + T* release() noexcept + { + T* const p = m_ptr; + m_ptr = nullptr; + return p; + } + + void swap(bind_ptr& p) noexcept + { + std::swap(m_ptr, p.m_ptr); + } + friend void swap(bind_ptr& a, bind_ptr& b) noexcept + { + a.swap(b); + } + +protected: + struct casting_move_tag { + }; + template + bind_ptr(bind_ptr* p, casting_move_tag) noexcept + : m_ptr(static_cast(p->release())) + { + } + +private: + T* m_ptr; + + void bind(T* p) noexcept + { + if (p) + p->bind_ptr(); + m_ptr = p; + } + void unbind() noexcept + { + if (m_ptr) + m_ptr->unbind_ptr(); + } + + template + friend class bind_ptr; +}; + + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const bind_ptr& p) +{ + out << static_cast(p.get()); + return out; +} + + +//@{ +// Comparison +template +bool operator==(T*, const bind_ptr&) noexcept; +template +bool operator!=(T*, const bind_ptr&) noexcept; +template +bool operator<(T*, const bind_ptr&) noexcept; +template +bool operator>(T*, const bind_ptr&) noexcept; +template +bool operator<=(T*, const bind_ptr&) noexcept; +template +bool operator>=(T*, const bind_ptr&) noexcept; +//@} + + +/// Polymorphic convenience base class for reference counting objects. +/// +/// Together with bind_ptr, this class delivers simple instrusive +/// reference counting. +/// +/// \sa bind_ptr +class RefCountBase { +public: + RefCountBase() noexcept + : m_ref_count(0) + { + } + virtual ~RefCountBase() noexcept + { + REALM_ASSERT(m_ref_count == 0); + } + + RefCountBase(const RefCountBase&) = delete; + RefCountBase(RefCountBase&&) = delete; + + void operator=(const RefCountBase&) = delete; + void operator=(RefCountBase&&) = delete; + +protected: + void bind_ptr() const noexcept + { + ++m_ref_count; + } + void unbind_ptr() const noexcept + { + if (--m_ref_count == 0) + delete this; + } + +private: + mutable unsigned long m_ref_count; + + template + friend class bind_ptr; +}; + + +/// Same as RefCountBase, but this one makes the copying of, and the +/// destruction of counted references thread-safe. +/// +/// \sa RefCountBase +/// \sa bind_ptr +class AtomicRefCountBase { +public: + AtomicRefCountBase() noexcept + : m_ref_count(0) + { + } + virtual ~AtomicRefCountBase() noexcept + { + REALM_ASSERT(m_ref_count == 0); + } + + AtomicRefCountBase(const AtomicRefCountBase&) = delete; + AtomicRefCountBase(AtomicRefCountBase&&) = delete; + + void operator=(const AtomicRefCountBase&) = delete; + void operator=(AtomicRefCountBase&&) = delete; + +protected: + // FIXME: Operators ++ and -- as used below use + // std::memory_order_seq_cst. This can be optimized. + void bind_ptr() const noexcept + { + ++m_ref_count; + } + void unbind_ptr() const noexcept + { + if (--m_ref_count == 0) { + delete this; + } + } + +private: + mutable std::atomic m_ref_count; + + template + friend class bind_ptr; +}; + + +// Implementation: + +template +template +bool bind_ptr::operator==(const bind_ptr& p) const noexcept +{ + return m_ptr == p.m_ptr; +} + +template +template +bool bind_ptr::operator==(U* p) const noexcept +{ + return m_ptr == p; +} + +template +template +bool bind_ptr::operator!=(const bind_ptr& p) const noexcept +{ + return m_ptr != p.m_ptr; +} + +template +template +bool bind_ptr::operator!=(U* p) const noexcept +{ + return m_ptr != p; +} + +template +template +bool bind_ptr::operator<(const bind_ptr& p) const noexcept +{ + return m_ptr < p.m_ptr; +} + +template +template +bool bind_ptr::operator<(U* p) const noexcept +{ + return m_ptr < p; +} + +template +template +bool bind_ptr::operator>(const bind_ptr& p) const noexcept +{ + return m_ptr > p.m_ptr; +} + +template +template +bool bind_ptr::operator>(U* p) const noexcept +{ + return m_ptr > p; +} + +template +template +bool bind_ptr::operator<=(const bind_ptr& p) const noexcept +{ + return m_ptr <= p.m_ptr; +} + +template +template +bool bind_ptr::operator<=(U* p) const noexcept +{ + return m_ptr <= p; +} + +template +template +bool bind_ptr::operator>=(const bind_ptr& p) const noexcept +{ + return m_ptr >= p.m_ptr; +} + +template +template +bool bind_ptr::operator>=(U* p) const noexcept +{ + return m_ptr >= p; +} + +template +bool operator==(T* a, const bind_ptr& b) noexcept +{ + return b == a; +} + +template +bool operator!=(T* a, const bind_ptr& b) noexcept +{ + return b != a; +} + +template +bool operator<(T* a, const bind_ptr& b) noexcept +{ + return b > a; +} + +template +bool operator>(T* a, const bind_ptr& b) noexcept +{ + return b < a; +} + +template +bool operator<=(T* a, const bind_ptr& b) noexcept +{ + return b >= a; +} + +template +bool operator>=(T* a, const bind_ptr& b) noexcept +{ + return b <= a; +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BIND_PTR_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer.hpp new file mode 100644 index 0000000..024510e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer.hpp @@ -0,0 +1,301 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BUFFER_HPP +#define REALM_UTIL_BUFFER_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { +namespace util { + + +/// A simple buffer concept that owns a region of memory and knows its +/// size. +template +class Buffer { +public: + Buffer() noexcept + : m_size(0) + { + } + explicit Buffer(size_t initial_size); + Buffer(Buffer&&) noexcept = default; + ~Buffer() noexcept + { + } + + Buffer& operator=(Buffer&&) noexcept = default; + + T& operator[](size_t i) noexcept + { + return m_data[i]; + } + const T& operator[](size_t i) const noexcept + { + return m_data[i]; + } + + T* data() noexcept + { + return m_data.get(); + } + const T* data() const noexcept + { + return m_data.get(); + } + size_t size() const noexcept + { + return m_size; + } + + /// False iff the data() returns null. + explicit operator bool() const noexcept + { + return bool(m_data); + } + + /// Discards the original contents. + void set_size(size_t new_size); + + /// \param new_size Specifies the new buffer size. + /// \param copy_begin, copy_end Specifies a range of element + /// values to be retained. \a copy_end must be less than, or equal + /// to size(). + /// + /// \param copy_to Specifies where the retained range should be + /// copied to. `\a copy_to + \a copy_end - \a copy_begin` must be + /// less than, or equal to \a new_size. + void resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to); + + void reserve(size_t used_size, size_t min_capacity); + + void reserve_extra(size_t used_size, size_t min_extra_capacity); + + T* release() noexcept; + + friend void swap(Buffer& a, Buffer& b) noexcept + { + using std::swap; + swap(a.m_data, b.m_data); + swap(a.m_size, b.m_size); + } + +private: + std::unique_ptr m_data; + size_t m_size; +}; + + +/// A buffer that can be efficiently resized. It acheives this by +/// using an underlying buffer that may be larger than the logical +/// size, and is automatically expanded in progressively larger steps. +template +class AppendBuffer { +public: + AppendBuffer() noexcept; + ~AppendBuffer() noexcept + { + } + + AppendBuffer(AppendBuffer&&) noexcept = default; + AppendBuffer& operator=(AppendBuffer&&) noexcept = default; + + /// Returns the current size of the buffer. + size_t size() const noexcept; + + /// Gives read and write access to the elements. + T* data() noexcept; + + /// Gives read access the elements. + const T* data() const noexcept; + + /// Append the specified elements. This increases the size of this + /// buffer by \a append_data_size. If the caller has previously requested + /// a minimum capacity that is greater than, or equal to the + /// resulting size, this function is guaranteed to not throw. + void append(const T* append_data, size_t append_data_size); + + /// If the specified size is less than the current size, then the + /// buffer contents is truncated accordingly. If the specified + /// size is greater than the current size, then the extra elements + /// will have undefined values. If the caller has previously + /// requested a minimum capacity that is greater than, or equal to + /// the specified size, this function is guaranteed to not throw. + void resize(size_t new_size); + + /// This operation does not change the size of the buffer as + /// returned by size(). If the specified capacity is less than the + /// current capacity, this operation has no effect. + void reserve(size_t min_capacity); + + /// Set the size to zero. The capacity remains unchanged. + void clear() noexcept; + + /// Release the underlying buffer and reset the size. Note: The returned + /// buffer may be larger than the amount of data appended to this buffer. + /// Callers should call `size()` prior to releasing the buffer to know the + /// usable/logical size. + Buffer release() noexcept; + +private: + util::Buffer m_buffer; + size_t m_size; +}; + + +// Implementation: + +class BufferSizeOverflow : public std::exception { +public: + const char* what() const noexcept override + { + return "Buffer size overflow"; + } +}; + +template +inline Buffer::Buffer(size_t initial_size) + : m_data(new T[initial_size]) // Throws + , m_size(initial_size) +{ +} + +template +inline void Buffer::set_size(size_t new_size) +{ + m_data.reset(new T[new_size]); // Throws + m_size = new_size; +} + +template +inline void Buffer::resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to) +{ + std::unique_ptr new_data(new T[new_size]); // Throws + realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to); + m_data.reset(new_data.release()); + m_size = new_size; +} + +template +inline void Buffer::reserve(size_t used_size, size_t min_capacity) +{ + size_t current_capacity = m_size; + if (REALM_LIKELY(current_capacity >= min_capacity)) + return; + size_t new_capacity = current_capacity; + + // Use growth factor 1.5. + if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_capacity, 3))) + new_capacity = std::numeric_limits::max(); + new_capacity /= 2; + + if (REALM_UNLIKELY(new_capacity < min_capacity)) + new_capacity = min_capacity; + resize(new_capacity, 0, used_size, 0); // Throws +} + +template +inline void Buffer::reserve_extra(size_t used_size, size_t min_extra_capacity) +{ + size_t min_capacity = used_size; + if (REALM_UNLIKELY(int_add_with_overflow_detect(min_capacity, min_extra_capacity))) + throw BufferSizeOverflow(); + reserve(used_size, min_capacity); // Throws +} + +template +inline T* Buffer::release() noexcept +{ + m_size = 0; + return m_data.release(); +} + + +template +inline AppendBuffer::AppendBuffer() noexcept + : m_size(0) +{ +} + +template +inline size_t AppendBuffer::size() const noexcept +{ + return m_size; +} + +template +inline T* AppendBuffer::data() noexcept +{ + return m_buffer.data(); +} + +template +inline const T* AppendBuffer::data() const noexcept +{ + return m_buffer.data(); +} + +template +inline void AppendBuffer::append(const T* append_data, size_t append_data_size) +{ + m_buffer.reserve_extra(m_size, append_data_size); // Throws + realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size); + m_size += append_data_size; +} + +template +inline void AppendBuffer::reserve(size_t min_capacity) +{ + m_buffer.reserve(m_size, min_capacity); +} + +template +inline void AppendBuffer::resize(size_t new_size) +{ + reserve(new_size); + m_size = new_size; +} + +template +inline void AppendBuffer::clear() noexcept +{ + m_size = 0; +} + +template +inline Buffer AppendBuffer::release() noexcept +{ + m_size = 0; + return std::move(m_buffer); +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BUFFER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp new file mode 100644 index 0000000..65feb8b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp @@ -0,0 +1,146 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BUFFER_STREAM_HPP +#define REALM_UTIL_BUFFER_STREAM_HPP + +#include + +namespace realm { +namespace util { + + +template, class A = std::allocator > +class BasicResettableExpandableOutputStreambuf: public std::basic_stringbuf { +public: + using char_type = typename std::basic_stringbuf::char_type; + + /// Reset current writing position (std::basic_streambuf::pptr()) to the + /// beginning of the output buffer without reallocating buffer memory. + void reset() noexcept; + + /// Get a pointer to the beginning of the output buffer + /// (std::basic_streambuf::pbase()). Note that this will change as the + /// buffer is reallocated. + char_type* data() noexcept; + const char_type* data() const noexcept; + + /// Get the number of bytes written to the output buffer since the creation + /// of the stream buffer, or since the last invocation of reset() + /// (std::basic_streambuf::pptr() - std::basic_streambuf::pbase()). + std::streamsize size() const noexcept; +}; + + +template, class A = std::allocator > +class BasicResettableExpandableBufferOutputStream: public std::basic_ostream { +public: + using char_type = typename std::basic_ostream::char_type; + + BasicResettableExpandableBufferOutputStream(); + + /// Calls BasicResettableExpandableOutputStreambuf::reset(). + void reset() noexcept; + + /// Calls BasicResettableExpandableOutputStreambuf::data(). + char_type* data() noexcept; + const char_type* data() const noexcept; + + /// Calls BasicResettableExpandableOutputStreambuf::size(). + std::streamsize size() const noexcept; + +private: + BasicResettableExpandableOutputStreambuf m_streambuf; +}; + + +using ResettableExpandableBufferOutputStream = BasicResettableExpandableBufferOutputStream; + + + + +// Implementation + +template +inline void BasicResettableExpandableOutputStreambuf::reset() noexcept +{ + char_type* pbeg = this->pbase(); + char_type* pend = this->epptr(); + this->setp(pbeg, pend); +} + +template +inline typename BasicResettableExpandableOutputStreambuf::char_type* +BasicResettableExpandableOutputStreambuf::data() noexcept +{ + return this->pbase(); +} + +template +inline const typename BasicResettableExpandableOutputStreambuf::char_type* +BasicResettableExpandableOutputStreambuf::data() const noexcept +{ + return this->pbase(); +} + +template +inline std::streamsize BasicResettableExpandableOutputStreambuf::size() const noexcept +{ + std::streamsize s = std::streamsize(this->pptr() - this->pbase()); + return s; +} + +template +inline BasicResettableExpandableBufferOutputStream:: +BasicResettableExpandableBufferOutputStream(): + std::basic_ostream(&m_streambuf) // Throws +{ +} + +template +inline void BasicResettableExpandableBufferOutputStream::reset() noexcept +{ + m_streambuf.reset(); +} + +template +inline typename BasicResettableExpandableBufferOutputStream::char_type* +BasicResettableExpandableBufferOutputStream::data() noexcept +{ + return m_streambuf.data(); +} + +template +inline const typename BasicResettableExpandableBufferOutputStream::char_type* +BasicResettableExpandableBufferOutputStream::data() const noexcept +{ + return m_streambuf.data(); +} + +template +inline std::streamsize BasicResettableExpandableBufferOutputStream::size() const noexcept +{ + return m_streambuf.size(); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BUFFER_STREAM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp new file mode 100644 index 0000000..7d2eab0 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp @@ -0,0 +1,66 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_CALL_WITH_TUPLE_HPP +#define REALM_UTIL_CALL_WITH_TUPLE_HPP + +#include +#include + +namespace realm { +namespace _impl { + +/// \cond doxygen_skip +/// Doxygen warns about a recursive class relation, but this is intentional. + +template +struct Indexes { +}; +template +struct GenIndexes : GenIndexes { +}; +template +struct GenIndexes<0, I...> { + typedef Indexes type; +}; + +/// \endcond + +template +auto call_with_tuple(F func, std::tuple args, Indexes) -> decltype(func(std::get(args)...)) +{ + static_cast(args); // Prevent GCC warning when tuple is empty + return func(std::get(args)...); +} + +} // namespace _impl + +namespace util { + +template +auto call_with_tuple(F func, std::tuple args) + -> decltype(_impl::call_with_tuple(std::move(func), std::move(args), + typename _impl::GenIndexes::type())) +{ + return _impl::call_with_tuple(std::move(func), std::move(args), typename _impl::GenIndexes::type()); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_CALL_WITH_TUPLE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp new file mode 100644 index 0000000..a1ec431 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp @@ -0,0 +1,108 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_CF_PTR_HPP +#define REALM_UTIL_CF_PTR_HPP + +#include + +#if REALM_PLATFORM_APPLE + +#include + +namespace realm { +namespace util { + +template +class CFPtr { +public: + explicit CFPtr(Ref ref = nullptr) noexcept + : m_ref(ref) + { + } + + CFPtr(CFPtr&& rg) noexcept + : m_ref(rg.m_ref) + { + rg.m_ref = nullptr; + } + + ~CFPtr() noexcept + { + if (m_ref) + CFRelease(m_ref); + } + + CFPtr& operator=(CFPtr&& rg) noexcept + { + REALM_ASSERT(!m_ref || m_ref != rg.m_ref); + if (m_ref) + CFRelease(m_ref); + m_ref = rg.m_ref; + rg.m_ref = nullptr; + return *this; + } + + explicit operator bool() const noexcept + { + return bool(m_ref); + } + + Ref get() const noexcept + { + return m_ref; + } + + Ref release() noexcept + { + Ref ref = m_ref; + m_ref = nullptr; + return ref; + } + + void reset(Ref ref = nullptr) noexcept + { + REALM_ASSERT(!m_ref || m_ref != ref); + if (m_ref) + CFRelease(m_ref); + m_ref = ref; + } + +private: + Ref m_ref; +}; + +template +CFPtr adoptCF(Ref ptr) +{ + return CFPtr(ptr); +} + +template +CFPtr retainCF(Ref ptr) +{ + CFRetain(ptr); + return CFPtr(ptr); +} +} +} + + +#endif // REALM_PLATFORM_APPLE + +#endif // REALM_UTIL_CF_PTR_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/compression.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/compression.hpp new file mode 100644 index 0000000..4b2d983 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/compression.hpp @@ -0,0 +1,150 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_COMPRESSION_HPP +#define REALM_UTIL_COMPRESSION_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace util { +namespace compression { + +enum class error { + out_of_memory = 1, + compress_buffer_too_small = 2, + compress_error = 3, + corrupt_input = 4, + incorrect_decompressed_size = 5, + decompress_error = 6 +}; + +const std::error_category& error_category() noexcept; + +std::error_code make_error_code(error) noexcept; + +} // namespace compression +} // namespace util +} // namespace realm + +namespace std { + +template<> struct is_error_code_enum { + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace util { +namespace compression { + +class Alloc { +public: + // Returns null on "out of memory" + virtual void* alloc(size_t size) = 0; + virtual void free(void* addr) noexcept = 0; + virtual ~Alloc() {} +}; + +class CompressMemoryArena: public Alloc { +public: + void* alloc(size_t size) override final + { + size_t offset = m_offset; + size_t padding = offset % alignof (std::max_align_t); + if (padding > m_size - offset) + return nullptr; + offset += padding; + void* addr = m_buffer.get() + offset; + if (size > m_size - offset) + return nullptr; + m_offset = offset + size; + return addr; + } + + void free(void*) noexcept override final + { + // No-op + } + + void reset() noexcept + { + m_offset = 0; + } + + size_t size() const noexcept + { + return m_size; + } + + void resize(size_t size) + { + m_buffer = std::make_unique(size); // Throws + m_size = size; + m_offset = 0; + } + +private: + size_t m_size = 0, m_offset = 0; + std::unique_ptr m_buffer; +}; + + +/// compress_bound() calculates an upper bound on the size of the compressed +/// data. The caller can use this function to allocate memory buffer calling +/// compress(). \a uncompressed_buf is the buffer with uncompresed data. The +/// size of the uncompressed data is \a uncompressed_size. \a compression_level +/// is described under compress(). \a bound is set to the upper bound at +/// return. The returned error code is of category compression::error_category. +std::error_code compress_bound(const char* uncompressed_buf, size_t uncompressed_size, + size_t& bound, int compression_level = 1); + +/// compress() compresses the data in the \a uncompressed_buf of size \a +/// uncompressed_size into \a compressed_buf. compress() resizes \a +/// compressed_buf. At return, \a compressed_buf has the size of the compressed +/// data. \a compression_level is [1-9] with 1 the fastest for the current zlib +/// implementation. The returned error code is of category +/// compression::error_category. +std::error_code compress(const char* uncompressed_buf, size_t uncompressed_size, + char* compressed_buf, size_t compressed_buf_size, + size_t& compressed_size, int compression_level = 1, + Alloc* custom_allocator = nullptr); + +/// decompress() decompresses the data in \param compressed_buf of size \a +/// compresed_size into \a decompressed_buf. \a decompressed_size is the +/// expected size of the decompressed data. \a decompressed_buf must have size +/// at least \a decompressed_size. decompress() throws on errors, including the +/// error where the size of the decompressed data is unequal to +/// decompressed_size. The returned error code is of category +/// compression::error_category. +std::error_code decompress(const char* compressed_buf, size_t compressed_size, + char* decompressed_buf, size_t decompressed_size); + +} // namespace compression +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_COMPRESSION_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/config.h b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/config.h new file mode 100644 index 0000000..98fab6c --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/config.h @@ -0,0 +1,27 @@ +/************************************************************************* + * + * CAUTION: DO NOT EDIT THIS FILE -- YOUR CHANGES WILL BE LOST! + * + * This file is generated by config.sh + * + *************************************************************************/ + +#define REALM_VERSION "unknown" + +#define REALM_INSTALL_PREFIX "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install" +#define REALM_INSTALL_EXEC_PREFIX "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install" +#define REALM_INSTALL_INCLUDEDIR "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install/include" +#define REALM_INSTALL_BINDIR "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install/bin" +#define REALM_INSTALL_LIBDIR "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install/lib" +#define REALM_INSTALL_LIBEXECDIR "/Users/realm/workspace/realm_realm-core_master-ZBP7YYGK7EEKLBWAJKZQMIXPK7BHX4IZSAMXTSGLQ5SXVPPKKJMQ/install/libexec" + +#ifdef REALM_DEBUG +# define REALM_MAX_BPNODE_SIZE 1000 +#else +# define REALM_MAX_BPNODE_SIZE 1000 +#endif + +#define REALM_ENABLE_ALLOC_SET_ZERO 0 +#define REALM_ENABLE_ENCRYPTION 1 +#define REALM_ENABLE_ASSERTIONS 1 +#define REALM_ENABLE_MEMDEBUG 0 diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp new file mode 100644 index 0000000..61ab9f1 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp @@ -0,0 +1,159 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP +#define REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP + +#include +#include +#include + +#if REALM_ENABLE_ENCRYPTION + +typedef size_t (*Header_to_size)(const char* addr); + +#include + +namespace realm { +namespace util { + +struct SharedFileInfo; +class EncryptedFileMapping; + +class EncryptedFileMapping { +public: + // Adds the newly-created object to file.mappings iff it's successfully constructed + EncryptedFileMapping(SharedFileInfo& file, size_t file_offset, void* addr, size_t size, File::AccessMode access); + ~EncryptedFileMapping(); + + // Default implementations of copy/assign can trigger multiple destructions + EncryptedFileMapping(const EncryptedFileMapping&) = delete; + EncryptedFileMapping& operator=(const EncryptedFileMapping&) = delete; + + // Write all dirty pages to disk and mark them read-only + // Does not call fsync + void flush() noexcept; + + // Sync this file to disk + void sync() noexcept; + + // Make sure that memory in the specified range is synchronized with any + // changes made globally visible through call to write_barrier + void read_barrier(const void* addr, size_t size, UniqueLock& lock, Header_to_size header_to_size); + + // Ensures that any changes made to memory in the specified range + // becomes visible to any later calls to read_barrier() + void write_barrier(const void* addr, size_t size) noexcept; + + // Set this mapping to a new address and size + // Flushes any remaining dirty pages from the old mapping + void set(void* new_addr, size_t new_size, size_t new_file_offset); + +private: + SharedFileInfo& m_file; + + size_t m_page_shift; + size_t m_blocks_per_page; + + void* m_addr = nullptr; + size_t m_file_offset = 0; + + uintptr_t m_first_page; + size_t m_page_count = 0; + + std::vector m_up_to_date_pages; + std::vector m_dirty_pages; + + File::AccessMode m_access; + +#ifdef REALM_DEBUG + std::unique_ptr m_validate_buffer; +#endif + + char* page_addr(size_t i) const noexcept; + + void mark_outdated(size_t i) noexcept; + void mark_up_to_date(size_t i) noexcept; + void mark_unwritable(size_t i) noexcept; + + bool copy_up_to_date_page(size_t i) noexcept; + void refresh_page(size_t i); + void write_page(size_t i) noexcept; + + void validate_page(size_t i) noexcept; + void validate() noexcept; +}; + + +inline void EncryptedFileMapping::read_barrier(const void* addr, size_t size, UniqueLock& lock, + Header_to_size header_to_size) +{ + size_t first_accessed_page = reinterpret_cast(addr) >> m_page_shift; + size_t first_idx = first_accessed_page - m_first_page; + + // make sure the first page is available + // Checking before taking the lock is important to performance. + if (!m_up_to_date_pages[first_idx]) { + if (!lock.holds_lock()) + lock.lock(); + // after taking the lock, we must repeat the check so that we never + // call refresh_page() on a page which is already up to date. + if (!m_up_to_date_pages[first_idx]) + refresh_page(first_idx); + } + + if (header_to_size) { + + // We know it's an array, and array headers are 8-byte aligned, so it is + // included in the first page which was handled above. + size = header_to_size(static_cast(addr)); + } + size_t last_accessed_page = (reinterpret_cast(addr) + size - 1) >> m_page_shift; + size_t last_idx = last_accessed_page - m_first_page; + + for (size_t idx = first_idx + 1; idx <= last_idx; ++idx) { + if (!m_up_to_date_pages[idx]) { + if (!lock.holds_lock()) + lock.lock(); + // after taking the lock, we must repeat the check so that we never + // call refresh_page() on a page which is already up to date. + if (!m_up_to_date_pages[idx]) + refresh_page(idx); + } + } +} +} +} + +#endif // REALM_ENABLE_ENCRYPTION + +namespace realm { +namespace util { + +/// Thrown by EncryptedFileMapping if a file opened is non-empty and does not +/// contain valid encrypted data +struct DecryptionFailed : util::File::AccessError { + DecryptionFailed() + : util::File::AccessError("Decryption failed", std::string()) + { + } +}; +} +} + +#endif // REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/features.h b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/features.h new file mode 100644 index 0000000..23c8361 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/features.h @@ -0,0 +1,285 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_FEATURES_H +#define REALM_UTIL_FEATURES_H + +#ifdef _MSC_VER +#pragma warning(disable : 4800) // Visual Studio int->bool performance warnings +#endif + +#ifdef _WIN32 +#define NOMINMAX +#endif + +#ifdef REALM_HAVE_CONFIG +#include +#else +#define REALM_VERSION "unknown" + +// Even if we don't have config.h (generated by `build.sh config`), it is still +// possible that the following three defines are set by another build system +// (such as Xcode or VS). + +#ifndef REALM_ENABLE_ALLOC_SET_ZERO +#define REALM_ENABLE_ALLOC_SET_ZERO 0 +#endif + +#ifndef REALM_ENABLE_ENCRYPTION +#define REALM_ENABLE_ENCRYPTION 0 +#endif + +#ifndef REALM_ENABLE_ASSERTIONS +#define REALM_ENABLE_ASSERTIONS 0 +#endif + +#ifndef REALM_ENABLE_MEMDEBUG +#define REALM_ENABLE_MEMDEBUG 0 +#endif + +#ifndef _WIN32 +#define REALM_INSTALL_PREFIX "/usr/local" +#define REALM_INSTALL_EXEC_PREFIX REALM_INSTALL_PREFIX +#define REALM_INSTALL_INCLUDEDIR REALM_INSTALL_PREFIX "/include" +#define REALM_INSTALL_BINDIR REALM_INSTALL_EXEC_PREFIX "/bin" +#define REALM_INSTALL_LIBDIR REALM_INSTALL_EXEC_PREFIX "/lib" +#define REALM_INSTALL_LIBEXECDIR REALM_INSTALL_EXEC_PREFIX "/libexec" +#endif +#endif + +/* The maximum number of elements in a B+-tree node. Applies to inner nodes and + * to leaves. The minimum allowable value is 2. + */ +#ifndef REALM_MAX_BPNODE_SIZE +#define REALM_MAX_BPNODE_SIZE 1000 +#endif + + +#define REALM_QUOTE_2(x) #x +#define REALM_QUOTE(x) REALM_QUOTE_2(x) + +/* See these links for information about feature check macroes in GCC, + * Clang, and MSVC: + * + * http://gcc.gnu.org/projects/cxx0x.html + * http://clang.llvm.org/cxx_status.html + * http://clang.llvm.org/docs/LanguageExtensions.html#checks-for-standard-language-features + * http://msdn.microsoft.com/en-us/library/vstudio/hh567368.aspx + * http://sourceforge.net/p/predef/wiki/Compilers + */ + + +/* Compiler is GCC and version is greater than or equal to the specified version */ +#define REALM_HAVE_AT_LEAST_GCC(maj, min) \ + (__GNUC__ > (maj) || __GNUC__ == (maj) && __GNUC_MINOR__ >= (min)) + +#if defined(__clang__) +#define REALM_HAVE_CLANG_FEATURE(feature) __has_feature(feature) +#define REALM_HAVE_CLANG_WARNING(warning) __has_warning(warning) +#else +#define REALM_HAVE_CLANG_FEATURE(feature) 0 +#define REALM_HAVE_CLANG_WARNING(warning) 0 +#endif + +#if defined(__GNUC__) // clang or GCC +#define REALM_PRAGMA(v) _Pragma(REALM_QUOTE_2(v)) +#elif defined(_MSC_VER) // VS +#define REALM_PRAGMA(v) __pragma(v) +#else +#define REALM_PRAGMA(v) +#endif + +#if defined(__clang__) +#define REALM_DIAG(v) REALM_PRAGMA(clang diagnostic v) +#elif defined(__GNUC__) +#define REALM_DIAG(v) REALM_PRAGMA(GCC diagnostic v) +#else +#define REALM_DIAG(v) +#endif + +#define REALM_DIAG_PUSH() REALM_DIAG(push) +#define REALM_DIAG_POP() REALM_DIAG(pop) + +#ifdef _MSC_VER +#define REALM_VS_WARNING_DISABLE #pragma warning (default: 4297) +#endif + +#if REALM_HAVE_CLANG_WARNING("-Wtautological-compare") || REALM_HAVE_AT_LEAST_GCC(6, 0) +#define REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE() REALM_DIAG(ignored "-Wtautological-compare") +#else +#define REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE() +#endif + +#ifdef _MSC_VER +#define REALM_DIAG_IGNORE_UNSIGNED_MINUS() REALM_PRAGMA(warning(disable : 4146)) +#else +#define REALM_DIAG_IGNORE_UNSIGNED_MINUS() +#endif + +/* Compiler is MSVC (Microsoft Visual C++) */ +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#define REALM_HAVE_AT_LEAST_MSVC_10_2010 1 +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1700 +#define REALM_HAVE_AT_LEAST_MSVC_11_2012 1 +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1800 +#define REALM_HAVE_AT_LEAST_MSVC_12_2013 1 +#endif + + +/* The way to specify that a function never returns. */ +#if REALM_HAVE_AT_LEAST_GCC(4, 8) || REALM_HAVE_CLANG_FEATURE(cxx_attributes) +#define REALM_NORETURN [[noreturn]] +#elif __GNUC__ +#define REALM_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define REALM_NORETURN __declspec(noreturn) +#else +#define REALM_NORETURN +#endif + + +/* The way to specify that a variable or type is intended to possibly + * not be used. Use it to suppress a warning from the compiler. */ +#if __GNUC__ +#define REALM_UNUSED __attribute__((unused)) +#else +#define REALM_UNUSED +#endif + +/* The way to specify that a function is deprecated + * not be used. Use it to suppress a warning from the compiler. */ +#if __GNUC__ +#define REALM_DEPRECATED(x) [[deprecated(x)]] +#else +#define REALM_DEPRECATED(x) __declspec(deprecated(x)) +#endif + + +#if __GNUC__ || defined __INTEL_COMPILER +#define REALM_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#define REALM_LIKELY(expr) __builtin_expect(!!(expr), 1) +#else +#define REALM_UNLIKELY(expr) (expr) +#define REALM_LIKELY(expr) (expr) +#endif + + +#if defined(__GNUC__) || defined(__HP_aCC) +#define REALM_FORCEINLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define REALM_FORCEINLINE __forceinline +#else +#define REALM_FORCEINLINE inline +#endif + + +#if defined(__GNUC__) || defined(__HP_aCC) +#define REALM_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define REALM_NOINLINE __declspec(noinline) +#else +#define REALM_NOINLINE +#endif + + +/* Thread specific data (only for POD types) */ +#if defined __clang__ +#define REALM_THREAD_LOCAL __thread +#else +#define REALM_THREAD_LOCAL thread_local +#endif + + +#if defined __ANDROID__ +#define REALM_ANDROID 1 +#else +#define REALM_ANDROID 0 +#endif + +#ifndef REALM_UWP +#define REALM_UWP 0 +#endif + +// Some documentation of the defines provided by Apple: +// http://developer.apple.com/library/mac/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html#//apple_ref/doc/uid/TP40002850-SW13 +#if defined __APPLE__ && defined __MACH__ +#define REALM_PLATFORM_APPLE 1 +/* Apple OSX and iOS (Darwin). */ +#include +#if TARGET_OS_IPHONE == 1 +/* Device (iPhone or iPad) or simulator. */ +#define REALM_IOS 1 +#else +#define REALM_IOS 0 +#endif +#if TARGET_OS_WATCH == 1 +/* Device (Apple Watch) or simulator. */ +#define REALM_WATCHOS 1 +/* The necessary signal handling / mach exception APIs are all unavailable */ +#undef REALM_ENABLE_ENCRYPTION +#define REALM_ENABLE_ENCRYPTION 0 +#else +#define REALM_WATCHOS 0 +#endif +#if TARGET_OS_TV +/* Device (Apple TV) or simulator. */ +#define REALM_TVOS 1 +#else +#define REALM_TVOS 0 +#endif +#else +#define REALM_PLATFORM_APPLE 0 +#define REALM_IOS 0 +#define REALM_WATCHOS 0 +#define REALM_TVOS 0 +#endif + + +#if REALM_ANDROID || REALM_IOS || REALM_WATCHOS +#define REALM_MOBILE 1 +#else +#define REALM_MOBILE 0 +#endif + + +#if defined(REALM_DEBUG) && !defined(REALM_COOKIE_CHECK) +#define REALM_COOKIE_CHECK +#endif + +#if !REALM_IOS && !REALM_WATCHOS && !REALM_TVOS && !defined(_WIN32) && !REALM_ANDROID +#define REALM_ASYNC_DAEMON +#endif + +// We're in i686 mode +#if defined(__i386) || defined(__i386__) || defined(__i686__) || defined(_M_I86) || defined(_M_IX86) +#define REALM_ARCHITECTURE_X86_32 1 +#else +#define REALM_ARCHITECTURE_X86_32 0 +#endif + +// We're in amd64 mode +#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) +#define REALM_ARCHITECTURE_X86_64 1 +#else +#define REALM_ARCHITECTURE_X86_64 0 +#endif + +#endif /* REALM_UTIL_FEATURES_H */ diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file.hpp new file mode 100644 index 0000000..88a1026 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file.hpp @@ -0,0 +1,1271 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_FILE_HPP +#define REALM_UTIL_FILE_HPP + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include // POSIX.1-2001 +#endif + +#include +#include +#include + + +namespace realm { +namespace util { + +class EncryptedFileMapping; + +/// Create the specified directory in the file system. +/// +/// \throw File::AccessError If the directory could not be created. If +/// the reason corresponds to one of the exception types that are +/// derived from File::AccessError, the derived exception type is +/// thrown (as long as the underlying system provides the information +/// to unambiguously distinguish that particular reason). +void make_dir(const std::string& path); + +/// Same as make_dir() except that this one returns false, rather than throwing +/// an exception, if the specified directory already existed. If the directory +// did not already exist and was newly created, this returns true. +bool try_make_dir(const std::string& path); + +/// Remove the specified empty directory path from the file system. It is an +/// error if the specified path is not a directory, or if it is a nonempty +/// directory. In so far as the specified path is a directory, std::remove(const +/// char*) is equivalent to this function. +/// +/// \throw File::AccessError If the directory could not be removed. If the +/// reason corresponds to one of the exception types that are derived from +/// File::AccessError, the derived exception type is thrown (as long as the +/// underlying system provides the information to unambiguously distinguish that +/// particular reason). +void remove_dir(const std::string& path); + +/// Remove the specified directory after removing all its contents. Files +/// (nondirectory entries) will be removed as if by a call to File::remove(), +/// and empty directories as if by a call to remove_dir(). +/// +/// \throw File::AccessError If removal of the directory, or any of its contents +/// fail. +void remove_dir_recursive(const std::string& path); + +/// Create a new unique directory for temporary files. The absolute +/// path to the new directory is returned without a trailing slash. +std::string make_temp_dir(); + +size_t page_size(); + + +/// This class provides a RAII abstraction over the concept of a file +/// descriptor (or file handle). +/// +/// Locks are automatically and immediately released when the File +/// instance is closed. +/// +/// You can use CloseGuard and UnlockGuard to acheive exception-safe +/// closing or unlocking prior to the File instance being detroyed. +/// +/// A single File instance must never be accessed concurrently by +/// multiple threads. +/// +/// You can write to a file via an std::ostream as follows: +/// +/// \code{.cpp} +/// +/// File::Streambuf my_streambuf(&my_file); +/// std::ostream out(&my_strerambuf); +/// out << 7945.9; +/// +/// \endcode +class File { +public: + enum Mode { + mode_Read, ///< access_ReadOnly, create_Never (fopen: rb) + mode_Update, ///< access_ReadWrite, create_Never (fopen: rb+) + mode_Write, ///< access_ReadWrite, create_Auto, flag_Trunc (fopen: wb+) + mode_Append ///< access_ReadWrite, create_Auto, flag_Append (fopen: ab+) + }; + + /// Equivalent to calling open(const std::string&, Mode) on a + /// default constructed instance. + explicit File(const std::string& path, Mode = mode_Read); + + /// Create an instance that is not initially attached to an open + /// file. + File() noexcept; + + ~File() noexcept; + + File(File&&) noexcept; + File& operator=(File&&) noexcept; + + // Disable copying by l-value. Copying an open file will create a scenario + // where the same file descriptor will be opened once but closed twice. + File(const File&) = delete; + File& operator=(const File&) = delete; + + /// Calling this function on an instance that is already attached + /// to an open file has undefined behavior. + /// + /// \throw AccessError If the file could not be opened. If the + /// reason corresponds to one of the exception types that are + /// derived from AccessError, the derived exception type is thrown + /// (as long as the underlying system provides the information to + /// unambiguously distinguish that particular reason). + void open(const std::string& path, Mode = mode_Read); + + /// This function is idempotent, that is, it is valid to call it + /// regardless of whether this instance currently is attached to + /// an open file. + void close() noexcept; + + /// Check whether this File instance is currently attached to an + /// open file. + bool is_attached() const noexcept; + + enum AccessMode { + access_ReadOnly, + access_ReadWrite, + }; + + enum CreateMode { + create_Auto, ///< Create the file if it does not already exist. + create_Never, ///< Fail if the file does not already exist. + create_Must ///< Fail if the file already exists. + }; + + enum { + flag_Trunc = 1, ///< Truncate the file if it already exists. + flag_Append = 2 ///< Move to end of file before each write. + }; + + /// See open(const std::string&, Mode). + /// + /// Specifying access_ReadOnly together with a create mode that is + /// not create_Never, or together with a non-zero \a flags + /// argument, results in undefined behavior. Specifying flag_Trunc + /// together with create_Must results in undefined behavior. + void open(const std::string& path, AccessMode, CreateMode, int flags); + + /// Same as open(path, access_ReadWrite, create_Auto, 0), except + /// that this one returns an indication of whether a new file was + /// created, or an existing file was opened. + void open(const std::string& path, bool& was_created); + + /// Read data into the specified buffer and return the number of + /// bytes read. If the returned number of bytes is less than \a + /// size, then the end of the file has been reached. + /// + /// Calling this function on an instance, that is not currently + /// attached to an open file, has undefined behavior. + size_t read(char* data, size_t size); + + /// Write the specified data to this file. + /// + /// Calling this function on an instance, that is not currently + /// attached to an open file, has undefined behavior. + /// + /// Calling this function on an instance, that was opened in + /// read-only mode, has undefined behavior. + void write(const char* data, size_t size); + + /// Calls write(s.data(), s.size()). + void write(const std::string& s) + { + write(s.data(), s.size()); + } + + /// Calls read(data, N). + template + size_t read(char (&data)[N]) + { + return read(data, N); + } + + /// Calls write(data(), N). + template + void write(const char (&data)[N]) + { + write(data, N); + } + + /// Plays the same role as off_t in POSIX + typedef int_fast64_t SizeType; + + /// Calling this function on an instance that is not attached to + /// an open file has undefined behavior. + SizeType get_size() const; + + /// If this causes the file to grow, then the new section will + /// have undefined contents. Setting the size with this function + /// does not necessarily allocate space on the target device. If + /// you want to ensure allocation, call alloc(). Calling this + /// function will generally affect the read/write offset + /// associated with this File instance. + /// + /// Calling this function on an instance that is not attached to + /// an open file has undefined behavior. Calling this function on + /// a file that is opened in read-only mode, is an error. + void resize(SizeType); + + /// The same as prealloc_if_supported() but when the operation is + /// not supported by the system, this function will still increase + /// the file size when the specified region extends beyond the + /// current end of the file. This allows you to both extend and + /// allocate in one operation. + /// + /// The downside is that this function is not guaranteed to have + /// atomic behaviour on all systems, that is, two processes, or + /// two threads should never call this function concurrently for + /// the same underlying file even though they access the file + /// through distinct File instances. + /// + /// \sa prealloc_if_supported() + void prealloc(SizeType offset, size_t size); + + /// When supported by the system, allocate space on the target + /// device for the specified region of the file. If the region + /// extends beyond the current end of the file, the file size is + /// increased as necessary. + /// + /// On systems that do not support this operation, this function + /// has no effect. You may call is_prealloc_supported() to + /// determine if it is supported on your system. + /// + /// Calling this function on an instance, that is not attached to + /// an open file, has undefined behavior. Calling this function on + /// a file, that is opened in read-only mode, is an error. + /// + /// This function is guaranteed to have atomic behaviour, that is, + /// there is never any risk of the file size being reduced even + /// with concurrently executing invocations. + /// + /// \sa prealloc() + /// \sa is_prealloc_supported() + void prealloc_if_supported(SizeType offset, size_t size); + + /// See prealloc_if_supported(). + static bool is_prealloc_supported(); + + /// Reposition the read/write offset of this File + /// instance. Distinct File instances have separate independent + /// offsets, as long as the cucrrent process is not forked. + void seek(SizeType); + + /// Flush in-kernel buffers to disk. This blocks the caller until the + /// synchronization operation is complete. On POSIX systems this function + /// calls `fsync()`. On Apple platforms if calls `fcntl()` with command + /// `F_FULLFSYNC`. + void sync(); + + /// Place an exclusive lock on this file. This blocks the caller + /// until all other locks have been released. + /// + /// Locks acquired on distinct File instances have fully recursive + /// behavior, even if they are acquired in the same process (or + /// thread) and are attached to the same underlying file. + /// + /// Calling this function on an instance that is not attached to + /// an open file, or on an instance that is already locked has + /// undefined behavior. + void lock_exclusive(); + + /// Place an shared lock on this file. This blocks the caller + /// until all other exclusive locks have been released. + /// + /// Locks acquired on distinct File instances have fully recursive + /// behavior, even if they are acquired in the same process (or + /// thread) and are attached to the same underlying file. + /// + /// Calling this function on an instance that is not attached to + /// an open file, or on an instance that is already locked has + /// undefined behavior. + void lock_shared(); + + /// Non-blocking version of lock_exclusive(). Returns true iff it + /// succeeds. + bool try_lock_exclusive(); + + /// Non-blocking version of lock_shared(). Returns true iff it + /// succeeds. + bool try_lock_shared(); + + /// Release a previously acquired lock on this file. This function + /// is idempotent. + void unlock() noexcept; + + /// Set the encryption key used for this file. Must be called before any + /// mappings are created or any data is read from or written to the file. + /// + /// \param key A 64-byte encryption key, or null to disable encryption. + void set_encryption_key(const char* key); + + enum { + /// If possible, disable opportunistic flushing of dirted + /// pages of a memory mapped file to physical medium. On some + /// systems this cannot be disabled. On other systems it is + /// the default behavior. An explicit call to sync_map() will + /// flush the buffers regardless of whether this flag is + /// specified or not. + map_NoSync = 1 + }; + + /// Map this file into memory. The file is mapped as shared + /// memory. This allows two processes to interact under exatly the + /// same rules as applies to the interaction via regular memory of + /// multiple threads inside a single process. + /// + /// This File instance does not need to remain in existence after + /// the mapping is established. + /// + /// Multiple concurrent mappings may be created from the same File + /// instance. + /// + /// Specifying access_ReadWrite for a file that is opened in + /// read-only mode, is an error. + /// + /// Calling this function on an instance that is not attached to + /// an open file, or one that is attached to an empty file has + /// undefined behavior. + /// + /// Calling this function with a size that is greater than the + /// size of the file has undefined behavior. + void* map(AccessMode, size_t size, int map_flags = 0, size_t offset = 0) const; + + /// The same as unmap(old_addr, old_size) followed by map(a, + /// new_size, map_flags), but more efficient on some systems. + /// + /// The old address range must have been acquired by a call to + /// map() or remap() on this File instance, the specified access + /// mode and flags must be the same as the ones specified + /// previously, and this File instance must not have been reopend + /// in the meantime. Failing to adhere to these rules will result + /// in undefined behavior. + /// + /// If this function throws, the old address range will remain + /// mapped. + void* remap(void* old_addr, size_t old_size, AccessMode a, size_t new_size, int map_flags = 0, + size_t file_offset = 0) const; + +#if REALM_ENABLE_ENCRYPTION + void* map(AccessMode, size_t size, EncryptedFileMapping*& mapping, int map_flags = 0, size_t offset = 0) const; +#endif + /// Unmap the specified address range which must have been + /// previously returned by map(). + static void unmap(void* addr, size_t size) noexcept; + + /// Flush in-kernel buffers to disk. This blocks the caller until + /// the synchronization operation is complete. The specified + /// address range must be (a subset of) one that was previously returned by + /// map(). + static void sync_map(void* addr, size_t size); + + /// Check whether the specified file or directory exists. Note + /// that a file or directory that resides in a directory that the + /// calling process has no access to, will necessarily be reported + /// as not existing. + static bool exists(const std::string& path); + + /// Check whether the specified path exists and refers to a directory. If + /// the referenced file system object resides in an inaccessible directory, + /// this function returns false. + static bool is_dir(const std::string& path); + + /// Remove the specified file path from the file system. It is an error if + /// the specified path is a directory. If the specified file is a symbolic + /// link, the link is removed, leaving the liked file intact. In so far as + /// the specified path is not a directory, std::remove(const char*) is + /// equivalent to this function. + /// + /// The specified file must not be open by the calling process. If + /// it is, this function has undefined behaviour. Note that an + /// open memory map of the file counts as "the file being open". + /// + /// \throw AccessError If the specified directory entry could not + /// be removed. If the reason corresponds to one of the exception + /// types that are derived from AccessError, the derived exception + /// type is thrown (as long as the underlying system provides the + /// information to unambiguously distinguish that particular + /// reason). + static void remove(const std::string& path); + + /// Same as remove() except that this one returns false, rather + /// than thriowing an exception, if the specified file does not + /// exist. If the file did exist, and was deleted, this function + /// returns true. + static bool try_remove(const std::string& path); + + /// Change the path of a directory entry. This can be used to + /// rename a file, and/or to move it from one directory to + /// another. This function is equivalent to std::rename(const + /// char*, const char*). + /// + /// \throw AccessError If the path of the directory entry could + /// not be changed. If the reason corresponds to one of the + /// exception types that are derived from AccessError, the derived + /// exception type is thrown (as long as the underlying system + /// provides the information to unambiguously distinguish that + /// particular reason). + static void move(const std::string& old_path, const std::string& new_path); + + /// Copy the file at the specified origin path to the specified target path. + static void copy(const std::string& origin_path, const std::string& target_path); + + /// Compare the two files at the specified paths for equality. Returns true + /// if, and only if they are equal. + static bool compare(const std::string& path_1, const std::string& path_2); + + /// Check whether two open file descriptors refer to the same + /// underlying file, that is, if writing via one of them, will + /// affect what is read from the other. In UNIX this boils down to + /// comparing inode numbers. + /// + /// Both instances have to be attached to open files. If they are + /// not, this function has undefined behavior. + bool is_same_file(const File&) const; + + // FIXME: Get rid of this method + bool is_removed() const; + + /// Resolve the specified path against the specified base directory. + /// + /// If \a path is absolute, or if \a base_dir is empty, \p path is returned + /// unmodified, otherwise \a path is resolved against \a base_dir. + /// + /// Examples (assuming POSIX): + /// + /// resolve("file", "dir") -> "dir/file" + /// resolve("../baz", "/foo/bar") -> "/foo/baz" + /// resolve("foo", ".") -> "./foo" + /// resolve(".", "/foo/") -> "/foo" + /// resolve("..", "foo") -> "." + /// resolve("../..", "foo") -> ".." + /// resolve("..", "..") -> "../.." + /// resolve("", "") -> "." + /// resolve("", "/") -> "/." + /// resolve("..", "/") -> "/." + /// resolve("..", "foo//bar") -> "foo" + /// + /// This function does not access the file system. + /// + /// \param path The path to be resolved. An empty string produces the same + /// result as as if "." was passed. The result has a trailing directory + /// separator (`/`) if, and only if this path has a trailing directory + /// separator. + /// + /// \param base_dir The base directory path, which may be relative or + /// absolute. A final directory separator (`/`) is optional. The empty + /// string is interpreted as a relative path. + static std::string resolve(const std::string& path, const std::string& base_dir); + + using ForEachHandler = std::function; + + /// Scan the specified directory recursivle, and report each file + /// (nondirectory entry) via the specified handler. + /// + /// The first argument passed to the handler is the name of a file (not the + /// whole path), and the second argument is the directory in which that file + /// resides. The directory will be specified as a path, and relative to \a + /// dir_path. The directory will be the empty string for files residing + /// directly in \a dir_path. + /// + /// If the handler returns false, scanning will be aborted immediately, and + /// for_each() will return false. Otherwise for_each() will return true. + /// + /// Scanning is done as if by a recursive set of DirScanner objects. + static bool for_each(const std::string& dir_path, ForEachHandler handler); + + struct UniqueID { +#ifdef _WIN32 // Windows version +// FIXME: This is not implemented for Windows +#else + // NDK r10e has a bug in sys/stat.h dev_t ino_t are 4 bytes, + // but stat.st_dev and st_ino are 8 bytes. So we just use uint64 instead. + uint_fast64_t device; + uint_fast64_t inode; +#endif + }; + // Return the unique id for the current opened file descriptor. + // Same UniqueID means they are the same file. + UniqueID get_unique_id() const; + // Return false if the file doesn't exist. Otherwise uid will be set. + static bool get_unique_id(const std::string& path, UniqueID& uid); + + class ExclusiveLock; + class SharedLock; + + template + class Map; + + class CloseGuard; + class UnlockGuard; + class UnmapGuard; + + class Streambuf; + + // Exceptions + class AccessError; + class PermissionDenied; + class NotFound; + class Exists; + +private: +#ifdef _WIN32 + void* m_handle; + bool m_have_lock; // Only valid when m_handle is not null + + SizeType get_file_position(); // POSIX version not needed because it's only used by Windows version of resize(). +#else + int m_fd; +#endif + + std::unique_ptr m_encryption_key; + + bool lock(bool exclusive, bool non_blocking); + void open_internal(const std::string& path, AccessMode, CreateMode, int flags, bool* success); + + struct MapBase { + void* m_addr = nullptr; + size_t m_size = 0; + + MapBase() noexcept; + ~MapBase() noexcept; + + // Disable copying. Copying an opened MapBase will create a scenario + // where the same memory will be mapped once but unmapped twice. + MapBase(const MapBase&) = delete; + MapBase& operator=(const MapBase&) = delete; + + void map(const File&, AccessMode, size_t size, int map_flags, size_t offset = 0); + void remap(const File&, AccessMode, size_t size, int map_flags); + void unmap() noexcept; + void sync(); +#if REALM_ENABLE_ENCRYPTION + util::EncryptedFileMapping* m_encrypted_mapping = nullptr; + inline util::EncryptedFileMapping* get_encrypted_mapping() const + { + return m_encrypted_mapping; + } +#else + inline util::EncryptedFileMapping* get_encrypted_mapping() const + { + return nullptr; + } +#endif + }; +}; + + +class File::ExclusiveLock { +public: + ExclusiveLock(File& f) + : m_file(f) + { + f.lock_exclusive(); + } + ~ExclusiveLock() noexcept + { + m_file.unlock(); + } + // Disable copying. It is not how this class should be used. + ExclusiveLock(const ExclusiveLock&) = delete; + ExclusiveLock& operator=(const ExclusiveLock&) = delete; + +private: + File& m_file; +}; + +class File::SharedLock { +public: + SharedLock(File& f) + : m_file(f) + { + f.lock_shared(); + } + ~SharedLock() noexcept + { + m_file.unlock(); + } + // Disable copying. It is not how this class should be used. + SharedLock(const SharedLock&) = delete; + SharedLock& operator=(const SharedLock&) = delete; + +private: + File& m_file; +}; + + +/// This class provides a RAII abstraction over the concept of a +/// memory mapped file. +/// +/// Once created, the Map instance makes no reference to the File +/// instance that it was based upon, and that File instance may be +/// destroyed before the Map instance is destroyed. +/// +/// Multiple concurrent mappings may be created from the same File +/// instance. +/// +/// You can use UnmapGuard to acheive exception-safe unmapping prior +/// to the Map instance being detroyed. +/// +/// A single Map instance must never be accessed concurrently by +/// multiple threads. +template +class File::Map : private MapBase { +public: + /// Equivalent to calling map() on a default constructed instance. + explicit Map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0); + + explicit Map(const File&, size_t offset, AccessMode = access_ReadOnly, size_t size = sizeof(T), + int map_flags = 0); + + /// Create an instance that is not initially attached to a memory + /// mapped file. + Map() noexcept; + + ~Map() noexcept; + + // Disable copying. Copying an opened Map will create a scenario + // where the same memory will be mapped once but unmapped twice. + Map(const Map&) = delete; + Map& operator=(const Map&) = delete; + + /// Move the mapping from another Map object to this Map object + File::Map& operator=(File::Map&& other) + { + if (m_addr) + unmap(); + m_addr = other.get_addr(); + m_size = other.m_size; + other.m_addr = 0; + other.m_size = 0; +#if REALM_ENABLE_ENCRYPTION + m_encrypted_mapping = other.m_encrypted_mapping; + other.m_encrypted_mapping = nullptr; +#endif + return *this; + } + + /// See File::map(). + /// + /// Calling this function on a Map instance that is already + /// attached to a memory mapped file has undefined behavior. The + /// returned pointer is the same as what will subsequently be + /// returned by get_addr(). + T* map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0, size_t offset = 0); + + /// See File::unmap(). This function is idempotent, that is, it is + /// valid to call it regardless of whether this instance is + /// currently attached to a memory mapped file. + void unmap() noexcept; + + /// See File::remap(). + /// + /// Calling this function on a Map instance that is not currently + /// attached to a memory mapped file has undefined behavior. The + /// returned pointer is the same as what will subsequently be + /// returned by get_addr(). + T* remap(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0); + + /// See File::sync_map(). + /// + /// Calling this function on an instance that is not currently + /// attached to a memory mapped file, has undefined behavior. + void sync(); + + /// Check whether this Map instance is currently attached to a + /// memory mapped file. + bool is_attached() const noexcept; + + /// Returns a pointer to the beginning of the memory mapped file, + /// or null if this instance is not currently attached. + T* get_addr() const noexcept; + + /// Returns the size of the mapped region, or zero if this + /// instance does not currently refer to a memory mapped + /// file. When this instance refers to a memory mapped file, the + /// returned value will always be identical to the size passed to + /// the constructor or to map(). + size_t get_size() const noexcept; + + /// Release the currently attached memory mapped file from this + /// Map instance. The address range may then be unmapped later by + /// a call to File::unmap(). + T* release() noexcept; + +#if REALM_ENABLE_ENCRYPTION + /// Get the encrypted file mapping corresponding to this mapping + inline EncryptedFileMapping* get_encrypted_mapping() const + { + return m_encrypted_mapping; + } +#else + inline EncryptedFileMapping* get_encrypted_mapping() const + { + return nullptr; + } +#endif + + friend class UnmapGuard; +}; + + +class File::CloseGuard { +public: + CloseGuard(File& f) noexcept + : m_file(&f) + { + } + ~CloseGuard() noexcept + { + if (m_file) + m_file->close(); + } + void release() noexcept + { + m_file = nullptr; + } + // Disallow the default implementation of copy/assign, this is not how this + // class is intended to be used. For example we could get unexpected + // behaviour if one CloseGuard is copied and released but the other is not. + CloseGuard(const CloseGuard&) = delete; + CloseGuard& operator=(const CloseGuard&) = delete; + +private: + File* m_file; +}; + + +class File::UnlockGuard { +public: + UnlockGuard(File& f) noexcept + : m_file(&f) + { + } + ~UnlockGuard() noexcept + { + if (m_file) + m_file->unlock(); + } + void release() noexcept + { + m_file = nullptr; + } + // Disallow the default implementation of copy/assign, this is not how this + // class is intended to be used. For example we could get unexpected + // behaviour if one UnlockGuard is copied and released but the other is not. + UnlockGuard(const UnlockGuard&) = delete; + UnlockGuard& operator=(const UnlockGuard&) = delete; + +private: + File* m_file; +}; + + +class File::UnmapGuard { +public: + template + UnmapGuard(Map& m) noexcept + : m_map(&m) + { + } + ~UnmapGuard() noexcept + { + if (m_map) + m_map->unmap(); + } + void release() noexcept + { + m_map = nullptr; + } + // Disallow the default implementation of copy/assign, this is not how this + // class is intended to be used. For example we could get unexpected + // behaviour if one UnmapGuard is copied and released but the other is not. + UnmapGuard(const UnmapGuard&) = delete; + UnmapGuard& operator=(const UnmapGuard&) = delete; + +private: + MapBase* m_map; +}; + + +/// Only output is supported at this point. +class File::Streambuf : public std::streambuf { +public: + explicit Streambuf(File*); + ~Streambuf() noexcept; + + // Disable copying + Streambuf(const Streambuf&) = delete; + Streambuf& operator=(const Streambuf&) = delete; + +private: + static const size_t buffer_size = 4096; + + File& m_file; + std::unique_ptr const m_buffer; + + int_type overflow(int_type) override; + int sync() override; + pos_type seekpos(pos_type, std::ios_base::openmode) override; + void flush(); +}; + + +/// Used for any I/O related exception. Note the derived exception +/// types that are used for various specific types of errors. +class File::AccessError : public std::runtime_error { +public: + AccessError(const std::string& msg, const std::string& path); + + /// Return the associated file system path, or the empty string if there is + /// no associated file system path, or if the file system path is unknown. + std::string get_path() const; + +private: + std::string m_path; +}; + + +/// Thrown if the user does not have permission to open or create +/// the specified file in the specified access mode. +class File::PermissionDenied : public AccessError { +public: + PermissionDenied(const std::string& msg, const std::string& path); +}; + + +/// Thrown if the directory part of the specified path was not +/// found, or create_Never was specified and the file did no +/// exist. +class File::NotFound : public AccessError { +public: + NotFound(const std::string& msg, const std::string& path); +}; + + +/// Thrown if create_Always was specified and the file did already +/// exist. +class File::Exists : public AccessError { +public: + Exists(const std::string& msg, const std::string& path); +}; + + +class DirScanner { +public: + DirScanner(const std::string& path, bool allow_missing = false); + ~DirScanner() noexcept; + bool next(std::string& name); + +private: +#ifndef _WIN32 + DIR* m_dirp; +#endif +}; + + +// Implementation: + +inline File::File(const std::string& path, Mode m) +{ +#ifdef _WIN32 + m_handle = nullptr; +#else + m_fd = -1; +#endif + + open(path, m); +} + +inline File::File() noexcept +{ +#ifdef _WIN32 + m_handle = nullptr; +#else + m_fd = -1; +#endif +} + +inline File::~File() noexcept +{ + close(); +} + +inline File::File(File&& f) noexcept +{ +#ifdef _WIN32 + m_handle = f.m_handle; + m_have_lock = f.m_have_lock; + f.m_handle = nullptr; +#else + m_fd = f.m_fd; + f.m_fd = -1; +#endif + m_encryption_key = std::move(f.m_encryption_key); +} + +inline File& File::operator=(File&& f) noexcept +{ + close(); +#ifdef _WIN32 + m_handle = f.m_handle; + m_have_lock = f.m_have_lock; + f.m_handle = nullptr; +#else + m_fd = f.m_fd; + f.m_fd = -1; +#endif + m_encryption_key = std::move(f.m_encryption_key); + return *this; +} + +inline void File::open(const std::string& path, Mode m) +{ + AccessMode a = access_ReadWrite; + CreateMode c = create_Auto; + int flags = 0; + switch (m) { + case mode_Read: + a = access_ReadOnly; + c = create_Never; + break; + case mode_Update: + c = create_Never; + break; + case mode_Write: + flags = flag_Trunc; + break; + case mode_Append: + flags = flag_Append; + break; + } + open(path, a, c, flags); +} + +inline void File::open(const std::string& path, AccessMode am, CreateMode cm, int flags) +{ + open_internal(path, am, cm, flags, nullptr); +} + + +inline void File::open(const std::string& path, bool& was_created) +{ + while (1) { + bool success; + open_internal(path, access_ReadWrite, create_Must, 0, &success); + if (success) { + was_created = true; + return; + } + open_internal(path, access_ReadWrite, create_Never, 0, &success); + if (success) { + was_created = false; + return; + } + } +} + +inline bool File::is_attached() const noexcept +{ +#ifdef _WIN32 + return (m_handle != nullptr); +#else + return 0 <= m_fd; +#endif +} + +inline void File::lock_exclusive() +{ + lock(true, false); +} + +inline void File::lock_shared() +{ + lock(false, false); +} + +inline bool File::try_lock_exclusive() +{ + return lock(true, true); +} + +inline bool File::try_lock_shared() +{ + return lock(false, true); +} + +inline File::MapBase::MapBase() noexcept +{ + m_addr = nullptr; +} + +inline File::MapBase::~MapBase() noexcept +{ + unmap(); +} + +inline void File::MapBase::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset) +{ + REALM_ASSERT(!m_addr); +#if REALM_ENABLE_ENCRYPTION + m_addr = f.map(a, size, m_encrypted_mapping, map_flags, offset); +#else + m_addr = f.map(a, size, map_flags, offset); +#endif + m_size = size; +} + +inline void File::MapBase::unmap() noexcept +{ + if (!m_addr) + return; + File::unmap(m_addr, m_size); + m_addr = nullptr; +#if REALM_ENABLE_ENCRYPTION + m_encrypted_mapping = nullptr; +#endif +} + +inline void File::MapBase::remap(const File& f, AccessMode a, size_t size, int map_flags) +{ + REALM_ASSERT(m_addr); + + m_addr = f.remap(m_addr, m_size, a, size, map_flags); + m_size = size; +} + +inline void File::MapBase::sync() +{ + REALM_ASSERT(m_addr); + + File::sync_map(m_addr, m_size); +} + +template +inline File::Map::Map(const File& f, AccessMode a, size_t size, int map_flags) +{ + map(f, a, size, map_flags); +} + +template +inline File::Map::Map(const File& f, size_t offset, AccessMode a, size_t size, int map_flags) +{ + map(f, a, size, map_flags, offset); +} + +template +inline File::Map::Map() noexcept +{ +} + +template +inline File::Map::~Map() noexcept +{ +} + +template +inline T* File::Map::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset) +{ + MapBase::map(f, a, size, map_flags, offset); + return static_cast(m_addr); +} + +template +inline void File::Map::unmap() noexcept +{ + MapBase::unmap(); +} + +template +inline T* File::Map::remap(const File& f, AccessMode a, size_t size, int map_flags) +{ + MapBase::remap(f, a, size, map_flags); + return static_cast(m_addr); +} + +template +inline void File::Map::sync() +{ + MapBase::sync(); +} + +template +inline bool File::Map::is_attached() const noexcept +{ + return (m_addr != nullptr); +} + +template +inline T* File::Map::get_addr() const noexcept +{ + return static_cast(m_addr); +} + +template +inline size_t File::Map::get_size() const noexcept +{ + return m_addr ? m_size : 0; +} + +template +inline T* File::Map::release() noexcept +{ + T* addr = static_cast(m_addr); + m_addr = nullptr; + return addr; +} + + +inline File::Streambuf::Streambuf(File* f) + : m_file(*f) + , m_buffer(new char[buffer_size]) +{ + char* b = m_buffer.get(); + setp(b, b + buffer_size); +} + +inline File::Streambuf::~Streambuf() noexcept +{ + try { + if (m_file.is_attached()) + flush(); + } + catch (...) { + // Errors deliberately ignored + } +} + +inline File::Streambuf::int_type File::Streambuf::overflow(int_type c) +{ + flush(); + if (c == traits_type::eof()) + return traits_type::not_eof(c); + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; +} + +inline int File::Streambuf::sync() +{ + flush(); + return 0; +} + +inline File::Streambuf::pos_type File::Streambuf::seekpos(pos_type pos, std::ios_base::openmode) +{ + flush(); + SizeType pos2 = 0; + if (int_cast_with_overflow_detect(std::streamsize(pos), pos2)) + throw std::runtime_error("Seek position overflow"); + m_file.seek(pos2); + return pos; +} + +inline void File::Streambuf::flush() +{ + size_t n = pptr() - pbase(); + m_file.write(pbase(), n); + setp(m_buffer.get(), epptr()); +} + +inline File::AccessError::AccessError(const std::string& msg, const std::string& path) + : std::runtime_error(msg) + , m_path(path) +{ +} + +inline std::string File::AccessError::get_path() const +{ + return m_path; +} + +inline File::PermissionDenied::PermissionDenied(const std::string& msg, const std::string& path) + : AccessError(msg, path) +{ +} + +inline File::NotFound::NotFound(const std::string& msg, const std::string& path) + : AccessError(msg, path) +{ +} + +inline File::Exists::Exists(const std::string& msg, const std::string& path) + : AccessError(msg, path) +{ +} + +inline bool operator==(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ +#ifdef _WIN32 // Windows version + throw std::runtime_error("Not yet supported"); +#else // POSIX version + return lhs.device == rhs.device && lhs.inode == rhs.inode; +#endif +} + +inline bool operator!=(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ + return !(lhs == rhs); +} + +inline bool operator<(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ +#ifdef _WIN32 // Windows version + throw std::runtime_error("Not yet supported"); +#else // POSIX version + if (lhs.device < rhs.device) + return true; + if (lhs.device > rhs.device) + return false; + if (lhs.inode < rhs.inode) + return true; + return false; +#endif +} + +inline bool operator>(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ + return rhs < lhs; +} + +inline bool operator<=(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ + return !(lhs > rhs); +} + +inline bool operator>=(const File::UniqueID& lhs, const File::UniqueID& rhs) +{ + return !(lhs < rhs); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_FILE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp new file mode 100644 index 0000000..6505b4b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp @@ -0,0 +1,118 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_FILE_MAPPER_HPP +#define REALM_UTIL_FILE_MAPPER_HPP + +#include +#include +#include + +namespace realm { +namespace util { + +void* mmap(int fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key); +void munmap(void* addr, size_t size) noexcept; +void* mremap(int fd, size_t file_offset, void* old_addr, size_t old_size, File::AccessMode a, size_t new_size, const char* encryption_key); +void msync(void* addr, size_t size); + +// A function which may be given to encryption_read_barrier. If present, the read barrier is a +// a barrier for a full array. If absent, the read barrier is a barrier only for the address +// range give as argument. If the barrier is for a full array, it will read the array header +// and determine the address range from the header. +using HeaderToSize = size_t (*)(const char* addr); +class EncryptedFileMapping; + +#if REALM_ENABLE_ENCRYPTION + + +// This variant allows the caller to obtain direct access to the encrypted file mapping +// for optimization purposes. +void* mmap(int fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key, + EncryptedFileMapping*& mapping); + +void do_encryption_read_barrier(const void* addr, size_t size, HeaderToSize header_to_size, + EncryptedFileMapping* mapping); + +void do_encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping); + +void inline encryption_read_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping, + HeaderToSize header_to_size = nullptr) +{ + if (mapping) + do_encryption_read_barrier(addr, size, header_to_size, mapping); +} + +void inline encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping) +{ + if (mapping) + do_encryption_write_barrier(addr, size, mapping); +} + + +extern util::Mutex& mapping_mutex; + +inline void do_encryption_read_barrier(const void* addr, size_t size, HeaderToSize header_to_size, + EncryptedFileMapping* mapping) +{ + UniqueLock lock(mapping_mutex, defer_lock_tag()); + mapping->read_barrier(addr, size, lock, header_to_size); +} + +inline void do_encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping) +{ + LockGuard lock(mapping_mutex); + mapping->write_barrier(addr, size); +} + + +#else +void inline encryption_read_barrier(const void*, size_t, EncryptedFileMapping*, HeaderToSize header_to_size = nullptr) +{ + static_cast(header_to_size); +} +void inline encryption_write_barrier(const void*, size_t) +{ +} +void inline encryption_write_barrier(const void*, size_t, EncryptedFileMapping*) +{ +} +#endif + +// helpers for encrypted Maps +template +void encryption_read_barrier(File::Map& map, size_t index, size_t num_elements = 1) +{ + T* addr = map.get_addr(); + encryption_read_barrier(addr + index, sizeof(T) * num_elements, map.get_encrypted_mapping()); +} + +template +void encryption_write_barrier(File::Map& map, size_t index, size_t num_elements = 1) +{ + T* addr = map.get_addr(); + encryption_write_barrier(addr + index, sizeof(T) * num_elements, map.get_encrypted_mapping()); +} + +File::SizeType encrypted_size_to_data_size(File::SizeType size) noexcept; +File::SizeType data_size_to_encrypted_size(File::SizeType size) noexcept; + +size_t round_up_to_page_size(size_t size) noexcept; +} +} +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp new file mode 100644 index 0000000..08f5d2b --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_HEX_DUMP_HPP +#define REALM_UTIL_HEX_DUMP_HPP + +#include +#include +#include +#include +#include +#include + +#include + +namespace realm { +namespace util { + +template +std::string hex_dump(const T* data, size_t size, const char* separator = " ", int min_digits = -1) +{ + using U = typename std::make_unsigned::type; + + if (min_digits < 0) + min_digits = (std::numeric_limits::digits + 3) / 4; + + std::ostringstream out; + for (const T* i = data; i != data + size; ++i) { + if (i != data) + out << separator; + out << std::setw(min_digits) << std::setfill('0') << std::hex << std::uppercase << util::promote(U(*i)); + } + return out.str(); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_HEX_DUMP_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/http.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/http.hpp new file mode 100644 index 0000000..5ad4f33 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/http.hpp @@ -0,0 +1,503 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_HTTP_HPP +#define REALM_UTIL_HTTP_HPP + +#include +#include +#include + +#include +#include +#include + +namespace realm { +namespace util { +enum class HTTPParserError { + None = 0, + ContentTooLong, + HeaderLineTooLong, + MalformedResponse, + MalformedRequest, +}; +std::error_code make_error_code(HTTPParserError); +} // namespace util +} // namespace realm + +namespace std { + template<> + struct is_error_code_enum: std::true_type {}; +} + +namespace realm { +namespace util { + +/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +/// +/// It is guaranteed that the backing integer value of this enum corresponds +/// to the numerical code representing the status. +enum class HTTPStatus { + Unknown = 0, + + Continue = 100, + SwitchingProtocols = 101, + + Ok = 200, + Created = 201, + Accepted = 202, + NonAuthoritative = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + + MultipleChoices = 300, + MovedPermanently = 301, + Found = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + SwitchProxy = 306, + TemporaryRedirect = 307, + PermanentRedirect = 308, + + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + UriTooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MisdirectedRequest = 421, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + UnavailableForLegalReasons = 451, + + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HttpVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + NotExtended = 510, + NetworkAuthenticationRequired = 511, +}; + +/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html +enum class HTTPMethod { + Options, + Get, + Head, + Post, + Put, + Delete, + Trace, + Connect, +}; + +struct CaseInsensitiveCompare { + bool operator()(const std::string& a, const std::string& b) const + { + auto cmp = [](char lhs, char rhs) { + return std::tolower(lhs, std::locale::classic()) < + std::tolower(rhs, std::locale::classic()); + }; + return std::lexicographical_compare(begin(a), end(a), begin(b), end(b), cmp); + } +}; + +/// Case-insensitive map suitable for storing HTTP headers. +using HTTPHeaders = std::map; + +struct HTTPRequest { + HTTPMethod method = HTTPMethod::Get; + HTTPHeaders headers; + std::string path; + + /// If the request object has a body, the Content-Length header MUST be + /// set to a string representation of the number of bytes in the body. + /// FIXME: Relax this restriction, and also support Transfer-Encoding + /// and other HTTP/1.1 features. + Optional body; +}; + +struct HTTPResponse { + HTTPStatus status = HTTPStatus::Unknown; + HTTPHeaders headers; + + // A body is only read from the response stream if the server sent the + // Content-Length header. + // FIXME: Support other transfer methods, including Transfer-Encoding and + // HTTP/1.1 features. + Optional body; +}; + + +/// Serialize HTTP request to output stream. +std::ostream& operator<<(std::ostream&, const HTTPRequest&); +/// Serialize HTTP response to output stream. +std::ostream& operator<<(std::ostream&, const HTTPResponse&); +/// Serialize HTTP method to output stream ("GET", "POST", etc.). +std::ostream& operator<<(std::ostream&, HTTPMethod); +/// Serialize HTTP status to output stream, include reason string ("200 OK" etc.) +std::ostream& operator<<(std::ostream&, HTTPStatus); + +struct HTTPParserBase { + // FIXME: Generally useful? + struct CallocDeleter { + void operator()(void* ptr) + { + std::free(ptr); + } + }; + + HTTPParserBase() + { + // Allocating read buffer with calloc to avoid accidentally spilling + // data from other sessions in case of a buffer overflow exploit. + m_read_buffer.reset(static_cast(std::calloc(read_buffer_size, 1))); + } + virtual ~HTTPParserBase() {} + + std::string m_write_buffer; + std::unique_ptr m_read_buffer; + Optional m_found_content_length; + static const size_t read_buffer_size = 8192; + static const size_t max_header_line_length = read_buffer_size; + + /// Parses the contents of m_read_buffer as a HTTP header line, + /// and calls on_header() as appropriate. on_header() will be called at + /// most once per invocation. + /// Returns false if the contents of m_read_buffer is not a valid HTTP + /// header line. + bool parse_header_line(size_t len); + + virtual std::error_code on_first_line(StringData line) = 0; + virtual void on_header(StringData key, StringData value) = 0; + virtual void on_body(StringData body) = 0; + virtual void on_complete(std::error_code = std::error_code{}) = 0; + + /// If the input matches a known HTTP method string, return the appropriate + /// HTTPMethod enum value. Otherwise, returns none. + static Optional parse_method_string(StringData method); + + /// Interpret line as the first line of an HTTP request. If the return value + /// is true, out_method and out_uri have been assigned the appropriate + /// values found in the request line. + static bool parse_first_line_of_request(StringData line, HTTPMethod& out_method, + StringData& out_uri); + + /// Interpret line as the first line of an HTTP response. If the return + /// value is true, out_status and out_reason have been assigned the + /// appropriate values found in the response line. + static bool parse_first_line_of_response(StringData line, HTTPStatus& out_status, + StringData& out_reason); + + void set_write_buffer(const HTTPRequest&); + void set_write_buffer(const HTTPResponse&); +}; + +template +struct HTTPParser: protected HTTPParserBase { + explicit HTTPParser(Socket& socket) : m_socket(socket) + {} + + void read_first_line() + { + auto handler = [this](std::error_code ec, size_t n) { + if (ec == error::operation_aborted) { + return; + } + if (ec) { + on_complete(ec); + return; + } + ec = on_first_line(StringData(m_read_buffer.get(), n)); + if (ec) { + on_complete(ec); + return; + } + read_headers(); + }; + m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n', + std::move(handler)); + } + + void read_headers() + { + auto handler = [this](std::error_code ec, size_t n) { + if (ec == error::operation_aborted) { + return; + } + if (ec) { + on_complete(ec); + return; + } + if (n <= 2) { + read_body(); + return; + } + parse_header_line(n); + // FIXME: Limit the total size of headers. Apache uses 8K. + read_headers(); + }; + m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n', + std::move(handler)); + } + + void read_body() + { + if (m_found_content_length) { + // FIXME: Support longer bodies. + // FIXME: Support multipart and other body types (no body shaming). + if (*m_found_content_length > read_buffer_size) { + on_complete(HTTPParserError::ContentTooLong); + return; + } + + auto handler = [this](std::error_code ec, size_t n) { + if (ec == error::operation_aborted) { + return; + } + if (!ec) { + on_body(StringData(m_read_buffer.get(), n)); + } + on_complete(ec); + }; + m_socket.async_read(m_read_buffer.get(), *m_found_content_length, + std::move(handler)); + } + else { + // No body, just finish. + on_complete(); + } + } + + void write_buffer(std::function handler) + { + m_socket.async_write(m_write_buffer.data(), m_write_buffer.size(), + std::move(handler)); + } + + Socket& m_socket; +}; + +template +struct HTTPClient: protected HTTPParser { + using Handler = void(HTTPResponse, std::error_code); + + explicit HTTPClient(Socket& socket) : HTTPParser(socket) {} + + /// Serialize and send \a request over the connected socket asynchronously. + /// + /// When the response has been received, or an error occurs, \a handler will + /// be invoked with the appropriate parameters. The HTTPResponse object + /// passed to \a handler will only be complete in non-error conditions, but + /// may be partially populated. + /// + /// It is an error to start a request before the \a handler of a previous + /// request has been invoked. It is permitted to call async_request() from + /// the handler, unless an error has been reported representing a condition + /// where the underlying socket is no longer able to communicate (for + /// example, if it has been closed). + /// + /// If a request is already in progress, an exception will be thrown. + /// + /// This method is *NOT* thread-safe. + void async_request(const HTTPRequest& request, std::function handler) + { + if (REALM_UNLIKELY(m_handler)) { + throw std::runtime_error("Request already in progress."); + } + this->set_write_buffer(request); + m_handler = std::move(handler); + this->write_buffer([this](std::error_code ec, size_t bytes_written) { + static_cast(bytes_written); + if (ec == error::operation_aborted) { + return; + } + if (ec) { + this->on_complete(ec); + return; + } + this->read_first_line(); + }); + } + +private: + std::function m_handler; + HTTPResponse m_response; + + std::error_code on_first_line(StringData line) override final + { + HTTPStatus status; + StringData reason; + if (this->parse_first_line_of_response(line, status, reason)) { + m_response.status = status; + static_cast(reason); // Ignore for now. + return std::error_code{}; + } + return HTTPParserError::MalformedResponse; + } + + void on_header(StringData key, StringData value) override final + { + // FIXME: Multiple headers with the same key should show up as a + // comma-separated list of their values, rather than overwriting. + m_response.headers[std::string(key)] = std::string(value); + } + + void on_body(StringData body) override final + { + m_response.body = std::string(body); + } + + void on_complete(std::error_code ec) override final + { + auto handler = std::move(m_handler); // Nullifies m_handler + handler(std::move(m_response), ec); + } +}; + +template +struct HTTPServer: protected HTTPParser { + using RequestHandler = void(HTTPRequest, std::error_code); + using RespondHandler = void(std::error_code); + + explicit HTTPServer(Socket& socket): HTTPParser(socket) + {} + + /// Receive a request on the underlying socket asynchronously. + /// + /// This function starts an asynchronous read operation and keeps reading + /// until an HTTP request has been received. \a handler is invoked when a + /// request has been received, or an error occurs. + /// + /// After a request is received, callers MUST invoke async_send_response() + /// to provide the client with a valid HTTP response, unless the error + /// passed to the handler represents a condition where the underlying socket + /// is no longer able to communicate (for example, if it has been closed). + /// + /// It is an error to attempt to receive a request before any previous + /// requests have been fully responded to, i.e. the \a handler argument of + /// async_send_response() must have been invoked before attempting to + /// receive the next request. + /// + /// This function is *NOT* thread-safe. + void async_receive_request(std::function handler) + { + if (REALM_UNLIKELY(m_request_handler)) { + throw std::runtime_error("Response already in progress."); + } + m_request_handler = std::move(handler); + this->read_first_line(); + } + + /// Send an HTTP response to a client asynchronously. + /// + /// This function starts an asynchronous write operation on the underlying + /// socket. \a handler is invoked when the response has been written to the + /// socket, or an error occurs. + /// + /// It is an error to call async_receive_request() again before \a handler + /// has been invoked, and it is an error to call async_send_response() + /// before the \a handler of a previous invocation has been invoked. + /// + /// This function is *NOT* thread-safe. + void async_send_response(const HTTPResponse& response, + std::function handler) + { + if (REALM_UNLIKELY(!m_request_handler)) { + throw std::runtime_error("No request in progress."); + } + if (m_respond_handler) { + // FIXME: Proper exception type. + throw std::runtime_error("Already responding to request"); + } + m_respond_handler = std::move(handler); + this->set_write_buffer(response); + this->write_buffer([this](std::error_code ec, size_t) { + if (ec == error::operation_aborted) { + return; + } + m_request_handler = nullptr; + auto handler = std::move(m_respond_handler); + handler(ec); + });; + } + +private: + std::function m_request_handler; + std::function m_respond_handler; + HTTPRequest m_request; + + std::error_code on_first_line(StringData line) override final + { + HTTPMethod method; + StringData uri; + if (this->parse_first_line_of_request(line, method, uri)) { + m_request.method = method; + m_request.path = uri; + return std::error_code{}; + } + return HTTPParserError::MalformedRequest; + } + + void on_header(StringData key, StringData value) override final + { + // FIXME: Multiple headers with the same key should show up as a + // comma-separated list of their values, rather than overwriting. + m_request.headers[std::string(key)] = std::string(value); + } + + void on_body(StringData body) override final + { + m_request.body = std::string(body); + } + + void on_complete(std::error_code ec) override final + { + // Deliberately not nullifying m_request_handler so that we can + // check for invariants in async_send_response. + m_request_handler(std::move(m_request), ec); + } +}; + +} // namespace util +} // namespace realm + + +#endif // REALM_UTIL_HTTP_HPP + diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/inspect.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/inspect.hpp new file mode 100644 index 0000000..84a669d --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/inspect.hpp @@ -0,0 +1,76 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_INSPECT_HPP +#define REALM_UTIL_INSPECT_HPP + +#include + +namespace realm { +namespace util { + +// LCOV_EXCL_START +// +// Because these are templated functions, every combination of output stream +// type and value(s) type(s) generates a new function. This makes LCOV/GCOVR +// report over 70 functions in this file, with only 6.6% function coverage, +// even though line coverage is at 100%. + +template +void inspect_value(OS& os, const T& value) +{ + os << value; +} + +template +void inspect_value(OS& os, const std::string& value) +{ + // FIXME: Escape the string. + os << "\"" << value << "\""; +} + +template +void inspect_value(OS& os, const char* value) +{ + // FIXME: Escape the string. + os << "\"" << value << "\""; +} + +template +void inspect_all(OS&) +{ + // No-op +} + +/// Convert all arguments to strings, and quote string arguments. +template +void inspect_all(OS& os, First&& first, Args&&... args) +{ + inspect_value(os, std::forward(first)); + if (sizeof...(Args) != 0) { + os << ", "; + } + inspect_all(os, std::forward(args)...); +} + +// LCOV_EXCL_STOP + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_INSPECT_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp new file mode 100644 index 0000000..8f803ee --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp @@ -0,0 +1,131 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_INTERPROCESS_CONDVAR +#define REALM_UTIL_INTERPROCESS_CONDVAR + + +#include +#include +#include +#include +#include +#include +#include +#include + +// Condvar Emulation is required if RobustMutex emulation is enabled +#ifdef REALM_ROBUST_MUTEX_EMULATION +#define REALM_CONDVAR_EMULATION +#endif + +namespace realm { +namespace util { + + +/// Condition variable for use in synchronization monitors. +/// This condition variable uses emulation based on named pipes +/// for the inter-process case, if enabled by REALM_CONDVAR_EMULATION. +/// +/// FIXME: This implementation will never release/delete pipes. This is unlikely +/// to be a problem as long as only a modest number of different database names +/// are in use +/// +/// A InterprocessCondVar is always process shared. +class InterprocessCondVar { +public: + InterprocessCondVar(); + ~InterprocessCondVar() noexcept; + + // Disable copying. Copying an open file will create a scenario + // where the same file descriptor will be opened once but closed twice. + InterprocessCondVar(const InterprocessCondVar&) = delete; + InterprocessCondVar& operator=(const InterprocessCondVar&) = delete; + +/// To use the InterprocessCondVar, you also must place a structure of type +/// InterprocessCondVar::SharedPart in memory shared by multiple processes +/// or in a memory mapped file, and use set_shared_part() to associate +/// the condition variable with it's shared part. You must initialize +/// the shared part using InterprocessCondVar::init_shared_part(), but only before +/// first use and only when you have exclusive access to the shared part. + +#ifdef REALM_CONDVAR_EMULATION + struct SharedPart { + uint64_t signal_counter; + uint64_t wait_counter; + }; +#else + typedef CondVar SharedPart; +#endif + + /// You need to bind the emulation to a SharedPart in shared/mmapped memory. + /// The SharedPart is assumed to have been initialized (possibly by another process) + /// earlier through a call to init_shared_part. + void set_shared_part(SharedPart& shared_part, std::string path, std::string condvar_name, std::string tmp_path); + + /// Initialize the shared part of a process shared condition variable. + /// A process shared condition variables may be represented by any number of + /// InterprocessCondVar instances in any number of different processes, + /// all sharing a common SharedPart instance, which must be in shared memory. + static void init_shared_part(SharedPart& shared_part); + + /// Release any system resources allocated for the shared part. This should + /// be used *only* when you are certain, that nobody is using it. + void release_shared_part(); + + /// Wait for someone to call notify() or notify_all() on this condition + /// variable. The call to wait() may return spuriously, so the caller should + /// always re-evaluate the condition on which to wait and loop on wait() + /// if necessary. + void wait(InterprocessMutex& m, const struct timespec* tp); + + /// If any threads are waiting for this condition, wake up at least one. + /// (Current implementation may actually wake all :-O ). The caller must + /// hold the lock associated with the condvar at the time of calling notify() + void notify() noexcept; + + /// Wake up every thread that is currently waiting on this condition. + /// The caller must hold the lock associated with the condvar at the time + /// of calling notify_all(). + void notify_all() noexcept; + + /// Cleanup and release system resources if possible. + void close() noexcept; + +private: + // non-zero if a shared part has been registered (always 0 on process local instances) + SharedPart* m_shared_part = nullptr; +#ifdef REALM_CONDVAR_EMULATION + // keep the path to allocated system resource so we can remove them again + std::string m_resource_path; + // pipe used for emulation. When using a named pipe, m_fd_read is read-write and m_fd_write is unused. + // When using an anonymous pipe (currently only for tvOS) m_fd_read is read-only and m_fd_write is write-only. + int m_fd_read = -1; + int m_fd_write = -1; +#endif +}; + + +// Implementation: + + +} // namespace util +} // namespace realm + + +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp new file mode 100644 index 0000000..c8fcc41 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp @@ -0,0 +1,318 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_INTERPROCESS_MUTEX +#define REALM_UTIL_INTERPROCESS_MUTEX + +#include +#include +#include +#include +#include +#include + +// Enable this only on platforms where it might be needed +#if REALM_PLATFORM_APPLE || REALM_ANDROID +#define REALM_ROBUST_MUTEX_EMULATION +#endif + +namespace realm { +namespace util { + +// fwd decl to support friend decl below +class InterprocessCondVar; + + +/// Emulation of a Robust Mutex. +/// A Robust Mutex is an interprocess mutex which will automatically +/// release any locks held by a process when it crashes. Contrary to +/// Posix robust mutexes, this robust mutex is not capable of informing +/// participants that they have been granted a lock after a crash of +/// the process holding it (though it could be added if needed). + +class InterprocessMutex { +public: + InterprocessMutex(); + ~InterprocessMutex() noexcept; + + // Disable copying. Copying a locked Mutex will create a scenario + // where the same file descriptor will be locked once but unlocked twice. + InterprocessMutex(const InterprocessMutex&) = delete; + InterprocessMutex& operator=(const InterprocessMutex&) = delete; + +#ifdef REALM_ROBUST_MUTEX_EMULATION + struct SharedPart { + }; +#else + using SharedPart = RobustMutex; +#endif + + /// You need to bind the emulation to a SharedPart in shared/mmapped memory. + /// The SharedPart is assumed to have been initialized (possibly by another process) + /// elsewhere. + void set_shared_part(SharedPart& shared_part, const std::string& path, const std::string& mutex_name); + void set_shared_part(SharedPart& shared_part, File&& lock_file); + + /// Destroy shared object. Potentially release system resources. Caller must + /// ensure that the shared_part is not in use at the point of call. + void release_shared_part(); + + /// Lock the mutex. If the mutex is already locked, wait for it to be unlocked. + void lock(); + + /// Non-blocking attempt to lock the mutex. Returns true if the lock is obtained. + /// If the lock can not be obtained return false immediately. + bool try_lock(); + + /// Unlock the mutex + void unlock(); + + /// Attempt to check if the mutex is valid (only relevant if not emulating) + bool is_valid() noexcept; + + static bool is_robust_on_this_platform() + { +#ifdef REALM_ROBUST_MUTEX_EMULATION + return true; // we're faking it! +#else + return RobustMutex::is_robust_on_this_platform(); +#endif + } + +private: +#ifdef REALM_ROBUST_MUTEX_EMULATION + struct LockInfo { + File m_file; + Mutex m_local_mutex; + LockInfo() {} + ~LockInfo() noexcept; + // Disable copying. + LockInfo(const LockInfo&) = delete; + LockInfo& operator=(const LockInfo&) = delete; + }; + /// InterprocessMutex created on the same file (same inode on POSIX) share the same LockInfo. + /// LockInfo will be saved in a static map as a weak ptr and use the UniqueID as the key. + /// Operations on the map need to be protected by s_mutex + static std::map>* s_info_map; + static Mutex* s_mutex; + /// We manually initialize these static variables when first needed, + /// creating them on the heap so that they last for the entire lifetime + /// of the process. The destructor of these is never called; the + /// process will clean up their memory when exiting. It is not enough + /// to count instances of InterprocessMutex and clean up these statics when + /// the count reaches zero because the program can create more + /// InterprocessMutex instances before the process ends, so we really need + /// these variables for the entire lifetime of the process. + static std::once_flag s_init_flag; + static void initialize_statics(); + + /// Only used for release_shared_part + std::string m_filename; + File::UniqueID m_fileuid; + std::shared_ptr m_lock_info; + + /// Free the lock info hold by this instance. + /// If it is the last reference, underly resources will be freed as well. + void free_lock_info(); +#else + SharedPart* m_shared_part = nullptr; +#endif + friend class InterprocessCondVar; +}; + +inline InterprocessMutex::InterprocessMutex() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + std::call_once(s_init_flag, initialize_statics); +#endif +} + +inline InterprocessMutex::~InterprocessMutex() noexcept +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + free_lock_info(); +#endif +} + +#ifdef REALM_ROBUST_MUTEX_EMULATION +inline InterprocessMutex::LockInfo::~LockInfo() noexcept +{ + if (m_file.is_attached()) { + m_file.close(); + } +} + +inline void InterprocessMutex::free_lock_info() +{ + // It has not been initialized yet. + if (!m_lock_info) + return; + + std::lock_guard guard(*s_mutex); + + m_lock_info.reset(); + if ((*s_info_map)[m_fileuid].expired()) { + s_info_map->erase(m_fileuid); + } + m_filename.clear(); +} + +inline void InterprocessMutex::initialize_statics() +{ + s_mutex = new Mutex(); + s_info_map = new std::map>(); +} +#endif + +inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, const std::string& path, + const std::string& mutex_name) +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + static_cast(shared_part); + + free_lock_info(); + + m_filename = path + "." + mutex_name + ".mx"; + + std::lock_guard guard(*s_mutex); + + // Try to get the file uid if the file exists + if (File::get_unique_id(m_filename, m_fileuid)) { + auto result = s_info_map->find(m_fileuid); + if (result != s_info_map->end()) { + // File exists and the lock info has been created in the map. + m_lock_info = result->second.lock(); + return; + } + } + + // LockInfo has not been created yet. + m_lock_info = std::make_shared(); + // Always use mod_Write to open file and retreive the uid in case other process + // deletes the file. + m_lock_info->m_file.open(m_filename, File::mode_Write); + m_fileuid = m_lock_info->m_file.get_unique_id(); + + (*s_info_map)[m_fileuid] = m_lock_info; +#else + m_shared_part = &shared_part; + static_cast(path); + static_cast(mutex_name); +#endif +} + +inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, File&& lock_file) +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + static_cast(shared_part); + + free_lock_info(); + + std::lock_guard guard(*s_mutex); + + m_fileuid = lock_file.get_unique_id(); + auto result = s_info_map->find(m_fileuid); + if (result == s_info_map->end()) { + m_lock_info = std::make_shared(); + m_lock_info->m_file = std::move(lock_file); + (*s_info_map)[m_fileuid] = m_lock_info; + } + else { + // File exists and the lock info has been created in the map. + m_lock_info = result->second.lock(); + lock_file.close(); + } +#else + m_shared_part = &shared_part; + static_cast(lock_file); +#endif +} + +inline void InterprocessMutex::release_shared_part() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + if (!m_filename.empty()) + File::try_remove(m_filename); + + free_lock_info(); +#else + m_shared_part = nullptr; +#endif +} + +inline void InterprocessMutex::lock() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + std::unique_lock mutex_lock(m_lock_info->m_local_mutex); + m_lock_info->m_file.lock_exclusive(); + mutex_lock.release(); +#else + REALM_ASSERT(m_shared_part); + m_shared_part->lock([]() {}); +#endif +} + +inline bool InterprocessMutex::try_lock() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + std::unique_lock mutex_lock(m_lock_info->m_local_mutex, std::try_to_lock_t()); + if (!mutex_lock.owns_lock()) { + return false; + } + bool success = m_lock_info->m_file.try_lock_exclusive(); + if (success) { + mutex_lock.release(); + return true; + } + else { + return false; + } +#else + REALM_ASSERT(m_shared_part); + return m_shared_part->try_lock([]() {}); +#endif +} + + +inline void InterprocessMutex::unlock() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + m_lock_info->m_file.unlock(); + m_lock_info->m_local_mutex.unlock(); +#else + REALM_ASSERT(m_shared_part); + m_shared_part->unlock(); +#endif +} + + +inline bool InterprocessMutex::is_valid() noexcept +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + return true; +#else + REALM_ASSERT(m_shared_part); + return m_shared_part->is_valid(); +#endif +} + + +} // namespace util +} // namespace realm + +#endif // #ifndef REALM_UTIL_INTERPROCESS_MUTEX diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/json_parser.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/json_parser.hpp new file mode 100644 index 0000000..48c4eb3 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/json_parser.hpp @@ -0,0 +1,544 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_JSON_PARSER_HPP +#define REALM_UTIL_JSON_PARSER_HPP + +#include +#include +#include + +#include + +namespace realm { +namespace util { + +/// A JSON parser that neither allocates heap memory nor throws exceptions. +/// +/// The parser takes as input a range of characters, and emits a stream of events +/// representing the structure of the JSON document. +/// +/// Parser errors are represented as `std::error_condition`s. +class JSONParser { +public: + using InputIterator = const char*; + + enum class EventType { + number, + string, + boolean, + null, + array_begin, + array_end, + object_begin, + object_end + }; + + using Range = StringData; + + struct Event { + EventType type; + Range range; + Event(EventType type): type(type) {} + + union { + bool boolean; + double number; + }; + + StringData escaped_string_value() const noexcept; + + /// Unescape the string value into \a buffer. + /// The type of this event must be EventType::string. + /// + /// \param buffer is a pointer to a buffer big enough to hold the + /// unescaped string value. The unescaped string is guaranteed to be + /// shorter than the escaped string, so escaped_string_value().size() can + /// be used as an upper bound. Unicode sequences of the form "\uXXXX" + /// will be converted to UTF-8 sequences. Note that the escaped form of + /// a unicode point takes exactly 6 bytes, which is also the maximum + /// possible length of a UTF-8 encoded codepoint. + StringData unescape_string(char* buffer) const noexcept; + }; + + enum class Error { + unexpected_token = 1, + unexpected_end_of_stream = 2 + }; + + JSONParser(StringData); + + /// Parse the input data, and call f repeatedly with an argument of type Event + /// representing the token that the parser encountered. + /// + /// The stream of events is "flat", which is to say that it is the responsibility + /// of the function f to keep track of any nested object structures as it deems + /// appropriate. + /// + /// This function is guaranteed to never throw, as long as f never throws. + template + std::error_condition parse(F&& f) noexcept(noexcept(f(std::declval()))); + + class ErrorCategory: public std::error_category { + public: + const char* name() const noexcept final; + std::string message(int) const final; + }; + static const ErrorCategory error_category; +private: + enum Token: char { + object_begin = '{', + object_end = '}', + array_begin = '[', + array_end = ']', + colon = ':', + comma = ',', + dquote = '"', + escape = '\\', + minus = '-', + space = ' ', + tab = '\t', + cr = '\r', + lf = '\n', + }; + + InputIterator m_current; + InputIterator m_end; + + template + std::error_condition parse_object(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_pair(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_array(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_number(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_string(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_value(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_boolean(F&& f) noexcept(noexcept(f(std::declval()))); + template + std::error_condition parse_null(F&& f) noexcept(noexcept(f(std::declval()))); + + std::error_condition expect_token(char, Range& out_range) noexcept; + std::error_condition expect_token(Token, Range& out_range) noexcept; + + // Returns true unless EOF was reached. + bool peek_char(char& out_c) noexcept; + bool peek_token(Token& out_t) noexcept; + bool is_whitespace(Token t) noexcept; + void skip_whitespace() noexcept; +}; + +std::error_condition make_error_condition(JSONParser::Error e); + +} // namespace util +} // namespace realm + +namespace std { +template<> +struct is_error_condition_enum { + static const bool value = true; +}; +} + +namespace realm { +namespace util { + +/// Implementation: + + +inline JSONParser::JSONParser(StringData input): + m_current(input.data()), m_end(input.data() + input.size()) +{ +} + +template +std::error_condition JSONParser::parse(F&& f) noexcept(noexcept(f(std::declval()))) +{ + return parse_value(f); +} + +template +std::error_condition JSONParser::parse_object(F&& f) noexcept(noexcept(f(std::declval()))) +{ + Event event{EventType::object_begin}; + auto ec = expect_token(Token::object_begin, event.range); + if (ec) + return ec; + ec = f(event); + if (ec) + return ec; + + while (true) { + ec = expect_token(Token::object_end, event.range); + if (!ec) { + // End of object + event.type = EventType::object_end; + ec = f(event); + if (ec) + return ec; + break; + } + + if (ec != Error::unexpected_token) + return ec; + + ec = parse_pair(f); + if (ec) + return ec; + + skip_whitespace(); + + Token t; + if (peek_token(t)) { + if (t == Token::object_end) { + // Fine, will terminate on next iteration + } + else if (t == Token::comma) + ++m_current; // OK, because peek_char returned true + else + return Error::unexpected_token; + } + else { + return Error::unexpected_end_of_stream; + } + } + + return std::error_condition{}; +} + +template +std::error_condition JSONParser::parse_pair(F&& f) noexcept(noexcept(f(std::declval()))) +{ + skip_whitespace(); + + auto ec = parse_string(f); + if (ec) + return ec; + + skip_whitespace(); + + Token t; + if (peek_token(t)) { + if (t == Token::colon) { + ++m_current; + } + else { + return Error::unexpected_token; + } + } + + return parse_value(f); +} + +template +std::error_condition JSONParser::parse_array(F&& f) noexcept(noexcept(f(std::declval()))) +{ + Event event{EventType::array_begin}; + auto ec = expect_token(Token::array_begin, event.range); + if (ec) + return ec; + ec = f(event); + if (ec) + return ec; + + while (true) { + ec = expect_token(Token::array_end, event.range); + if (!ec) { + // End of array + event.type = EventType::array_end; + ec = f(event); + if (ec) + return ec; + break; + } + + if (ec != Error::unexpected_token) + return ec; + + ec = parse_value(f); + if (ec) + return ec; + + skip_whitespace(); + + Token t; + if (peek_token(t)) { + if (t == Token::array_end) { + // Fine, will terminate next iteration. + } + else if (t == Token::comma) + ++m_current; // OK, because peek_char returned true + else + return Error::unexpected_token; + } + else { + return Error::unexpected_end_of_stream; + } + } + + return std::error_condition{}; +} + +template +std::error_condition JSONParser::parse_number(F&& f) noexcept(noexcept(f(std::declval()))) +{ + static const size_t buffer_size = 64; + char buffer[buffer_size] = {0}; + size_t bytes_to_copy = std::min(m_end - m_current, buffer_size - 1); + if (bytes_to_copy == 0) + return Error::unexpected_end_of_stream; + + if (std::isspace(*m_current)) { + // JSON has a different idea of what constitutes whitespace than isspace(), + // but strtod() uses isspace() to skip initial whitespace. We have already + // skipped whitespace that JSON considers valid, so if there is any whitespace + // at m_current now, it is invalid according to JSON, and so is an error. + return Error::unexpected_token; + } + + switch (m_current[0]) { + case 'N': + // strtod() parses "NAN", JSON does not. + case 'I': + // strtod() parses "INF", JSON does not. + case 'p': + case 'P': + // strtod() may parse exponent notation, JSON does not. + return Error::unexpected_token; + case '0': + if (bytes_to_copy > 2 && (m_current[1] == 'x' || m_current[1] == 'X')) { + // strtod() parses hexadecimal, JSON does not. + return Error::unexpected_token; + } + } + + std::copy(m_current, m_current + bytes_to_copy, buffer); + + char* endp = nullptr; + Event event{EventType::number}; + event.number = std::strtod(buffer, &endp); + + if (endp == buffer) { + return Error::unexpected_token; + } + size_t num_bytes_consumed = endp - buffer; + m_current += num_bytes_consumed; + return f(event); +} + +template +std::error_condition JSONParser::parse_string(F&& f) noexcept(noexcept(f(std::declval()))) +{ + InputIterator p = m_current; + if (p >= m_end) + return Error::unexpected_end_of_stream; + + auto count_num_escapes_backwards = [](const char* p, const char* begin) -> size_t { + size_t result = 0; + for (; p > begin && *p == Token::escape; ++p) + ++result; + return result; + }; + + Token t = static_cast(*p); + InputIterator inner_end; + if (t == Token::dquote) { + inner_end = m_current; + do { + inner_end = std::find(inner_end + 1, m_end, Token::dquote); + if (inner_end == m_end) + return Error::unexpected_end_of_stream; + } while (count_num_escapes_backwards(inner_end - 1, m_current) % 2 == 1); + + Event event{EventType::string}; + event.range = Range(m_current, inner_end - m_current + 1); + m_current = inner_end + 1; + return f(event); + } + return Error::unexpected_token; +} + +template +std::error_condition JSONParser::parse_boolean(F&& f) noexcept(noexcept(f(std::declval()))) +{ + auto first_nonalpha = std::find_if_not(m_current, m_end, [](auto c) { return std::isalpha(c); }); + + Event event{EventType::boolean}; + event.range = Range(m_current, first_nonalpha - m_current); + if (event.range == "true") { + event.boolean = true; + m_current += 4; + return f(event); + } + else if (event.range == "false") { + event.boolean = false; + m_current += 5; + return f(event); + } + + return Error::unexpected_token; +} + +template +std::error_condition JSONParser::parse_null(F&& f) noexcept(noexcept(f(std::declval()))) +{ + auto first_nonalpha = std::find_if_not(m_current, m_end, [](auto c) { return std::isalpha(c); }); + + Event event{EventType::null}; + event.range = Range(m_current, first_nonalpha - m_current); + if (event.range == "null") { + m_current += 4; + return f(event); + } + + return Error::unexpected_token; +} + +template +std::error_condition JSONParser::parse_value(F&& f) noexcept(noexcept(f(std::declval()))) +{ + skip_whitespace(); + + if (m_current >= m_end) + return Error::unexpected_end_of_stream; + + if (*m_current == Token::object_begin) + return parse_object(f); + + if (*m_current == Token::array_begin) + return parse_array(f); + + if (*m_current == 't' || *m_current == 'f') + return parse_boolean(f); + + if (*m_current == 'n') + return parse_null(f); + + if (*m_current == Token::dquote) + return parse_string(f); + + return parse_number(f); +} + +inline +bool JSONParser::is_whitespace(Token t) noexcept +{ + switch (t) { + case Token::space: + case Token::tab: + case Token::cr: + case Token::lf: + return true; + default: + return false; + } +} + +inline +void JSONParser::skip_whitespace() noexcept +{ + while (m_current < m_end && is_whitespace(static_cast(*m_current))) + ++m_current; +} + +inline +std::error_condition JSONParser::expect_token(char c, Range& out_range) noexcept +{ + skip_whitespace(); + if (m_current == m_end) + return Error::unexpected_end_of_stream; + if (*m_current == c) { + out_range = Range(m_current, 1); + ++m_current; + return std::error_condition{}; + } + return Error::unexpected_token; +} + +inline +std::error_condition JSONParser::expect_token(Token t, Range& out_range) noexcept +{ + return expect_token(static_cast(t), out_range); +} + +inline +bool JSONParser::peek_char(char& out_c) noexcept +{ + if (m_current < m_end) { + out_c = *m_current; + return true; + } + return false; +} + +inline +bool JSONParser::peek_token(Token& out_t) noexcept +{ + if (m_current < m_end) { + out_t = static_cast(*m_current); + return true; + } + return false; +} + +inline +StringData JSONParser::Event::escaped_string_value() const noexcept +{ + REALM_ASSERT(type == EventType::string); + REALM_ASSERT(range.size() >= 2); + return StringData(range.data() + 1, range.size() - 2); +} + +template +OS& operator<<(OS& os, JSONParser::EventType type) +{ + switch (type) { + case JSONParser::EventType::number: os << "number"; return os; + case JSONParser::EventType::string: os << "string"; return os; + case JSONParser::EventType::boolean: os << "boolean"; return os; + case JSONParser::EventType::null: os << "null"; return os; + case JSONParser::EventType::array_begin: os << "["; return os; + case JSONParser::EventType::array_end: os << "]"; return os; + case JSONParser::EventType::object_begin: os << "{"; return os; + case JSONParser::EventType::object_end: os << "}"; return os; + } + REALM_UNREACHABLE(); +} + +template +OS& operator<<(OS& os, const JSONParser::Event& e) { + os << e.type; + switch (e.type) { + case JSONParser::EventType::number: return os << "(" << e.number << ")"; + case JSONParser::EventType::string: return os << "(" << e.range << ")"; + case JSONParser::EventType::boolean: return os << "(" << e.boolean << ")"; + default: return os; + } +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_JSON_PARSER_HPP + diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/logger.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/logger.hpp new file mode 100644 index 0000000..0946208 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/logger.hpp @@ -0,0 +1,511 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_LOGGER_HPP +#define REALM_UTIL_LOGGER_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace realm { +namespace util { + + +/// All Logger objects store a reference to a LevelThreshold object which it +/// uses to efficiently query about the current log level threshold +/// (`level_threshold.get()`). All messages logged with a level that is lower +/// than the current threshold will be dropped. For the sake of efficiency, this +/// test happens before the message is formatted. +/// +/// A logger is not inherently thread-safe, but specific implementations can be +/// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation +/// of do_log() must be thread-safe and the referenced LevelThreshold object +/// must have a thread-safe get() method. +/// +/// Examples: +/// +/// logger.error("Overlong message from master coordinator"); +/// logger.info("Listening for peers on %1:%2", listen_address, listen_port); +class Logger { +public: + template + void trace(const char* message, Params&&...); + template + void debug(const char* message, Params&&...); + template + void detail(const char* message, Params&&...); + template + void info(const char* message, Params&&...); + template + void warn(const char* message, Params&&...); + template + void error(const char* message, Params&&...); + template + void fatal(const char* message, Params&&...); + + /// Specifies criticality when passed to log(). Functions as a criticality + /// threshold when returned from LevelThreshold::get(). + /// + /// error Be silent unless when there is an error. + /// warn Be silent unless when there is an error or a warning. + /// info Reveal information about what is going on, but in a + /// minimalistic fashion to avoid general overhead from logging + /// and to keep volume down. + /// detail Same as 'info', but prioritize completeness over minimalism. + /// debug Reveal information that can aid debugging, no longer paying + /// attention to efficiency. + /// trace A version of 'debug' that allows for very high volume + /// output. + enum class Level { all, trace, debug, detail, info, warn, error, fatal, off }; + + template + void log(Level, const char* message, Params&&...); + + /// Shorthand for `int(level) >= int(level_threshold.get())`. + bool would_log(Level level) const noexcept; + + class LevelThreshold; + + const LevelThreshold& level_threshold; + + virtual ~Logger() noexcept; + +protected: + Logger(const LevelThreshold&) noexcept; + + static void do_log(Logger&, Level, std::string message); + + virtual void do_log(Level, std::string message) = 0; + + static const char* get_level_prefix(Level) noexcept; + +private: + struct State; + + template + REALM_NOINLINE void do_log(Level, const char* message, Params&&...); + void log_impl(State&); + template + void log_impl(State&, Param&&, Params&&...); + template + static void subst(State&, Param&&); +}; + +template +std::basic_ostream& operator<<(std::basic_ostream&, Logger::Level); + +template +std::basic_istream& operator>>(std::basic_istream&, Logger::Level&); + +class Logger::LevelThreshold { +public: + virtual Level get() const noexcept = 0; +}; + + +/// A root logger that is not thread-safe and allows for the log level threshold +/// to be changed over time. The initial log level threshold is +/// Logger::Level::info. +class RootLogger : private Logger::LevelThreshold, public Logger { +public: + void set_level_threshold(Level) noexcept; + +protected: + RootLogger(); + +private: + Level m_level_threshold = Level::info; + Level get() const noexcept override final; +}; + + +/// A logger that writes to STDERR. This logger is not thread-safe. +/// +/// Since this class is a RootLogger, it contains modifiable a log level +/// threshold. +class StderrLogger : public RootLogger { +protected: + void do_log(Level, std::string) override final; +}; + + +/// A logger that writes to a stream. This logger is not thread-safe. +/// +/// Since this class is a RootLogger, it contains modifiable a log level +/// threshold. +class StreamLogger : public RootLogger { +public: + explicit StreamLogger(std::ostream&) noexcept; + +protected: + void do_log(Level, std::string) override final; + +private: + std::ostream& m_out; +}; + + +/// A logger that writes to a file. This logger is not thread-safe. +/// +/// Since this class is a RootLogger, it contains modifiable a log level +/// threshold. +class FileLogger : public StreamLogger { +public: + explicit FileLogger(std::string path); + explicit FileLogger(util::File); + +private: + util::File m_file; + util::File::Streambuf m_streambuf; + std::ostream m_out; +}; + + +/// A thread-safe logger. This logger ignores the level threshold of the base +/// logger. Instead, it introduces new a LevelThreshold object with a fixed +/// value to achieve thread safety. +class ThreadSafeLogger : private Logger::LevelThreshold, public Logger { +public: + explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info); + +protected: + void do_log(Level, std::string) override final; + +private: + const Level m_level_threshold; // Immutable for thread safety + Logger& m_base_logger; + Mutex m_mutex; + Level get() const noexcept override final; +}; + + +/// A logger that adds a fixed prefix to each message. This logger inherits the +/// LevelThreshold object of the specified base logger. This logger is +/// thread-safe if, and only if the base logger is thread-safe. +class PrefixLogger : public Logger { +public: + PrefixLogger(std::string prefix, Logger& base_logger) noexcept; + +protected: + void do_log(Level, std::string) override final; + +private: + const std::string m_prefix; + Logger& m_base_logger; +}; + + +// Implementation + +struct Logger::State { + Logger::Level m_level; + std::string m_message; + std::string m_search; + int m_param_num = 1; + std::ostringstream m_formatter; + std::locale m_locale = std::locale::classic(); + State(Logger::Level level, const char* s) + : m_level(level) + , m_message(s) + , m_search(m_message) + { + m_formatter.imbue(m_locale); + } +}; + +template +inline void Logger::trace(const char* message, Params&&... params) +{ + log(Level::trace, message, std::forward(params)...); // Throws +} + +template +inline void Logger::debug(const char* message, Params&&... params) +{ + log(Level::debug, message, std::forward(params)...); // Throws +} + +template +inline void Logger::detail(const char* message, Params&&... params) +{ + log(Level::detail, message, std::forward(params)...); // Throws +} + +template +inline void Logger::info(const char* message, Params&&... params) +{ + log(Level::info, message, std::forward(params)...); // Throws +} + +template +inline void Logger::warn(const char* message, Params&&... params) +{ + log(Level::warn, message, std::forward(params)...); // Throws +} + +template +inline void Logger::error(const char* message, Params&&... params) +{ + log(Level::error, message, std::forward(params)...); // Throws +} + +template +inline void Logger::fatal(const char* message, Params&&... params) +{ + log(Level::fatal, message, std::forward(params)...); // Throws +} + +template +inline void Logger::log(Level level, const char* message, Params&&... params) +{ + if (would_log(level)) + do_log(level, message, std::forward(params)...); // Throws +} + +inline bool Logger::would_log(Level level) const noexcept +{ + return int(level) >= int(level_threshold.get()); +} + +inline Logger::~Logger() noexcept +{ +} + +inline Logger::Logger(const LevelThreshold& lt) noexcept + : level_threshold(lt) +{ +} + +inline void Logger::do_log(Logger& logger, Level level, std::string message) +{ + logger.do_log(level, std::move(message)); // Throws +} + +template +void Logger::do_log(Level level, const char* message, Params&&... params) +{ + State state(level, message); + log_impl(state, std::forward(params)...); // Throws +} + +inline void Logger::log_impl(State& state) +{ + do_log(state.m_level, std::move(state.m_message)); // Throws +} + +template +inline void Logger::log_impl(State& state, Param&& param, Params&&... params) +{ + subst(state, std::forward(param)); // Throws + log_impl(state, std::forward(params)...); // Throws +} + +template +void Logger::subst(State& state, Param&& param) +{ + state.m_formatter << "%" << state.m_param_num; + std::string key = state.m_formatter.str(); + state.m_formatter.str(std::string()); + std::string::size_type j = state.m_search.find(key); + if (j != std::string::npos) { + state.m_formatter << std::forward(param); + std::string str = state.m_formatter.str(); + state.m_formatter.str(std::string()); + state.m_message.replace(j, key.size(), str); + state.m_search.replace(j, key.size(), std::string(str.size(), '\0')); + } + ++state.m_param_num; +} + +template +std::basic_ostream& operator<<(std::basic_ostream& out, Logger::Level level) +{ + switch (level) { + case Logger::Level::all: + out << "all"; + return out; + case Logger::Level::trace: + out << "trace"; + return out; + case Logger::Level::debug: + out << "debug"; + return out; + case Logger::Level::detail: + out << "detail"; + return out; + case Logger::Level::info: + out << "info"; + return out; + case Logger::Level::warn: + out << "warn"; + return out; + case Logger::Level::error: + out << "error"; + return out; + case Logger::Level::fatal: + out << "fatal"; + return out; + case Logger::Level::off: + out << "off"; + return out; + } + REALM_ASSERT(false); + return out; +} + +template +std::basic_istream& operator>>(std::basic_istream& in, Logger::Level& level) +{ + std::basic_string str; + auto check = [&](const char* name) { + size_t n = strlen(name); + if (n != str.size()) + return false; + for (size_t i = 0; i < n; ++i) { + if (in.widen(name[i]) != str[i]) + return false; + } + return true; + }; + if (in >> str) { + if (check("all")) { + level = Logger::Level::all; + } + else if (check("trace")) { + level = Logger::Level::trace; + } + else if (check("debug")) { + level = Logger::Level::debug; + } + else if (check("detail")) { + level = Logger::Level::detail; + } + else if (check("info")) { + level = Logger::Level::info; + } + else if (check("warn")) { + level = Logger::Level::warn; + } + else if (check("error")) { + level = Logger::Level::error; + } + else if (check("fatal")) { + level = Logger::Level::fatal; + } + else if (check("off")) { + level = Logger::Level::off; + } + else { + in.setstate(std::ios_base::failbit); + } + } + return in; +} + +inline void RootLogger::set_level_threshold(Level new_level_threshold) noexcept +{ + m_level_threshold = new_level_threshold; +} + +inline RootLogger::RootLogger() + : Logger::LevelThreshold() + , Logger(static_cast(*this)) +{ +} + +inline Logger::Level RootLogger::get() const noexcept +{ + return m_level_threshold; +} + +inline void StderrLogger::do_log(Level level, std::string message) +{ + std::cerr << get_level_prefix(level) << message << '\n'; // Throws + std::cerr.flush(); // Throws +} + +inline StreamLogger::StreamLogger(std::ostream& out) noexcept + : m_out(out) +{ +} + +inline void StreamLogger::do_log(Level level, std::string message) +{ + m_out << get_level_prefix(level) << message << '\n'; // Throws + m_out.flush(); // Throws +} + +inline FileLogger::FileLogger(std::string path) + : StreamLogger(m_out) + , m_file(path, util::File::mode_Write) // Throws + , m_streambuf(&m_file) // Throws + , m_out(&m_streambuf) // Throws +{ +} + +inline FileLogger::FileLogger(util::File file) + : StreamLogger(m_out) + , m_file(std::move(file)) + , m_streambuf(&m_file) // Throws + , m_out(&m_streambuf) // Throws +{ +} + +inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level threshold) + : Logger::LevelThreshold() + , Logger(static_cast(*this)) + , m_level_threshold(threshold) + , m_base_logger(base_logger) +{ +} + +inline void ThreadSafeLogger::do_log(Level level, std::string message) +{ + LockGuard l(m_mutex); + Logger::do_log(m_base_logger, level, message); // Throws +} + +inline Logger::Level ThreadSafeLogger::get() const noexcept +{ + return m_level_threshold; +} + +inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept + : Logger(base_logger.level_threshold) + , m_prefix(std::move(prefix)) + , m_base_logger(base_logger) +{ +} + +inline void PrefixLogger::do_log(Level level, std::string message) +{ + Logger::do_log(m_base_logger, level, m_prefix + message); // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_LOGGER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp new file mode 100644 index 0000000..51584e8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp @@ -0,0 +1,212 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_MEMORY_STREAM_HPP +#define REALM_UTIL_MEMORY_STREAM_HPP + +#include +#include +#include +#include + +namespace realm { +namespace util { + +class MemoryInputStreambuf : public std::streambuf { +public: + MemoryInputStreambuf(); + ~MemoryInputStreambuf() noexcept; + + /// Behavior is undefined if the size of the specified buffer exceeds + /// PTRDIFF_MAX. + void set_buffer(const char* begin, const char* end) noexcept; + +private: + const char* m_begin; + const char* m_end; + const char* m_curr; + + int_type underflow() override; + int_type uflow() override; + int_type pbackfail(int_type) override; + std::streamsize showmanyc() override; + pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode) override; + pos_type seekpos(pos_type, std::ios_base::openmode) override; + + pos_type do_seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode); +}; + + +class MemoryOutputStreambuf : public std::streambuf { +public: + MemoryOutputStreambuf(); + ~MemoryOutputStreambuf() noexcept; + + /// Behavior is undefined if the size of the specified buffer exceeds + /// PTRDIFF_MAX. + void set_buffer(char* begin, char* end) noexcept; + + /// Returns the amount of data written to the buffer. + size_t size() const noexcept; +}; + + +class MemoryInputStream : public std::istream { +public: + MemoryInputStream(); + ~MemoryInputStream() noexcept; + + /// \{ Behavior is undefined if the size of the specified buffer exceeds + /// PTRDIFF_MAX. + void set_buffer(const char* begin, const char* end) noexcept; + template void set_buffer(const char (&buffer)[N]) noexcept; + void set_string(const std::string&) noexcept; + void set_c_string(const char* c_str) noexcept; + /// \} + +private: + MemoryInputStreambuf m_streambuf; +}; + + +class MemoryOutputStream : public std::ostream { +public: + MemoryOutputStream(); + ~MemoryOutputStream() noexcept; + + /// \{ Behavior is undefined if the size of the specified buffer exceeds + /// PTRDIFF_MAX. + void set_buffer(char* begin, char* end) noexcept; + template void set_buffer(char (&buffer)[N]) noexcept; + /// \} + + /// Returns the amount of data written to the underlying buffer. + size_t size() const noexcept; + +private: + MemoryOutputStreambuf m_streambuf; +}; + + +// Implementation + +inline MemoryInputStreambuf::MemoryInputStreambuf() + : m_begin(nullptr) + , m_end(nullptr) + , m_curr(nullptr) +{ +} + +inline MemoryInputStreambuf::~MemoryInputStreambuf() noexcept +{ +} + +inline void MemoryInputStreambuf::set_buffer(const char* begin, const char* end) noexcept +{ + m_begin = begin; + m_end = end; + m_curr = begin; +} + + +inline MemoryOutputStreambuf::MemoryOutputStreambuf() +{ +} + +inline MemoryOutputStreambuf::~MemoryOutputStreambuf() noexcept +{ +} + +inline void MemoryOutputStreambuf::set_buffer(char* begin, char* end) noexcept +{ + setp(begin, end); +} + +inline size_t MemoryOutputStreambuf::size() const noexcept +{ + return pptr() - pbase(); +} + + +inline MemoryInputStream::MemoryInputStream() + : std::istream(&m_streambuf) +{ +} + +inline MemoryInputStream::~MemoryInputStream() noexcept +{ +} + +inline void MemoryInputStream::set_buffer(const char* begin, const char* end) noexcept +{ + m_streambuf.set_buffer(begin, end); + clear(); +} + +template inline void MemoryInputStream::set_buffer(const char (&buffer)[N]) noexcept +{ + const char* begin = buffer; + const char* end = begin + N; + set_buffer(begin, end); +} + +inline void MemoryInputStream::set_string(const std::string& str) noexcept +{ + const char* begin = str.data(); + const char* end = begin + str.size(); + set_buffer(begin, end); +} + +inline void MemoryInputStream::set_c_string(const char* c_str) noexcept +{ + const char* begin = c_str; + const char* end = begin + traits_type::length(c_str); + set_buffer(begin, end); +} + + +inline MemoryOutputStream::MemoryOutputStream() + : std::ostream(&m_streambuf) +{ +} + +inline MemoryOutputStream::~MemoryOutputStream() noexcept +{ +} + +inline void MemoryOutputStream::set_buffer(char* begin, char* end) noexcept +{ + m_streambuf.set_buffer(begin, end); + clear(); +} + +template +inline void MemoryOutputStream::set_buffer(char (&buffer)[N]) noexcept +{ + set_buffer(buffer, buffer + N); +} + +inline size_t MemoryOutputStream::size() const noexcept +{ + return m_streambuf.size(); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_MEMORY_STREAM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp new file mode 100644 index 0000000..9335ba9 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp @@ -0,0 +1,49 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_MISC_ERRORS_HPP +#define REALM_UTIL_MISC_ERRORS_HPP + +#include + + +namespace realm { +namespace util { +namespace error { + +enum misc_errors { + unknown = 1, +}; + +std::error_code make_error_code(misc_errors); + +} // namespace error +} // namespace util +} // namespace realm + +namespace std { + +template <> +class is_error_code_enum { +public: + static const bool value = true; +}; + +} // namespace std + +#endif // REALM_UTIL_MISC_ERRORS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp new file mode 100644 index 0000000..c45e4f3 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp @@ -0,0 +1,49 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_MISCELLANEOUS_HPP +#define REALM_UTIL_MISCELLANEOUS_HPP + +#include + +namespace realm { +namespace util { + +// FIXME: Replace this with std::add_const_t when we switch over to C++14 by +// default. +/// \brief Adds const qualifier, unless T already has the const qualifier +template +using add_const_t = typename std::add_const::type; + +// FIXME: Replace this with std::as_const when we switch over to C++17 by +// default. +/// \brief Forms an lvalue reference to const T +template +constexpr add_const_t& as_const(T& v) noexcept +{ + return v; +} + +/// \brief Disallows rvalue arguments +template +add_const_t& as_const(const T&&) = delete; + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_MISCELLANEOUS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network.hpp new file mode 100644 index 0000000..623efe5 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network.hpp @@ -0,0 +1,3480 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_UTIL_NETWORK_HPP +#define REALM_UTIL_NETWORK_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +// Linux epoll +// +// Require Linux kernel version >= 2.6.27 such that we have epoll_create1(), +// `O_CLOEXEC`, and `EPOLLRDHUP`. +#if defined(__linux__) +# include +# if !defined(REALM_HAVE_EPOLL) +# if !defined(REALM_DISABLE_UTIL_NETWORK_EPOLL) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) +# define REALM_HAVE_EPOLL 1 +# endif +# endif +# endif +#endif +#if !defined(REALM_HAVE_EPOLL) +# define REALM_HAVE_EPOLL 0 +#endif + +// FreeBSD Kqueue. +// +// Available on Mac OS X, FreeBSD, NetBSD, OpenBSD +#if (defined(__MACH__) && defined(__APPLE__)) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) +# if !defined(REALM_HAVE_KQUEUE) +# if !defined(REALM_DISABLE_UTIL_NETWORK_KQUEUE) +# define REALM_HAVE_KQUEUE 1 +# endif +# endif +#endif +#if !defined(REALM_HAVE_KQUEUE) +# define REALM_HAVE_KQUEUE 0 +#endif + + + +// FIXME: Unfinished business around `Address::m_ip_v6_scope_id`. + + +namespace realm { +namespace util { + +/// \brief TCP/IP networking API. +/// +/// The design of this networking API is heavily inspired by the ASIO C++ +/// library (http://think-async.com). +/// +/// +/// ### Thread safety +/// +/// A *service context* is a set of objects consisting of an instance of +/// Service, and all the objects that are associated with that instance (\ref +/// Resolver, \ref Socket`, \ref Acceptor`, \ref DeadlineTimer, and \ref +/// ssl::Stream). +/// +/// In general, it is unsafe for two threads to call functions on the same +/// object, or on different objects in the same service context. This also +/// applies to destructors. Notable exceptions are the fully thread-safe +/// functions, such as Service::post(), Service::stop(), and Service::reset(). +/// +/// On the other hand, it is always safe for two threads to call functions on +/// objects belonging to different service contexts. +/// +/// One implication of these rules is that at most one thread must execute run() +/// at any given time, and if one thread is executing run(), then no other +/// thread is allowed to access objects in the same service context (with the +/// mentioned exceptions). +/// +/// Unless otherwise specified, free-standing objects, such as \ref +/// StreamProtocol, \ref Address, \ref Endpoint, and \ref Endpoint::List are +/// fully thread-safe as long as they are not mutated. If one thread is mutating +/// such an object, no other thread may access it. Note that these free-standing +/// objects are not associcated with an instance of Service, and are therefore +/// not part of a service context. +/// +/// +/// ### Comparison with ASIO +/// +/// There is a crucial difference between the two libraries in regards to the +/// guarantees that are provided about the cancelability of asynchronous +/// operations. The Realm networking library (this library) considers an +/// asynchronous operation to be complete precisely when the completion handler +/// starts to execute, and it guarantees that such an operation is cancelable up +/// until that point in time. In particular, if `cancel()` is called on a socket +/// or a deadline timer object before the completion handler starts to execute, +/// then that operation will be canceled, and will receive +/// `error::operation_aborted`. This guarantee is possible to provide (and free +/// of ambiguities) precisely because this library prohibits multiple threads +/// from executing the event loop concurrently, and because `cancel()` is +/// allowed to be called only from a completion handler (executed by the event +/// loop thread) or while no thread is executing the event loop. This guarantee +/// allows for safe destruction of sockets and deadline timers as long as the +/// completion handlers react appropriately to `error::operation_aborted`, in +/// particular, that they do not attempt to access the socket or deadline timer +/// object in such cases. +/// +/// ASIO, on the other hand, allows for an asynchronous operation to complete +/// and become **uncancellable** before the completion handler starts to +/// execute. For this reason, it is possible with ASIO to get the completion +/// handler of an asynchronous wait operation to start executing and receive an +/// error code other than "operation aborted" at a point in time where +/// `cancel()` has already been called on the deadline timer object, or even at +/// a point in timer where the deadline timer has been destroyed. This seems +/// like an inevitable consequence of the fact that ASIO allows for multiple +/// threads to execute the event loop concurrently. This generally forces ASIO +/// applications to invent ways of extending the lifetime of deadline timer and +/// socket objects until the completion handler starts executing. +/// +/// IMPORTANT: Even if ASIO is used in a way where at most one thread executes +/// the event loop, there is still no guarantee that an asynchronous operation +/// remains cancelable up until the point in time where the completion handler +/// starts to execute. +namespace network { + +std::string host_name(); + + +class StreamProtocol; +class Address; +class Endpoint; +class Service; +class Resolver; +class SocketBase; +class Socket; +class Acceptor; +class DeadlineTimer; +class ReadAheadBuffer; +namespace ssl { +class Stream; +} // namespace ssl + + +/// \brief An IP protocol descriptor. +class StreamProtocol { +public: + static StreamProtocol ip_v4(); + static StreamProtocol ip_v6(); + + bool is_ip_v4() const; + bool is_ip_v6() const; + + int protocol() const; + int family() const; + + StreamProtocol(); + ~StreamProtocol() noexcept {} + +private: + int m_family; + int m_socktype; + int m_protocol; + + friend class Resolver; + friend class SocketBase; +}; + + +/// \brief An IP address (IPv4 or IPv6). +class Address { +public: + bool is_ip_v4() const; + bool is_ip_v6() const; + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const Address&); + + Address(); + ~Address() noexcept {} + +private: + using ip_v4_type = in_addr; + using ip_v6_type = in6_addr; + union union_type { + ip_v4_type m_ip_v4; + ip_v6_type m_ip_v6; + }; + union_type m_union; + std::uint_least32_t m_ip_v6_scope_id = 0; + bool m_is_ip_v6 = false; + + friend Address make_address(const char*, std::error_code&) noexcept; + friend class Endpoint; +}; + +Address make_address(const char* c_str); +Address make_address(const char* c_str, std::error_code& ec) noexcept; +Address make_address(const std::string&); +Address make_address(const std::string&, std::error_code& ec) noexcept; + + +/// \brief An IP endpoint. +/// +/// An IP endpoint is a triplet (`protocol`, `address`, `port`). +class Endpoint { +public: + using port_type = std::uint_fast16_t; + class List; + + StreamProtocol protocol() const; + Address address() const; + port_type port() const; + + Endpoint(); + Endpoint(const StreamProtocol&, port_type); + Endpoint(const Address&, port_type); + ~Endpoint() noexcept {} + + using data_type = sockaddr; + data_type* data(); + const data_type* data() const; + +private: + StreamProtocol m_protocol; + + using sockaddr_base_type = sockaddr; + using sockaddr_ip_v4_type = sockaddr_in; + using sockaddr_ip_v6_type = sockaddr_in6; + union sockaddr_union_type { + sockaddr_base_type m_base; + sockaddr_ip_v4_type m_ip_v4; + sockaddr_ip_v6_type m_ip_v6; + }; + sockaddr_union_type m_sockaddr_union; + + friend class Service; + friend class Resolver; + friend class SocketBase; + friend class Socket; +}; + + +/// \brief A list of IP endpoints. +class Endpoint::List { +public: + using iterator = const Endpoint*; + + iterator begin() const noexcept; + iterator end() const noexcept; + std::size_t size() const noexcept; + bool empty() const noexcept; + + List() noexcept = default; + List(List&&) noexcept = default; + ~List() noexcept = default; + + List& operator=(List&&) noexcept = default; + +private: + Buffer m_endpoints; + + friend class Resolver; +}; + + +/// \brief TCP/IP networking service. +class Service { +public: + Service(); + ~Service() noexcept; + + /// \brief Execute the event loop. + /// + /// Execute completion handlers of completed asynchronous operations, or + /// wait for more completion handlers to become ready for + /// execution. Handlers submitted via post() are considered immeditely + /// ready. If there are no completion handlers ready for execution, and + /// there are no asynchronous operations in progress, run() returns. + /// + /// All completion handlers, including handlers submitted via post() will be + /// executed from run(), that is by the thread that executes run(). If no + /// thread executes run(), then the completion handlers will not be + /// executed. + /// + /// Exceptions thrown by completion handlers will always propagate back + /// through run(). + /// + /// Syncronous operations (e.g., Socket::connect()) execute independently of + /// the event loop, and do not require that any thread calls run(). + void run(); + + /// @{ \brief Stop event loop execution. + /// + /// stop() puts the event loop into the stopped mode. If a thread is currently + /// executing run(), it will be made to return in a timely fashion, that is, + /// without further blocking. If a thread is currently blocked in run(), it + /// will be unblocked. Handlers that can be executed immediately, may, or + /// may not be executed before run() returns, but new handlers submitted by + /// these, will not be executed. + /// + /// The event loop will remain in the stopped mode until reset() is + /// called. If reset() is called before run() returns, it may, or may not + /// cause run() to continue normal operation without returning. + /// + /// Both stop() and reset() are thread-safe, that is, they may be called by + /// any thread. Also, both of these function may be called from completion + /// handlers (including posted handlers). + void stop() noexcept; + void reset() noexcept; + /// @} + + /// \brief Submit a handler to be executed by the event loop thread. + /// + /// Register the sepcified completion handler for immediate asynchronous + /// execution. The specified handler will be executed by an expression on + /// the form `handler()`. If the the handler object is movable, it will + /// never be copied. Otherwise, it will be copied as necessary. + /// + /// This function is thread-safe, that is, it may be called by any + /// thread. It may also be called from other completion handlers. + /// + /// The handler will never be called as part of the execution of post(). It + /// will always be called by a thread that is executing run(). If no thread + /// is currently executing run(), the handler will not be executed until a + /// thread starts executing run(). If post() is called while another thread + /// is executing run(), the handler may be called before post() returns. If + /// post() is called from another completion handler, the submitted handler + /// is guaranteed to not be called during the execution of post(). + /// + /// Completion handlers added through post() will be executed in the order + /// that they are added. More precisely, if post() is called twice to add + /// two handlers, A and B, and the execution of post(A) ends before the + /// beginning of the execution of post(B), then A is guaranteed to execute + /// before B. + template void post(H handler); + +private: + enum class Want { nothing = 0, read, write }; + + template class OperQueue; + class Descriptor; + class AsyncOper; + class WaitOperBase; + class PostOperBase; + template class PostOper; + class IoOper; + class UnusedOper; // Allocated, but currently unused memory + + template class BasicStreamOps; + + struct OwnersOperDeleter { + void operator()(AsyncOper*) const noexcept; + }; + struct LendersOperDeleter { + void operator()(AsyncOper*) const noexcept; + }; + using OwnersOperPtr = std::unique_ptr; + using LendersOperPtr = std::unique_ptr; + using LendersWaitOperPtr = std::unique_ptr; + using LendersIoOperPtr = std::unique_ptr; + + class IoReactor; + class Impl; + const std::unique_ptr m_impl; + + template + static std::unique_ptr alloc(OwnersOperPtr&, Args&&...); + + template static void execute(std::unique_ptr&); + + using PostOperConstr = PostOperBase*(void* addr, std::size_t size, Impl&, void* cookie); + void do_post(PostOperConstr, std::size_t size, void* cookie); + template + static PostOperBase* post_oper_constr(void* addr, std::size_t size, Impl&, void* cookie); + static void recycle_post_oper(Impl&, PostOperBase*) noexcept; + + using clock = std::chrono::steady_clock; + + friend class Resolver; + friend class SocketBase; + friend class Socket; + friend class Acceptor; + friend class DeadlineTimer; + friend class ReadAheadBuffer; + friend class ssl::Stream; +}; + + +template class Service::OperQueue { +public: + using LendersOperPtr = std::unique_ptr; + bool empty() const noexcept; + void push_back(LendersOperPtr) noexcept; + template void push_back(OperQueue&) noexcept; + LendersOperPtr pop_front() noexcept; + void clear() noexcept; + OperQueue() noexcept = default; + OperQueue(OperQueue&&) noexcept; + ~OperQueue() noexcept; +private: + Oper* m_back = nullptr; + template friend class OperQueue; +}; + + +class Service::Descriptor { +public: + using native_handle_type = int; + + Impl& service_impl; + + Descriptor(Impl& service) noexcept; + ~Descriptor() noexcept; + + /// \param in_blocking_mode Must be true if, and only if the passed file + /// descriptor refers to a file description in which the file status flag + /// O_NONBLOCK is not set. + /// + /// The passed file descriptor must have the file descriptor flag FD_CLOEXEC + /// set. + void assign(int fd, bool in_blocking_mode) noexcept; + void close() noexcept; + + bool is_open() const noexcept; + + native_handle_type native_handle() const noexcept; + bool in_blocking_mode() const noexcept; + + void accept(Descriptor&, StreamProtocol, Endpoint*, std::error_code&) noexcept; + std::size_t read_some(char* buffer, std::size_t size, std::error_code&) noexcept; + std::size_t write_some(const char* data, std::size_t size, std::error_code&) noexcept; + + /// \tparam Oper An operation type inherited from IoOper with an initate() + /// function that initiates the operation and figures out whether it needs + /// to read from, or write to the underlying descriptor to + /// proceed. `initiate()` must return Want::read if the operation needs to + /// read, or Want::write if the operation needs to write. If the operation + /// completes immediately (e.g. due to a failure during initialization), + /// `initiate()` must return Want::nothing. + template + void initiate_oper(std::unique_ptr, Args&&...); + + void ensure_blocking_mode(); + void ensure_nonblocking_mode(); + +private: + int m_fd = -1; + bool m_in_blocking_mode; // Not in nonblocking mode + +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + bool m_read_ready; + bool m_write_ready; + bool m_imminent_end_of_input; // Kernel has seen the end of input + bool m_is_registered; + OperQueue m_suspended_read_ops, m_suspended_write_ops; + + void deregister_for_async() noexcept; +#endif + + bool assume_read_would_block() const noexcept; + bool assume_write_would_block() const noexcept; + + void set_read_ready(bool) noexcept; + void set_write_ready(bool) noexcept; + + void set_nonblock_flag(bool value); + void add_initiated_oper(LendersIoOperPtr, Want); + + void do_close() noexcept; + + friend class IoReactor; +}; + + +class Resolver { +public: + class Query; + + Resolver(Service&); + ~Resolver() noexcept; + + /// Thread-safe. + Service& get_service() noexcept; + + /// @{ \brief Resolve the specified query to one or more endpoints. + Endpoint::List resolve(const Query&); + Endpoint::List resolve(const Query&, std::error_code&); + /// @} + + /// \brief Perform an asynchronous resolve operation. + /// + /// Initiate an asynchronous resolve operation. The completion handler will + /// be called when the operation completes. The operation completes when it + /// succeeds, or an error occurs. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_resolve(), even when + /// async_resolve() is executed by the event loop thread. The completion + /// handler is guaranteed to be called eventually, as long as there is time + /// enough for the operation to complete or fail, and a thread is executing + /// Service::run() for long enough. + /// + /// The operation can be canceled by calling cancel(), and will be + /// automatically canceled if the resolver object is destroyed. If the + /// operation is canceled, it will fail with `error::operation_aborted`. The + /// operation remains cancelable up until the point in time where the + /// completion handler starts to execute. This means that if cancel() is + /// called before the completion handler starts to execute, then the + /// completion handler is guaranteed to have `error::operation_aborted` + /// passed to it. This is true regardless of whether cancel() is called + /// explicitly or implicitly, such as when the resolver is destroyed. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec, endpoints)` where `ec` is the error code and `endpoints` is + /// an object of type `Endpoint::List`. If the the handler object is + /// movable, it will never be copied. Otherwise, it will be copied as + /// necessary. + /// + /// It is an error to start a new resolve operation (synchronous or + /// asynchronous) while an asynchronous resolve operation is in progress via + /// the same resolver object. An asynchronous resolve operation is + /// considered complete as soon as the completion handler starts to + /// execute. This means that a new resolve operation can be started from the + /// completion handler. + template void async_resolve(Query, H handler); + + /// \brief Cancel all asynchronous operations. + /// + /// Cause all incomplete asynchronous operations, that are associated with + /// this resolver (at most one), to fail with `error::operation_aborted`. An + /// asynchronous operation is complete precisely when its completion handler + /// starts executing. + /// + /// Completion handlers of canceled operations will become immediately ready + /// to execute, but will never be executed directly as part of the execution + /// of cancel(). + /// + /// Cancellation happens automatically when the resolver object is destroyed. + void cancel() noexcept; + +private: + class ResolveOperBase; + template class ResolveOper; + + using LendersResolveOperPtr = std::unique_ptr; + + Service::Impl& m_service_impl; + + Service::OwnersOperPtr m_resolve_oper; + + void initiate_oper(LendersResolveOperPtr); +}; + + +class Resolver::Query { +public: + enum { + /// Locally bound socket endpoint (server side) + passive = AI_PASSIVE, + + /// Ignore families without a configured non-loopback address + address_configured = AI_ADDRCONFIG + }; + + Query(std::string service_port, int init_flags = passive|address_configured); + Query(const StreamProtocol&, std::string service_port, + int init_flags = passive|address_configured); + Query(std::string host_name, std::string service_port, + int init_flags = address_configured); + Query(const StreamProtocol&, std::string host_name, std::string service_port, + int init_flags = address_configured); + + ~Query() noexcept; + + int flags() const; + StreamProtocol protocol() const; + std::string host() const; + std::string service() const; + +private: + int m_flags; + StreamProtocol m_protocol; + std::string m_host; // hostname + std::string m_service; // port + + friend class Resolver; +}; + + +class SocketBase { +public: + using native_handle_type = Service::Descriptor::native_handle_type; + + ~SocketBase() noexcept; + + /// Thread-safe. + Service& get_service() noexcept; + + bool is_open() const noexcept; + native_handle_type native_handle() const noexcept; + + /// @{ \brief Open the socket for use with the specified protocol. + /// + /// It is an error to call open() on a socket that is already open. + void open(const StreamProtocol&); + std::error_code open(const StreamProtocol&, std::error_code&); + /// @} + + /// \brief Close this socket. + /// + /// If the socket is open, it will be closed. If it is already closed (or + /// never opened), this function does nothing (idempotency). + /// + /// A socket is automatically closed when destroyed. + /// + /// When the socket is closed, any incomplete asynchronous operation will be + /// canceled (as if cancel() was called). + void close() noexcept; + + /// \brief Cancel all asynchronous operations. + /// + /// Cause all incomplete asynchronous operations, that are associated with + /// this socket, to fail with `error::operation_aborted`. An asynchronous + /// operation is complete precisely when its completion handler starts + /// executing. + /// + /// Completion handlers of canceled operations will become immediately ready + /// to execute, but will never be executed directly as part of the execution + /// of cancel(). + void cancel() noexcept; + + template + void get_option(O& opt) const; + + template + std::error_code get_option(O& opt, std::error_code&) const; + + template + void set_option(const O& opt); + + template + std::error_code set_option(const O& opt, std::error_code&); + + void bind(const Endpoint&); + std::error_code bind(const Endpoint&, std::error_code&); + + Endpoint local_endpoint() const; + Endpoint local_endpoint(std::error_code&) const; + +private: + enum opt_enum { + opt_ReuseAddr, ///< `SOL_SOCKET`, `SO_REUSEADDR` + opt_Linger, ///< `SOL_SOCKET`, `SO_LINGER` + }; + + template class Option; + +public: + using reuse_address = Option; + + // linger struct defined by POSIX sys/socket.h. + struct linger_opt; + using linger = Option; + +protected: + Service::Descriptor m_desc; + +private: + StreamProtocol m_protocol; + +protected: + Service::OwnersOperPtr m_read_oper; // Read or accept + Service::OwnersOperPtr m_write_oper; // Write or connect + + SocketBase(Service&); + + const StreamProtocol& get_protocol() const noexcept; + std::error_code do_assign(const StreamProtocol&, int sock_fd, std::error_code& ec); + void do_close() noexcept; + + void get_option(opt_enum, void* value_data, std::size_t& value_size, std::error_code&) const; + void set_option(opt_enum, const void* value_data, std::size_t value_size, std::error_code&); + void map_option(opt_enum, int& level, int& option_name) const; + + friend class Acceptor; +}; + + +template class SocketBase::Option { +public: + Option(T value = T()); + T value() const; + +private: + T m_value; + + void get(const SocketBase&, std::error_code&); + void set(SocketBase&, std::error_code&) const; + + friend class SocketBase; +}; + +struct SocketBase::linger_opt { + linger_opt(bool enable, int timeout_seconds = 0) + { + m_linger.l_onoff = enable ? 1 : 0; + m_linger.l_linger = timeout_seconds; + } + + ::linger m_linger; + + operator ::linger() const { return m_linger; } + + bool enabled() const { return m_linger.l_onoff != 0; } + int timeout() const { return m_linger.l_linger; } +}; + + +/// Switching between synchronous and asynchronous operations is allowed, but +/// only in a nonoverlapping fashion. That is, a synchronous operation is not +/// allowed to run concurrently with an asynchronous one on the same +/// socket. Note that an asynchronous operation is considered to be running +/// until its completion handler starts executing. +class Socket: public SocketBase { +public: + Socket(Service&); + + /// \brief Create a socket with an already-connected native socket handle. + /// + /// This constructor is shorthand for creating the socket with the + /// one-argument constructor, and then calling the two-argument assign() + /// with the specified protocol and native handle. + Socket(Service&, const StreamProtocol&, native_handle_type); + + ~Socket() noexcept; + + void connect(const Endpoint&); + std::error_code connect(const Endpoint&, std::error_code&); + + /// @{ \brief Perform a synchronous read operation. + /// + /// read() will not return until the specified buffer is full, or an error + /// occurs. Reaching the end of input before the buffer is filled, is + /// considered an error, and will cause the operation to fail with + /// `network::end_of_input`. + /// + /// read_until() will not return until the specified buffer contains the + /// specified delimiter, or an error occurs. If the buffer is filled before + /// the delimiter is found, the operation fails with + /// `network::delim_not_found`. Otherwise, if the end of input is reached + /// before the delimiter is found, the operation fails with + /// `network::end_of_input`. If the operation succeeds, the last byte placed + /// in the buffer is the delimiter. + /// + /// The versions that take a ReadAheadBuffer argument will read through that + /// buffer. This allows for fewer larger reads on the underlying + /// socket. Since unconsumed data may be left in the read-ahead buffer after + /// a read operation returns, it is important that the same read-ahead + /// buffer is passed to the next read operation. + /// + /// The versions of read() and read_until() that do not take an + /// `std::error_code&` argument will throw std::system_error on failure. + /// + /// The versions that do take an `std::error_code&` argument will set \a ec + /// to `std::error_code()` on success, and to something else on failure. On + /// failure they will return the number of bytes placed in the specified + /// buffer before the error occured. + /// + /// \return The number of bytes places in the specified buffer upon return. + std::size_t read(char* buffer, std::size_t size); + std::size_t read(char* buffer, std::size_t size, std::error_code& ec); + std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&); + std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&, std::error_code& ec); + std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&); + std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&, + std::error_code& ec); + /// @} + + /// @{ \brief Perform a synchronous write operation. + /// + /// write() will not return until all the specified bytes have been written + /// to the socket, or an error occurs. + /// + /// The versions of write() that does not take an `std::error_code&` + /// argument will throw std::system_error on failure. When it succeeds, it + /// always returns \a size. + /// + /// The versions that does take an `std::error_code&` argument will set \a + /// ec to `std::error_code()` on success, and to something else on + /// failure. On success it returns \a size. On faulure it returns the number + /// of bytes written before the failure occured. + std::size_t write(const char* data, std::size_t size); + std::size_t write(const char* data, std::size_t size, std::error_code& ec); + /// @} + + /// @{ \brief Read at least one byte from this socket. + /// + /// If \a size is zero, both versions of read_some() will return zero + /// without blocking. Read errors may or may not be detected in this case. + /// + /// Otherwise, if \a size is greater than zero, and at least one byte is + /// immediately available, that is, without blocking, then both versions + /// will read at least one byte (but generally as many immediately available + /// bytes as will fit into the specified buffer), and return without + /// blocking. + /// + /// Otherwise, both versions will block the calling thread until at least one + /// byte becomes available, or an error occurs. + /// + /// In this context, it counts as an error, if the end of input is reached + /// before at least one byte becomes available (see + /// `network::end_of_input`). + /// + /// If no error occurs, both versions will return the number of bytes placed + /// in the specified buffer, which is generally as many as are immediately + /// available at the time when the first byte becomes available, although + /// never more than \a size. + /// + /// If no error occurs, the three-argument version will set \a ec to + /// indicate success. + /// + /// If an error occurs, the two-argument version will throw + /// `std::system_error`, while the three-argument version will set \a ec to + /// indicate the error, and return zero. + /// + /// As long as \a size is greater than zero, the two argument version will + /// always return a value that is greater than zero, while the three + /// argument version will return a value greater than zero when, and only + /// when \a ec is set to indicate success (no error, and no end of input). + std::size_t read_some(char* buffer, std::size_t size); + std::size_t read_some(char* buffer, std::size_t size, std::error_code& ec); + /// @} + + /// @{ \brief Write at least one byte to this socket. + /// + /// If \a size is zero, both versions of write_some() will return zero + /// without blocking. Write errors may or may not be detected in this case. + /// + /// Otherwise, if \a size is greater than zero, and at least one byte can be + /// written immediately, that is, without blocking, then both versions will + /// write at least one byte (but generally as many as can be written + /// immediately), and return without blocking. + /// + /// Otherwise, both versions will block the calling thread until at least one + /// byte can be written, or an error occurs. + /// + /// If no error occurs, both versions will return the number of bytes + /// written, which is generally as many as can be written immediately at the + /// time when the first byte can be written. + /// + /// If no error occurs, the three-argument version will set \a ec to + /// indicate success. + /// + /// If an error occurs, the two-argument version will throw + /// `std::system_error`, while the three-argument version will set \a ec to + /// indicate the error, and return zero. + /// + /// As long as \a size is greater than zero, the two argument version will + /// always return a value that is greater than zero, while the three + /// argument version will return a value greater than zero when, and only + /// when \a ec is set to indicate success. + std::size_t write_some(const char* data, std::size_t size); + std::size_t write_some(const char* data, std::size_t size, std::error_code&); + /// @} + + /// \brief Perform an asynchronous connect operation. + /// + /// Initiate an asynchronous connect operation. The completion handler is + /// called when the operation completes. The operation completes when the + /// connection is established, or an error occurs. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_connect(), even when + /// async_connect() is executed by the event loop thread. The completion + /// handler is guaranteed to be called eventually, as long as there is time + /// enough for the operation to complete or fail, and a thread is executing + /// Service::run() for long enough. + /// + /// The operation can be canceled by calling cancel(), and will be + /// automatically canceled if the socket is closed. If the operation is + /// canceled, it will fail with `error::operation_aborted`. The operation + /// remains cancelable up until the point in time where the completion + /// handler starts to execute. This means that if cancel() is called before + /// the completion handler starts to execute, then the completion handler is + /// guaranteed to have `error::operation_aborted` passed to it. This is true + /// regardless of whether cancel() is called explicitly or implicitly, such + /// as when the socket is destroyed. + /// + /// If the socket is not already open, it will be opened as part of the + /// connect operation as if by calling `open(ep.protocol())`. If the opening + /// operation succeeds, but the connect operation fails, the socket will be + /// left in the opened state. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec)` where `ec` is the error code. If the the handler object is + /// movable, it will never be copied. Otherwise, it will be copied as + /// necessary. + /// + /// It is an error to start a new connect operation (synchronous or + /// asynchronous) while an asynchronous connect operation is in progress. An + /// asynchronous connect operation is considered complete as soon as the + /// completion handler starts to execute. + /// + /// \param ep The remote endpoint of the connection to be established. + template void async_connect(const Endpoint& ep, H handler); + + /// @{ \brief Perform an asynchronous read operation. + /// + /// Initiate an asynchronous buffered read operation on the associated + /// socket. The completion handler will be called when the operation + /// completes, or an error occurs. + /// + /// async_read() will continue reading until the specified buffer is full, + /// or an error occurs. If the end of input is reached before the buffer is + /// filled, the operation fails with `network::end_of_input`. + /// + /// async_read_until() will continue reading until the specified buffer + /// contains the specified delimiter, or an error occurs. If the buffer is + /// filled before a delimiter is found, the operation fails with + /// `network::delim_not_found`. Otherwise, if the end of input is reached + /// before a delimiter is found, the operation fails with + /// `network::end_of_input`. Otherwise, if the operation succeeds, the last + /// byte placed in the buffer is the delimiter. + /// + /// The versions that take a ReadAheadBuffer argument will read through that + /// buffer. This allows for fewer larger reads on the underlying + /// socket. Since unconsumed data may be left in the read-ahead buffer after + /// a read operation completes, it is important that the same read-ahead + /// buffer is passed to the next read operation. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_read() or + /// async_read_until(), even when async_read() or async_read_until() is + /// executed by the event loop thread. The completion handler is guaranteed + /// to be called eventually, as long as there is time enough for the + /// operation to complete or fail, and a thread is executing Service::run() + /// for long enough. + /// + /// The operation can be canceled by calling cancel() on the associated + /// socket, and will be automatically canceled if the associated socket is + /// closed. If the operation is canceled, it will fail with + /// `error::operation_aborted`. The operation remains cancelable up until + /// the point in time where the completion handler starts to execute. This + /// means that if cancel() is called before the completion handler starts to + /// execute, then the completion handler is guaranteed to have + /// `error::operation_aborted` passed to it. This is true regardless of + /// whether cancel() is called explicitly or implicitly, such as when the + /// socket is destroyed. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec, n)` where `ec` is the error code, and `n` is the number of + /// bytes placed in the buffer (of type `std::size_t`). `n` is guaranteed to + /// be less than, or equal to \a size. If the the handler object is movable, + /// it will never be copied. Otherwise, it will be copied as necessary. + /// + /// It is an error to start a read operation before the associated socket is + /// connected. + /// + /// It is an error to start a new read operation (synchronous or + /// asynchronous) while an asynchronous read operation is in progress. An + /// asynchronous read operation is considered complete as soon as the + /// completion handler starts executing. This means that a new read + /// operation can be started from the completion handler of another + /// asynchronous buffered read operation. + template void async_read(char* buffer, std::size_t size, H handler); + template void async_read(char* buffer, std::size_t size, ReadAheadBuffer&, H handler); + template void async_read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer&, H handler); + /// @} + + /// \brief Perform an asynchronous write operation. + /// + /// Initiate an asynchronous write operation. The completion handler is + /// called when the operation completes. The operation completes when all + /// the specified bytes have been written to the socket, or an error occurs. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_write(), even when + /// async_write() is executed by the event loop thread. The completion + /// handler is guaranteed to be called eventually, as long as there is time + /// enough for the operation to complete or fail, and a thread is executing + /// Service::run() for long enough. + /// + /// The operation can be canceled by calling cancel(), and will be + /// automatically canceled if the socket is closed. If the operation is + /// canceled, it will fail with `error::operation_aborted`. The operation + /// remains cancelable up until the point in time where the completion + /// handler starts to execute. This means that if cancel() is called before + /// the completion handler starts to execute, then the completion handler is + /// guaranteed to have `error::operation_aborted` passed to it. This is true + /// regardless of whether cancel() is called explicitly or implicitly, such + /// as when the socket is destroyed. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec, n)` where `ec` is the error code, and `n` is the number of + /// bytes written (of type `std::size_t`). If the the handler object is + /// movable, it will never be copied. Otherwise, it will be copied as + /// necessary. + /// + /// It is an error to start an asynchronous write operation before the + /// socket is connected. + /// + /// It is an error to start a new write operation (synchronous or + /// asynchronous) while an asynchronous write operation is in progress. An + /// asynchronous write operation is considered complete as soon as the + /// completion handler starts to execute. This means that a new write + /// operation can be started from the completion handler of another + /// asynchronous write operation. + template void async_write(const char* data, std::size_t size, H handler); + + template void async_read_some(char* buffer, std::size_t size, H handler); + template void async_write_some(const char* data, std::size_t size, H handler); + + enum shutdown_type { + /// Shutdown the receive side of the socket. + shutdown_receive = SHUT_RD, + + /// Shutdown the send side of the socket. + shutdown_send = SHUT_WR, + + /// Shutdown both send and receive on the socket. + shutdown_both = SHUT_RDWR + }; + + /// @{ \brief Shut down the connected sockets sending and/or receiving + /// side. + /// + /// It is an error to call this function when the socket is not both open + /// and connected. + void shutdown(shutdown_type); + std::error_code shutdown(shutdown_type, std::error_code&); + /// @} + + /// @{ \brief Initialize socket with an already-connected native socket + /// handle. + /// + /// The specified native handle must refer to a socket that is already fully + /// open and connected. + /// + /// If the assignment operation succeeds, this socket object has taken + /// ownership of the specified native handle, and the handle will be closed + /// when the socket object is destroyed, (or when close() is called). If the + /// operation fails, the caller still owns the specified native handle. + /// + /// It is an error to call connect() or async_connect() on a socket object + /// that is initialized this way (unless it is first closed). + /// + /// It is an error to call this function on a socket object that is already + /// open. + void assign(const StreamProtocol&, native_handle_type); + std::error_code assign(const StreamProtocol&, native_handle_type, std::error_code&); + /// @} + + /// Returns a reference to this socket, as this socket is the lowest layer + /// of a stream. + Socket& lowest_layer() noexcept; + +private: + using Want = Service::Want; + using StreamOps = Service::BasicStreamOps; + + class ConnectOperBase; + template class ConnectOper; + + using LendersConnectOperPtr = std::unique_ptr; + + // `ec` untouched on success, but no immediate completion + bool initiate_async_connect(const Endpoint&, std::error_code& ec); + // `ec` untouched on success + std::error_code finalize_async_connect(std::error_code& ec) noexcept; + + // See Service::BasicStreamOps for details on these these 6 functions. + void do_init_read_async(std::error_code&, Want&) noexcept; + void do_init_write_async(std::error_code&, Want&) noexcept; + std::size_t do_read_some_sync(char* buffer, std::size_t size, + std::error_code&) noexcept; + std::size_t do_write_some_sync(const char* data, std::size_t size, + std::error_code&) noexcept; + std::size_t do_read_some_async(char* buffer, std::size_t size, + std::error_code&, Want&) noexcept; + std::size_t do_write_some_async(const char* data, std::size_t size, + std::error_code&, Want&) noexcept; + + friend class Service::BasicStreamOps; + friend class Service::BasicStreamOps; + friend class ReadAheadBuffer; + friend class ssl::Stream; +}; + + +/// Switching between synchronous and asynchronous operations is allowed, but +/// only in a nonoverlapping fashion. That is, a synchronous operation is not +/// allowed to run concurrently with an asynchronous one on the same +/// acceptor. Note that an asynchronous operation is considered to be running +/// until its completion handler starts executing. +class Acceptor: public SocketBase { +public: + Acceptor(Service&); + ~Acceptor() noexcept; + + static constexpr int max_connections = SOMAXCONN; + + void listen(int backlog = max_connections); + std::error_code listen(int backlog, std::error_code&); + + void accept(Socket&); + void accept(Socket&, Endpoint&); + std::error_code accept(Socket&, std::error_code&); + std::error_code accept(Socket&, Endpoint&, std::error_code&); + + /// @{ \brief Perform an asynchronous accept operation. + /// + /// Initiate an asynchronous accept operation. The completion handler will + /// be called when the operation completes. The operation completes when the + /// connection is accepted, or an error occurs. If the operation succeeds, + /// the specified local socket will have become connected to a remote + /// socket. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_accept(), even when + /// async_accept() is executed by the event loop thread. The completion + /// handler is guaranteed to be called eventually, as long as there is time + /// enough for the operation to complete or fail, and a thread is executing + /// Service::run() for long enough. + /// + /// The operation can be canceled by calling cancel(), and will be + /// automatically canceled if the acceptor is closed. If the operation is + /// canceled, it will fail with `error::operation_aborted`. The operation + /// remains cancelable up until the point in time where the completion + /// handler starts to execute. This means that if cancel() is called before + /// the completion handler starts to execute, then the completion handler is + /// guaranteed to have `error::operation_aborted` passed to it. This is true + /// regardless of whether cancel() is called explicitly or implicitly, such + /// as when the acceptor is destroyed. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec)` where `ec` is the error code. If the the handler object is + /// movable, it will never be copied. Otherwise, it will be copied as + /// necessary. + /// + /// It is an error to start a new accept operation (synchronous or + /// asynchronous) while an asynchronous accept operation is in progress. An + /// asynchronous accept operation is considered complete as soon as the + /// completion handler starts executing. This means that a new accept + /// operation can be started from the completion handler. + /// + /// \param sock This is the local socket, that upon successful completion + /// will have become connected to the remote socket. It must be in the + /// closed state (Socket::is_open()) when async_accept() is called. + /// + /// \param ep Upon completion, the remote peer endpoint will have been + /// assigned to this variable. + template void async_accept(Socket& sock, H handler); + template void async_accept(Socket& sock, Endpoint& ep, H handler); + /// @} + +private: + using Want = Service::Want; + + class AcceptOperBase; + template class AcceptOper; + + using LendersAcceptOperPtr = std::unique_ptr; + + std::error_code accept(Socket&, Endpoint*, std::error_code&); + Want do_accept_async(Socket&, Endpoint*, std::error_code&) noexcept; + + template void async_accept(Socket&, Endpoint*, H); +}; + + +/// \brief A timer object supporting asynchronous wait operations. +class DeadlineTimer { +public: + DeadlineTimer(Service&); + ~DeadlineTimer() noexcept; + + /// Thread-safe. + Service& get_service() noexcept; + + /// \brief Perform an asynchronous wait operation. + /// + /// Initiate an asynchronous wait operation. The completion handler becomes + /// ready to execute when the expiration time is reached, or an error occurs + /// (cancellation counts as an error here). The expiration time is the time + /// of initiation plus the specified delay. The error code passed to the + /// complition handler will **never** indicate success, unless the + /// expiration time was reached. + /// + /// The completion handler is always executed by the event loop thread, + /// i.e., by a thread that is executing Service::run(). Conversely, the + /// completion handler is guaranteed to not be called while no thread is + /// executing Service::run(). The execution of the completion handler is + /// always deferred to the event loop, meaning that it never happens as a + /// synchronous side effect of the execution of async_wait(), even when + /// async_wait() is executed by the event loop thread. The completion + /// handler is guaranteed to be called eventually, as long as there is time + /// enough for the operation to complete or fail, and a thread is executing + /// Service::run() for long enough. + /// + /// The operation can be canceled by calling cancel(), and will be + /// automatically canceled if the timer is destroyed. If the operation is + /// canceled, it will fail with `error::operation_aborted`. The operation + /// remains cancelable up until the point in time where the completion + /// handler starts to execute. This means that if cancel() is called before + /// the completion handler starts to execute, then the completion handler is + /// guaranteed to have `error::operation_aborted` passed to it. This is true + /// regardless of whether cancel() is called explicitly or implicitly, such + /// as when the timer is destroyed. + /// + /// The specified handler will be executed by an expression on the form + /// `handler(ec)` where `ec` is the error code. If the the handler object is + /// movable, it will never be copied. Otherwise, it will be copied as + /// necessary. + /// + /// It is an error to start a new asynchronous wait operation while an + /// another one is in progress. An asynchronous wait operation is in + /// progress until its completion handler starts executing. + template + void async_wait(std::chrono::duration delay, H handler); + + /// \brief Cancel an asynchronous wait operation. + /// + /// If an asynchronous wait operation, that is associated with this deadline + /// timer, is in progress, cause it to fail with + /// `error::operation_aborted`. An asynchronous wait operation is in + /// progress until its completion handler starts executing. + /// + /// Completion handlers of canceled operations will become immediately ready + /// to execute, but will never be executed directly as part of the execution + /// of cancel(). + /// + /// Cancellation happens automatically when the timer object is destroyed. + void cancel() noexcept; + +private: + template class WaitOper; + + using clock = Service::clock; + + Service::Impl& m_service_impl; + Service::OwnersOperPtr m_wait_oper; + + void add_oper(Service::LendersWaitOperPtr); +}; + + +class ReadAheadBuffer { +public: + ReadAheadBuffer(); + + /// Discard any buffered data. + void clear() noexcept; + +private: + using Want = Service::Want; + + char* m_begin = nullptr; + char* m_end = nullptr; + static constexpr std::size_t s_size = 1024; + const std::unique_ptr m_buffer; + + bool empty() const noexcept; + bool read(char*& begin, char* end, int delim, std::error_code&) noexcept; + template void refill_sync(S& stream, std::error_code&) noexcept; + template bool refill_async(S& stream, std::error_code&, Want&) noexcept; + + template friend class Service::BasicStreamOps; +}; + + +enum errors { + /// End of input. + end_of_input = 1, + + /// Delimiter not found. + delim_not_found, + + /// Host not found (authoritative). + host_not_found, + + /// Host not found (non-authoritative). + host_not_found_try_again, + + /// The query is valid but does not have associated address data. + no_data, + + /// A non-recoverable error occurred. + no_recovery, + + /// The service is not supported for the given socket type. + service_not_found, + + /// The socket type is not supported. + socket_type_not_supported, + + /// Premature end of input (e.g., end of input before reception of SSL + /// shutdown alert). + premature_end_of_input +}; + +std::error_code make_error_code(errors); + +} // namespace network +} // namespace util +} // namespace realm + +namespace std { + +template<> class is_error_code_enum { +public: + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace util { +namespace network { + + + + + +// Implementation + +// ---------------- StreamProtocol ---------------- + +inline StreamProtocol StreamProtocol::ip_v4() +{ + StreamProtocol prot; + prot.m_family = AF_INET; + return prot; +} + +inline StreamProtocol StreamProtocol::ip_v6() +{ + StreamProtocol prot; + prot.m_family = AF_INET6; + return prot; +} + +inline bool StreamProtocol::is_ip_v4() const +{ + return m_family == AF_INET; +} + +inline bool StreamProtocol::is_ip_v6() const +{ + return m_family == AF_INET6; +} + +inline int StreamProtocol::family() const +{ + return m_family; +} + +inline int StreamProtocol::protocol() const +{ + return m_protocol; +} + +inline StreamProtocol::StreamProtocol(): + m_family{AF_UNSPEC}, // Allow both IPv4 and IPv6 + m_socktype{SOCK_STREAM}, // Or SOCK_DGRAM for UDP + m_protocol{0} // Any protocol +{ +} + +// ---------------- Address ---------------- + +inline bool Address::is_ip_v4() const +{ + return !m_is_ip_v6; +} + +inline bool Address::is_ip_v6() const +{ + return m_is_ip_v6; +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const Address& addr) +{ + // FIXME: Not taking `addr.m_ip_v6_scope_id` into account. What does ASIO + // do? + union buffer_union { + char ip_v4[INET_ADDRSTRLEN]; + char ip_v6[INET6_ADDRSTRLEN]; + }; + char buffer[sizeof (buffer_union)]; + int af = addr.m_is_ip_v6 ? AF_INET6 : AF_INET; + const char* ret = ::inet_ntop(af, &addr.m_union, buffer, sizeof buffer); + if (ret == 0) { + std::error_code ec = make_basic_system_error_code(errno); + throw std::system_error(ec); + } + out << ret; + return out; +} + +inline Address::Address() +{ + m_union.m_ip_v4 = ip_v4_type(); +} + +inline Address make_address(const char* c_str) +{ + std::error_code ec; + Address addr = make_address(c_str, ec); + if (ec) + throw std::system_error(ec); + return addr; +} + +inline Address make_address(const std::string& str) +{ + std::error_code ec; + Address addr = make_address(str, ec); + if (ec) + throw std::system_error(ec); + return addr; +} + +inline Address make_address(const std::string& str, std::error_code& ec) noexcept +{ + return make_address(str.c_str(), ec); +} + +// ---------------- Endpoint ---------------- + +inline StreamProtocol Endpoint::protocol() const +{ + return m_protocol; +} + +inline Address Endpoint::address() const +{ + Address addr; + if (m_protocol.is_ip_v4()) { + addr.m_union.m_ip_v4 = m_sockaddr_union.m_ip_v4.sin_addr; + } + else { + addr.m_union.m_ip_v6 = m_sockaddr_union.m_ip_v6.sin6_addr; + addr.m_ip_v6_scope_id = m_sockaddr_union.m_ip_v6.sin6_scope_id; + addr.m_is_ip_v6 = true; + } + return addr; +} + +inline Endpoint::port_type Endpoint::port() const +{ + return ntohs(m_protocol.is_ip_v4() ? m_sockaddr_union.m_ip_v4.sin_port : + m_sockaddr_union.m_ip_v6.sin6_port); +} + +inline Endpoint::data_type* Endpoint::data() +{ + return &m_sockaddr_union.m_base; +} + +inline const Endpoint::data_type* Endpoint::data() const +{ + return &m_sockaddr_union.m_base; +} + +inline Endpoint::Endpoint(): + Endpoint{StreamProtocol::ip_v4(), 0} +{ +} + +inline Endpoint::Endpoint(const StreamProtocol& protocol, port_type port): + m_protocol{protocol} +{ + int family = m_protocol.family(); + if (family == AF_INET) { + m_sockaddr_union.m_ip_v4 = sockaddr_ip_v4_type(); // Clear + m_sockaddr_union.m_ip_v4.sin_family = AF_INET; + m_sockaddr_union.m_ip_v4.sin_port = htons(port); + m_sockaddr_union.m_ip_v4.sin_addr.s_addr = INADDR_ANY; + } + else if (family == AF_INET6) { + m_sockaddr_union.m_ip_v6 = sockaddr_ip_v6_type(); // Clear + m_sockaddr_union.m_ip_v6.sin6_family = AF_INET6; + m_sockaddr_union.m_ip_v6.sin6_port = htons(port); + } + else { + m_sockaddr_union.m_ip_v4 = sockaddr_ip_v4_type(); // Clear + m_sockaddr_union.m_ip_v4.sin_family = AF_UNSPEC; + m_sockaddr_union.m_ip_v4.sin_port = htons(port); + m_sockaddr_union.m_ip_v4.sin_addr.s_addr = INADDR_ANY; + } +} + +inline Endpoint::Endpoint(const Address& addr, port_type port) +{ + if (addr.m_is_ip_v6) { + m_protocol = StreamProtocol::ip_v6(); + m_sockaddr_union.m_ip_v6.sin6_family = AF_INET6; + m_sockaddr_union.m_ip_v6.sin6_port = htons(port); + m_sockaddr_union.m_ip_v6.sin6_flowinfo = 0; + m_sockaddr_union.m_ip_v6.sin6_addr = addr.m_union.m_ip_v6; + m_sockaddr_union.m_ip_v6.sin6_scope_id = addr.m_ip_v6_scope_id; + } + else { + m_protocol = StreamProtocol::ip_v4(); + m_sockaddr_union.m_ip_v4.sin_family = AF_INET; + m_sockaddr_union.m_ip_v4.sin_port = htons(port); + m_sockaddr_union.m_ip_v4.sin_addr = addr.m_union.m_ip_v4; + } +} + +inline Endpoint::List::iterator Endpoint::List::begin() const noexcept +{ + return m_endpoints.data(); +} + +inline Endpoint::List::iterator Endpoint::List::end() const noexcept +{ + return m_endpoints.data() + m_endpoints.size(); +} + +inline std::size_t Endpoint::List::size() const noexcept +{ + return m_endpoints.size(); +} + +inline bool Endpoint::List::empty() const noexcept +{ + return m_endpoints.size() == 0; +} + +// ---------------- Service::OperQueue ---------------- + +template inline bool Service::OperQueue::empty() const noexcept +{ + return !m_back; +} + +template inline void Service::OperQueue::push_back(LendersOperPtr op) noexcept +{ + REALM_ASSERT(!op->m_next); + if (m_back) { + op->m_next = m_back->m_next; + m_back->m_next = op.get(); + } + else { + op->m_next = op.get(); + } + m_back = op.release(); +} + +template template +inline void Service::OperQueue::push_back(OperQueue& q) noexcept +{ + if (!q.m_back) + return; + if (m_back) + std::swap(m_back->m_next, q.m_back->m_next); + m_back = q.m_back; + q.m_back = nullptr; +} + +template inline auto Service::OperQueue::pop_front() noexcept -> LendersOperPtr +{ + Oper* op = nullptr; + if (m_back) { + op = static_cast(m_back->m_next); + if (op != m_back) { + m_back->m_next = op->m_next; + } + else { + m_back = nullptr; + } + op->m_next = nullptr; + } + return LendersOperPtr(op); +} + +template inline void Service::OperQueue::clear() noexcept +{ + if (m_back) { + LendersOperPtr op(m_back); + while (op->m_next != m_back) + op.reset(static_cast(op->m_next)); + m_back = nullptr; + } +} + +template inline Service::OperQueue::OperQueue(OperQueue&& q) noexcept: + m_back{q.m_back} +{ + q.m_back = nullptr; +} + +template inline Service::OperQueue::~OperQueue() noexcept +{ + clear(); +} + +// ---------------- Service::Descriptor ---------------- + +inline Service::Descriptor::Descriptor(Impl& s) noexcept: + service_impl{s} +{ +} + +inline Service::Descriptor::~Descriptor() noexcept +{ + if (is_open()) + close(); +} + +inline void Service::Descriptor::assign(int fd, bool in_blocking_mode) noexcept +{ + REALM_ASSERT(!is_open()); + m_fd = fd; + m_in_blocking_mode = in_blocking_mode; +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + m_read_ready = false; + m_write_ready = false; + m_imminent_end_of_input = false; + m_is_registered = false; +#endif +} + +inline void Service::Descriptor::close() noexcept +{ + REALM_ASSERT(is_open()); +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + if (m_is_registered) + deregister_for_async(); + m_is_registered = false; +#endif + do_close(); +} + +inline bool Service::Descriptor::is_open() const noexcept +{ + return (m_fd != -1); +} + +inline auto Service::Descriptor::native_handle() const noexcept -> native_handle_type +{ + return m_fd; +} + +inline bool Service::Descriptor::in_blocking_mode() const noexcept +{ + return m_in_blocking_mode; +} + +template +inline void Service::Descriptor::initiate_oper(std::unique_ptr op, + Args&&... args) +{ + Service::Want want = op->initiate(std::forward(args)...); // Throws + add_initiated_oper(std::move(op), want); // Throws +} + +inline void Service::Descriptor::ensure_blocking_mode() +{ + // Assuming that descriptors are either used mostly in blocking mode, or + // mostly in nonblocking mode. + if (REALM_UNLIKELY(!m_in_blocking_mode)) { + bool value = false; + set_nonblock_flag(value); // Throws + m_in_blocking_mode = true; + } +} + +inline void Service::Descriptor::ensure_nonblocking_mode() +{ + // Assuming that descriptors are either used mostly in blocking mode, or + // mostly in nonblocking mode. + if (REALM_UNLIKELY(m_in_blocking_mode)) { + bool value = true; + set_nonblock_flag(value); // Throws + m_in_blocking_mode = false; + } +} + +inline bool Service::Descriptor::assume_read_would_block() const noexcept +{ +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + return !m_in_blocking_mode && !m_read_ready; +#else + return false; +#endif +} + +inline bool Service::Descriptor::assume_write_would_block() const noexcept +{ +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + return !m_in_blocking_mode && !m_write_ready; +#else + return false; +#endif +} + +inline void Service::Descriptor::set_read_ready(bool value) noexcept +{ +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + m_read_ready = value; +#else + // No-op + static_cast(value); +#endif +} + +inline void Service::Descriptor::set_write_ready(bool value) noexcept +{ +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + m_write_ready = value; +#else + // No-op + static_cast(value); +#endif +} + +// ---------------- Service ---------------- + +class Service::AsyncOper { +public: + bool in_use() const noexcept; + bool is_complete() const noexcept; + bool is_canceled() const noexcept; + void cancel() noexcept; + /// Every object of type \ref AsyncOper must be destroyed either by a call + /// to this function or to recycle(). This function recycles the operation + /// object (commits suicide), even if it throws. + virtual void recycle_and_execute() = 0; + /// Every object of type \ref AsyncOper must be destroyed either by a call + /// to recycle_and_execute() or to this function. This function destroys the + /// object (commits suicide). + virtual void recycle() noexcept = 0; + /// Must be called when the owner dies, and the object is in use (not an + /// instance of UnusedOper). + virtual void orphan() noexcept = 0; +protected: + AsyncOper(std::size_t size, bool in_use) noexcept; + virtual ~AsyncOper() noexcept {} + void set_is_complete(bool value) noexcept; + template + void do_recycle_and_execute(bool orphaned, H& handler, Args&&...); + void do_recycle(bool orphaned) noexcept; +private: + std::size_t m_size; // Allocated number of bytes + bool m_in_use = false; + // Set to true when the operation completes successfully or fails. If the + // operation is canceled before this happens, it will never be set to + // true. Always false when not in use + bool m_complete = false; + // Set to true when the operation is canceled. Always false when not in use. + bool m_canceled = false; + AsyncOper* m_next = nullptr; // Always null when not in use + template + void do_recycle_and_execute_helper(bool orphaned, bool& was_recycled, H handler, Args...); + friend class Service; +}; + +class Service::WaitOperBase: public AsyncOper { +public: + WaitOperBase(std::size_t size, DeadlineTimer& timer, clock::time_point expiration_time): + AsyncOper{size, true}, // Second argument is `in_use` + m_timer{&timer}, + m_expiration_time{expiration_time} + { + } + void expired() noexcept + { + set_is_complete(true); + } + void recycle() noexcept override final + { + bool orphaned = !m_timer; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_timer = nullptr; + } +protected: + DeadlineTimer* m_timer; + clock::time_point m_expiration_time; + friend class Service; +}; + +class Service::PostOperBase: public AsyncOper { +public: + PostOperBase(std::size_t size, Impl& service): + AsyncOper{size, true}, // Second argument is `in_use` + m_service{service} + { + } + void recycle() noexcept override final + { + // Service::recycle_post_oper() destroys this operation object + Service::recycle_post_oper(m_service, this); + } + void orphan() noexcept override final + { + REALM_ASSERT(false); // Never called + } +protected: + Impl& m_service; +}; + +template class Service::PostOper: public PostOperBase { +public: + PostOper(std::size_t size, Impl& service, H handler): + PostOperBase{size, service}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + // Recycle the operation object before the handler is exceuted, such + // that the memory is available for a new post operation that might be + // initiated during the execution of the handler. + bool was_recycled = false; + try { + H handler = std::move(m_handler); // Throws + // Service::recycle_post_oper() destroys this operation object + Service::recycle_post_oper(m_service, this); + was_recycled = true; + handler(); // Throws + } + catch (...) { + if (!was_recycled) { + // Service::recycle_post_oper() destroys this operation object + Service::recycle_post_oper(m_service, this); + } + throw; + } + } +private: + H m_handler; +}; + +class Service::IoOper: public AsyncOper { +public: + IoOper(std::size_t size) noexcept: + AsyncOper{size, true} // Second argument is `in_use` + { + } + virtual Descriptor& descriptor() noexcept = 0; + /// Advance this operation and figure out out whether it needs to read from, + /// or write to the underlying descriptor to advance further. This function + /// must return Want::read if the operation needs to read, or Want::write if + /// the operation needs to write to advance further. If the operation + /// completes (due to success or failure), this function must return + /// Want::nothing. + virtual Want advance() noexcept = 0; +}; + +class Service::UnusedOper: public AsyncOper { +public: + UnusedOper(std::size_t size) noexcept: + AsyncOper{size, false} // Second argument is `in_use` + { + } + void recycle_and_execute() override final + { + // Must never be called + REALM_ASSERT(false); + } + void recycle() noexcept override final + { + // Must never be called + REALM_ASSERT(false); + } + void orphan() noexcept override final + { + // Must never be called + REALM_ASSERT(false); + } +}; + +// `S` must be a stream class with the following member functions: +// +// Socket& lowest_layer() noexcept; +// +// void do_init_read_async(std::error_code& ec, Want& want) noexcept; +// void do_init_write_async(std::error_code& ec, Want& want) noexcept; +// +// std::size_t do_read_some_sync(char* buffer, std::size_t size, +// std::error_code& ec) noexcept; +// std::size_t do_write_some_sync(const char* data, std::size_t size, +// std::error_code& ec) noexcept; +// std::size_t do_read_some_async(char* buffer, std::size_t size, +// std::error_code& ec, Want& want) noexcept; +// std::size_t do_write_some_async(const char* data, std::size_t size, +// std::error_code& ec, Want& want) noexcept; +// +// If an error occurs during any of these 6 functions, the `ec` argument must be +// set accordingly. Otherwise the `ec` argument must be set to +// `std::error_code()`. +// +// The do_init_*_async() functions must update the `want` argument to indicate +// how the operation must be initiated: +// +// Want::read Wait for read readiness, then call do_*_some_async(). +// Want::write Wait for write readiness, then call do_*_some_async(). +// Want::nothing Call do_*_some_async() immediately without waiting for +// read or write readiness. +// +// If end-of-input occurs while reading, do_read_some_*() must fail, set `ec` to +// `network::end_of_input`, and return zero. +// +// If an error occurs during reading or writing, do_*_some_sync() must set `ec` +// accordingly (to something other than `std::system_error()`) and return +// zero. Otherwise they must set `ec` to `std::system_error()` and return the +// number of bytes read or written, which **must** be at least 1. If the +// underlying socket is in nonblocking mode, and no bytes could be immediately +// read or written these functions must fail with +// `error::resource_unavailable_try_again`. +// +// If an error occurs during reading or writing, do_*_some_async() must set `ec` +// accordingly (to something other than `std::system_error()`), `want` to +// `Want::nothing`, and return zero. Otherwise they must set `ec` to +// `std::system_error()` and return the number of bytes read or written, which +// must be zero if no bytes could be immediately read or written. Note, in this +// case it is not an error if the underlying socket is in nonblocking mode, and +// no bytes could be immediately read or written. When these functions succeed, +// but return zero because no bytes could be immediately read or written, they +// must set `want` to something other than `Want::nothing`. +// +// If no error occurs, do_*_some_async() must set `want` to indicate how the +// operation should proceed if additional data needs to be read or written, or +// if no bytes were transferred: +// +// Want::read Wait for read readiness, then call do_*_some_async() again. +// Want::write Wait for write readiness, then call do_*_some_async() again. +// Want::nothing Call do_*_some_async() again without waiting for read or +// write readiness. +// +// NOTE: If, for example, do_read_some_async() sets `want` to `Want::write`, it +// means that the stream needs to write data to the underlying TCP socket before +// it is able to deliver any additional data to the caller. While such a +// situation will never occur on a raw TCP socket, it can occur on an SSL stream +// (Secure Socket Layer). +// +// When do_*_some_async() returns `n`, at least one of the following conditions +// must be true: +// +// n > 0 Bytes were transferred. +// ec != std::error_code() An error occured. +// want != Want::nothing Wait for read/write readiness. +// +// This is of critical importance, as it is the only way we can avoid falling +// into a busy loop of repeated invocations of do_*_some_async(). +// +// NOTE: do_*_some_async() are allowed to set `want` to `Want::read` or +// `Want::write`, even when they succesfully transfer a nonzero number of bytes. +template class Service::BasicStreamOps { +public: + class StreamOper; + class ReadOperBase; + class WriteOperBase; + class BufferedReadOperBase; + template class ReadOper; + template class WriteOper; + template class BufferedReadOper; + + using LendersReadOperPtr = std::unique_ptr; + using LendersWriteOperPtr = std::unique_ptr; + using LendersBufferedReadOperPtr = std::unique_ptr; + + // Synchronous read + static std::size_t read(S& stream, char* buffer, std::size_t size, + std::error_code& ec) + { + REALM_ASSERT(!stream.lowest_layer().m_read_oper || + !stream.lowest_layer().m_read_oper->in_use()); + stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws + char* begin = buffer; + char* end = buffer + size; + char* curr = begin; + for (;;) { + if (curr == end) { + ec = std::error_code(); // Success + break; + } + char* buffer_2 = curr; + std::size_t size_2 = std::size_t(end - curr); + std::size_t n = stream.do_read_some_sync(buffer_2, size_2, ec); + if (REALM_UNLIKELY(ec)) + break; + REALM_ASSERT(n > 0); + REALM_ASSERT(n <= size_2); + curr += n; + } + std::size_t n = std::size_t(curr - begin); + return n; + } + + // Synchronous write + static std::size_t write(S& stream, const char* data, std::size_t size, + std::error_code& ec) + { + REALM_ASSERT(!stream.lowest_layer().m_write_oper || + !stream.lowest_layer().m_write_oper->in_use()); + stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws + const char* begin = data; + const char* end = data + size; + const char* curr = begin; + for (;;) { + if (curr == end) { + ec = std::error_code(); // Success + break; + } + const char* data_2 = curr; + std::size_t size_2 = std::size_t(end - curr); + std::size_t n = stream.do_write_some_sync(data_2, size_2, ec); + if (REALM_UNLIKELY(ec)) + break; + REALM_ASSERT(n > 0); + REALM_ASSERT(n <= size_2); + curr += n; + } + std::size_t n = std::size_t(curr - begin); + return n; + } + + // Synchronous read + static std::size_t buffered_read(S& stream, char* buffer, std::size_t size, int delim, + ReadAheadBuffer& rab, std::error_code& ec) + { + REALM_ASSERT(!stream.lowest_layer().m_read_oper || + !stream.lowest_layer().m_read_oper->in_use()); + stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws + char* begin = buffer; + char* end = buffer + size; + char* curr = begin; + for (;;) { + bool complete = rab.read(curr, end, delim, ec); + if (complete) + break; + + rab.refill_sync(stream, ec); + if (REALM_UNLIKELY(ec)) + break; + } + std::size_t n = (curr - begin); + return n; + } + + // Synchronous read + static std::size_t read_some(S& stream, char* buffer, std::size_t size, + std::error_code& ec) + { + REALM_ASSERT(!stream.lowest_layer().m_read_oper || + !stream.lowest_layer().m_read_oper->in_use()); + stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws + return stream.do_read_some_sync(buffer, size, ec); + } + + // Synchronous write + static std::size_t write_some(S& stream, const char* data, std::size_t size, + std::error_code& ec) + { + REALM_ASSERT(!stream.lowest_layer().m_write_oper || + !stream.lowest_layer().m_write_oper->in_use()); + stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws + return stream.do_write_some_sync(data, size, ec); + } + + template + static void async_read(S& stream, char* buffer, std::size_t size, bool is_read_some, H handler) + { + char* begin = buffer; + char* end = buffer + size; + LendersReadOperPtr op = + Service::alloc>(stream.lowest_layer().m_read_oper, stream, is_read_some, + begin, end, std::move(handler)); // Throws + stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws + } + + template + static void async_write(S& stream, const char* data, std::size_t size, bool is_write_some, + H handler) + { + const char* begin = data; + const char* end = data + size; + LendersWriteOperPtr op = + Service::alloc>(stream.lowest_layer().m_write_oper, stream, is_write_some, + begin, end, std::move(handler)); // Throws + stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws + } + + template + static void async_buffered_read(S& stream, char* buffer, std::size_t size, int delim, + ReadAheadBuffer& rab, H handler) + { + char* begin = buffer; + char* end = buffer + size; + LendersBufferedReadOperPtr op = + Service::alloc>(stream.lowest_layer().m_read_oper, stream, + begin, end, delim, rab, + std::move(handler)); // Throws + stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws + } +}; + +template class Service::BasicStreamOps::StreamOper: public IoOper { +public: + StreamOper(std::size_t size, S& stream) noexcept: + IoOper{size}, + m_stream{&stream} + { + } + void recycle() noexcept override final + { + bool orphaned = !m_stream; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_stream = nullptr; + } + Descriptor& descriptor() noexcept override final + { + return m_stream->lowest_layer().m_desc; + } +protected: + S* m_stream; + std::error_code m_error_code; +}; + +template class Service::BasicStreamOps::ReadOperBase: public StreamOper { +public: + ReadOperBase(std::size_t size, S& stream, bool is_read_some, char* begin, char* end) noexcept: + StreamOper{size, stream}, + m_is_read_some{is_read_some}, + m_begin{begin}, + m_end{end} + { + } + Want initiate() + { + auto& s = *this; + REALM_ASSERT(this == s.m_stream->lowest_layer().m_read_oper.get()); + REALM_ASSERT(!s.is_complete()); + REALM_ASSERT(s.m_curr <= s.m_end); + Want want = Want::nothing; + if (REALM_UNLIKELY(s.m_curr == s.m_end)) { + s.set_is_complete(true); // Success + } + else { + s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws + s.m_stream->do_init_read_async(s.m_error_code, want); + if (want == Want::nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + } + else { + want = advance(); + } + } + } + return want; + } + Want advance() noexcept override final + { + auto& s = *this; + REALM_ASSERT(!s.is_complete()); + REALM_ASSERT(!s.is_canceled()); + REALM_ASSERT(!s.m_error_code); + REALM_ASSERT(s.m_curr < s.m_end); + REALM_ASSERT(!s.m_is_read_some || s.m_curr == m_begin); + for (;;) { + // Read into callers buffer + char* buffer = s.m_curr; + std::size_t size = std::size_t(s.m_end - s.m_curr); + Want want = Want::nothing; + std::size_t n = s.m_stream->do_read_some_async(buffer, size, s.m_error_code, want); + REALM_ASSERT(n > 0 || s.m_error_code || want != Want::nothing); // No busy loop, please + bool got_nothing = (n == 0); + if (got_nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + return Want::nothing; + } + // Got nothing, but want something + return want; + } + REALM_ASSERT(!s.m_error_code); + // Check for completion + REALM_ASSERT(n <= size); + s.m_curr += n; + if (s.m_is_read_some || s.m_curr == s.m_end) { + s.set_is_complete(true); // Success + return Want::nothing; + } + if (want != Want::nothing) + return want; + REALM_ASSERT(n < size); + } + } +protected: + const bool m_is_read_some; + char* const m_begin; // May be dangling after cancellation + char* const m_end; // May be dangling after cancellation + char* m_curr = m_begin; // May be dangling after cancellation +}; + +template class Service::BasicStreamOps::WriteOperBase: public StreamOper { +public: + WriteOperBase(std::size_t size, S& stream, bool is_write_some, + const char* begin, const char* end) noexcept: + StreamOper{size, stream}, + m_is_write_some{is_write_some}, + m_begin{begin}, + m_end{end} + { + } + Want initiate() + { + auto& s = *this; + REALM_ASSERT(this == s.m_stream->lowest_layer().m_write_oper.get()); + REALM_ASSERT(!s.is_complete()); + REALM_ASSERT(s.m_curr <= s.m_end); + Want want = Want::nothing; + if (REALM_UNLIKELY(s.m_curr == s.m_end)) { + s.set_is_complete(true); // Success + } + else { + s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws + s.m_stream->do_init_write_async(s.m_error_code, want); + if (want == Want::nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + } + else { + want = advance(); + } + } + } + return want; + } + Want advance() noexcept override final + { + auto& s = *this; + REALM_ASSERT(!s.is_complete()); + REALM_ASSERT(!s.is_canceled()); + REALM_ASSERT(!s.m_error_code); + REALM_ASSERT(s.m_curr < s.m_end); + REALM_ASSERT(!s.m_is_write_some || s.m_curr == s.m_begin); + for (;;) { + // Write from callers buffer + const char* data = s.m_curr; + std::size_t size = std::size_t(s.m_end - s.m_curr); + Want want = Want::nothing; + std::size_t n = s.m_stream->do_write_some_async(data, size, s.m_error_code, want); + REALM_ASSERT(n > 0 || s.m_error_code || want != Want::nothing); // No busy loop, please + bool wrote_nothing = (n == 0); + if (wrote_nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + return Want::nothing; + } + // Wrote nothing, but want something written + return want; + } + REALM_ASSERT(!s.m_error_code); + // Check for completion + REALM_ASSERT(n <= size); + s.m_curr += n; + if (s.m_is_write_some || s.m_curr == s.m_end) { + s.set_is_complete(true); // Success + return Want::nothing; + } + if (want != Want::nothing) + return want; + REALM_ASSERT(n < size); + } + } +protected: + const bool m_is_write_some; + const char* const m_begin; // May be dangling after cancellation + const char* const m_end; // May be dangling after cancellation + const char* m_curr = m_begin; // May be dangling after cancellation +}; + +template class Service::BasicStreamOps::BufferedReadOperBase: public StreamOper { +public: + BufferedReadOperBase(std::size_t size, S& stream, char* begin, char* end, int delim, + ReadAheadBuffer& rab) noexcept: + StreamOper{size, stream}, + m_read_ahead_buffer{rab}, + m_begin{begin}, + m_end{end}, + m_delim{delim} + { + } + Want initiate() + { + auto& s = *this; + REALM_ASSERT(this == s.m_stream->lowest_layer().m_read_oper.get()); + REALM_ASSERT(!s.is_complete()); + Want want = Want::nothing; + bool complete = s.m_read_ahead_buffer.read(s.m_curr, s.m_end, s.m_delim, s.m_error_code); + if (complete) { + s.set_is_complete(true); // Success or failure + } + else { + s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws + s.m_stream->do_init_read_async(s.m_error_code, want); + if (want == Want::nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + } + else { + want = advance(); + } + } + } + return want; + } + Want advance() noexcept override final + { + auto& s = *this; + REALM_ASSERT(!s.is_complete()); + REALM_ASSERT(!s.is_canceled()); + REALM_ASSERT(!s.m_error_code); + REALM_ASSERT(s.m_read_ahead_buffer.empty()); + REALM_ASSERT(s.m_curr < s.m_end); + for (;;) { + // Fill read-ahead buffer from stream (is empty now) + Want want = Want::nothing; + bool nonempty = s.m_read_ahead_buffer.refill_async(*s.m_stream, s.m_error_code, want); + REALM_ASSERT(nonempty || s.m_error_code || + want != Want::nothing); // No busy loop, please + bool got_nothing = !nonempty; + if (got_nothing) { + if (REALM_UNLIKELY(s.m_error_code)) { + s.set_is_complete(true); // Failure + return Want::nothing; + } + // Got nothing, but want something + return want; + } + // Transfer buffered data to callers buffer + bool complete = + s.m_read_ahead_buffer.read(s.m_curr, s.m_end, s.m_delim, s.m_error_code); + if (complete) { + s.set_is_complete(true); // Success or failure (delim_not_found) + return Want::nothing; + } + if (want != Want::nothing) + return want; + } + } +protected: + ReadAheadBuffer& m_read_ahead_buffer; // May be dangling after cancellation + char* const m_begin; // May be dangling after cancellation + char* const m_end; // May be dangling after cancellation + char* m_curr = m_begin; // May be dangling after cancellation + const int m_delim; +}; + +template template +class Service::BasicStreamOps::ReadOper: public ReadOperBase { +public: + ReadOper(std::size_t size, S& stream, bool is_read_some, char* begin, char* end, H handler): + ReadOperBase{size, stream, is_read_some, begin, end}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + auto& s = *this; + REALM_ASSERT(s.is_complete() || s.is_canceled()); + REALM_ASSERT(s.is_complete() == (s.m_error_code || s.m_curr == s.m_end || + (s.m_is_read_some && s.m_curr != s.m_begin))); + REALM_ASSERT(s.m_curr >= s.m_begin); + bool orphaned = !s.m_stream; + std::error_code ec = s.m_error_code; + if (s.is_canceled()) + ec = error::operation_aborted; + std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin); + // Note: do_recycle_and_execute() commits suicide. + s.template do_recycle_and_execute(orphaned, s.m_handler, ec, + num_bytes_transferred); // Throws + } +private: + H m_handler; +}; + +template template +class Service::BasicStreamOps::WriteOper: public WriteOperBase { +public: + WriteOper(std::size_t size, S& stream, bool is_write_some, + const char* begin, const char* end, H handler): + WriteOperBase{size, stream, is_write_some, begin, end}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + auto& s = *this; + REALM_ASSERT(s.is_complete() || s.is_canceled()); + REALM_ASSERT(s.is_complete() == (s.m_error_code || s.m_curr == s.m_end || + (s.m_is_write_some && s.m_curr != s.m_begin))); + REALM_ASSERT(s.m_curr >= s.m_begin); + bool orphaned = !s.m_stream; + std::error_code ec = s.m_error_code; + if (s.is_canceled()) + ec = error::operation_aborted; + std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin); + // Note: do_recycle_and_execute() commits suicide. + s.template do_recycle_and_execute(orphaned, s.m_handler, ec, + num_bytes_transferred); // Throws + } +private: + H m_handler; +}; + +template template +class Service::BasicStreamOps::BufferedReadOper: public BufferedReadOperBase { +public: + BufferedReadOper(std::size_t size, S& stream, char* begin, char* end, int delim, + ReadAheadBuffer& rab, H handler): + BufferedReadOperBase{size, stream, begin, end, delim, rab}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + auto& s = *this; + REALM_ASSERT(s.is_complete() || (s.is_canceled() && !s.m_error_code)); + REALM_ASSERT(s.is_canceled() || s.m_error_code || + (s.m_delim != std::char_traits::eof() ? + s.m_curr > s.m_begin && s.m_curr[-1] == + std::char_traits::to_char_type(s.m_delim) : + s.m_curr == s.m_end)); + REALM_ASSERT(s.m_curr >= s.m_begin); + bool orphaned = !s.m_stream; + std::error_code ec = s.m_error_code; + if (s.is_canceled()) + ec = error::operation_aborted; + std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin); + // Note: do_recycle_and_execute() commits suicide. + s.template do_recycle_and_execute(orphaned, s.m_handler, ec, + num_bytes_transferred); // Throws + } +private: + H m_handler; +}; + +template inline void Service::post(H handler) +{ + do_post(&Service::post_oper_constr, sizeof (PostOper), &handler); +} + +inline void Service::OwnersOperDeleter::operator()(AsyncOper* op) const noexcept +{ + if (op->in_use()) { + op->orphan(); + } + else { + void* addr = op; + op->~AsyncOper(); + delete[] static_cast(addr); + } +} + +inline void Service::LendersOperDeleter::operator()(AsyncOper* op) const noexcept +{ + op->recycle(); // Suicide +} + +template std::unique_ptr +Service::alloc(OwnersOperPtr& owners_ptr, Args&&... args) +{ + void* addr = owners_ptr.get(); + std::size_t size; + if (REALM_LIKELY(addr)) { + REALM_ASSERT(!owners_ptr->in_use()); + size = owners_ptr->m_size; + // We can use static dispatch in the destructor call here, since an + // object, that is not in use, is always an instance of UnusedOper. + REALM_ASSERT(dynamic_cast(owners_ptr.get())); + static_cast(owners_ptr.get())->UnusedOper::~UnusedOper(); + if (REALM_UNLIKELY(size < sizeof (Oper))) { + owners_ptr.release(); + delete[] static_cast(addr); + goto no_object; + } + } + else { + no_object: + addr = new char[sizeof (Oper)]; // Throws + size = sizeof (Oper); + owners_ptr.reset(static_cast(addr)); + } + std::unique_ptr lenders_ptr; + try { + lenders_ptr.reset(new (addr) Oper(size, std::forward(args)...)); // Throws + } + catch (...) { + new (addr) UnusedOper(size); // Does not throw + throw; + } + return lenders_ptr; +} + +template +inline void Service::execute(std::unique_ptr& lenders_ptr) +{ + lenders_ptr.release()->recycle_and_execute(); // Throws +} + +template inline Service::PostOperBase* +Service::post_oper_constr(void* addr, std::size_t size, Impl& service, void* cookie) +{ + H& handler = *static_cast(cookie); + return new (addr) PostOper(size, service, std::move(handler)); // Throws +} + +inline bool Service::AsyncOper::in_use() const noexcept +{ + return m_in_use; +} + +inline bool Service::AsyncOper::is_complete() const noexcept +{ + return m_complete; +} + +inline void Service::AsyncOper::cancel() noexcept +{ + REALM_ASSERT(m_in_use); + REALM_ASSERT(!m_canceled); + m_canceled = true; +} + +inline Service::AsyncOper::AsyncOper(std::size_t size, bool is_in_use) noexcept: + m_size{size}, + m_in_use{is_in_use} +{ +} + +inline bool Service::AsyncOper::is_canceled() const noexcept +{ + return m_canceled; +} + +inline void Service::AsyncOper::set_is_complete(bool value) noexcept +{ + REALM_ASSERT(!m_complete); + REALM_ASSERT(!value || m_in_use); + m_complete = value; +} + +template +inline void Service::AsyncOper::do_recycle_and_execute(bool orphaned, H& handler, Args&&... args) +{ + // Recycle the operation object before the handler is exceuted, such that + // the memory is available for a new post operation that might be initiated + // during the execution of the handler. + bool was_recycled = false; + try { + // We need to copy or move all arguments to be passed to the handler, + // such that there is no risk of references to the recycled operation + // object being passed to the handler (the passed arguments may be + // references to members of the recycled operation object). The easiest + // way to achive this, is by forwarding the reference arguments (passed + // to this function) to a helper function whose arguments have + // nonreference type (`Args...` rather than `Args&&...`). + // + // Note that the copying and moving of arguments may throw, and it is + // important that the operation is still recycled even if that + // happens. For that reason, copying and moving of arguments must not + // happen until we are in a scope (this scope) that catches and deals + // correctly with such exceptions. + do_recycle_and_execute_helper(orphaned, was_recycled, std::move(handler), + std::forward(args)...); // Throws + } + catch (...) { + if (!was_recycled) + do_recycle(orphaned); + throw; + } +} + +template +inline void Service::AsyncOper::do_recycle_and_execute_helper(bool orphaned, bool& was_recycled, + H handler, Args... args) +{ + do_recycle(orphaned); + was_recycled = true; + handler(std::move(args)...); // Throws +} + +inline void Service::AsyncOper::do_recycle(bool orphaned) noexcept +{ + REALM_ASSERT(in_use()); + void* addr = this; + std::size_t size = m_size; + this->~AsyncOper(); // Suicide + if (orphaned) { + delete[] static_cast(addr); + } + else { + new (addr) UnusedOper(size); + } +} + +// ---------------- Resolver ---------------- + +class Resolver::ResolveOperBase: public Service::AsyncOper { +public: + ResolveOperBase(std::size_t size, Resolver& r, Query q) noexcept: + AsyncOper{size, true}, + m_resolver{&r}, + m_query{std::move(q)} + { + } + void perform() + { + // FIXME: Temporary hack until we get a true asynchronous resolver + m_endpoints = m_resolver->resolve(std::move(m_query), m_error_code); // Throws + set_is_complete(true); + } + void recycle() noexcept override final + { + bool orphaned = !m_resolver; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_resolver = nullptr; + } +protected: + Resolver* m_resolver; + Query m_query; + Endpoint::List m_endpoints; + std::error_code m_error_code; +}; + +template class Resolver::ResolveOper: public ResolveOperBase { +public: + ResolveOper(std::size_t size, Resolver& r, Query q, H handler): + ResolveOperBase{size, r, std::move(q)}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code)); + REALM_ASSERT(is_canceled() || m_error_code || !m_endpoints.empty()); + bool orphaned = !m_resolver; + std::error_code ec = m_error_code; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec, std::move(m_endpoints)); // Throws + } +private: + H m_handler; +}; + +inline Resolver::Resolver(Service& service): + m_service_impl{*service.m_impl} +{ +} + +inline Resolver::~Resolver() noexcept +{ + cancel(); +} + +inline Endpoint::List Resolver::resolve(const Query& q) +{ + std::error_code ec; + Endpoint::List list = resolve(q, ec); + if (REALM_UNLIKELY(ec)) + throw std::system_error(ec); + return list; +} + +template void Resolver::async_resolve(Query query, H handler) +{ + LendersResolveOperPtr op = Service::alloc>(m_resolve_oper, *this, + std::move(query), + std::move(handler)); // Throws + initiate_oper(std::move(op)); // Throws +} + +inline Resolver::Query::Query(std::string service_port, int init_flags): + m_flags{init_flags}, + m_service{service_port} +{ +} + +inline Resolver::Query::Query(const StreamProtocol& prot, std::string service_port, + int init_flags): + m_flags{init_flags}, + m_protocol{prot}, + m_service{service_port} +{ +} + +inline Resolver::Query::Query(std::string host_name, std::string service_port, int init_flags): + m_flags{init_flags}, + m_host{host_name}, + m_service{service_port} +{ +} + +inline Resolver::Query::Query(const StreamProtocol& prot, std::string host_name, + std::string service_port, int init_flags): + m_flags{init_flags}, + m_protocol{prot}, + m_host{host_name}, + m_service{service_port} +{ +} + +inline Resolver::Query::~Query() noexcept +{ +} + +inline int Resolver::Query::flags() const +{ + return m_flags; +} + +inline StreamProtocol Resolver::Query::protocol() const +{ + return m_protocol; +} + +inline std::string Resolver::Query::host() const +{ + return m_host; +} + +inline std::string Resolver::Query::service() const +{ + return m_service; +} + +// ---------------- SocketBase ---------------- + +inline SocketBase::SocketBase(Service& service): + m_desc{*service.m_impl} +{ +} + +inline SocketBase::~SocketBase() noexcept +{ + close(); +} + +inline bool SocketBase::is_open() const noexcept +{ + return m_desc.is_open(); +} + +inline auto SocketBase::native_handle() const noexcept -> native_handle_type +{ + return m_desc.native_handle(); +} + +inline void SocketBase::open(const StreamProtocol& prot) +{ + std::error_code ec; + if (open(prot, ec)) + throw std::system_error(ec); +} + +inline void SocketBase::close() noexcept +{ + if (!is_open()) + return; + cancel(); + m_desc.close(); +} + +template +inline void SocketBase::get_option(O& opt) const +{ + std::error_code ec; + if (get_option(opt, ec)) + throw std::system_error(ec); +} + +template +inline std::error_code SocketBase::get_option(O& opt, std::error_code& ec) const +{ + opt.get(*this, ec); + return ec; +} + +template +inline void SocketBase::set_option(const O& opt) +{ + std::error_code ec; + if (set_option(opt, ec)) + throw std::system_error(ec); +} + +template +inline std::error_code SocketBase::set_option(const O& opt, std::error_code& ec) +{ + opt.set(*this, ec); + return ec; +} + +inline void SocketBase::bind(const Endpoint& ep) +{ + std::error_code ec; + if (bind(ep, ec)) + throw std::system_error(ec); +} + +inline Endpoint SocketBase::local_endpoint() const +{ + std::error_code ec; + Endpoint ep = local_endpoint(ec); + if (ec) + throw std::system_error(ec); + return ep; +} + +inline const StreamProtocol& SocketBase::get_protocol() const noexcept +{ + return m_protocol; +} + +template +inline SocketBase::Option::Option(T init_value): + m_value{init_value} +{ +} + +template +inline T SocketBase::Option::value() const +{ + return m_value; +} + +template +inline void SocketBase::Option::get(const SocketBase& sock, std::error_code& ec) +{ + union { + U value; + char strut[sizeof (U) + 1]; + }; + std::size_t value_size = sizeof strut; + sock.get_option(opt_enum(opt), &value, value_size, ec); + if (!ec) { + REALM_ASSERT(value_size == sizeof value); + m_value = T(value); + } +} + +template +inline void SocketBase::Option::set(SocketBase& sock, std::error_code& ec) const +{ + U value_to_set = U(m_value); + sock.set_option(opt_enum(opt), &value_to_set, sizeof value_to_set, ec); +} + +// ---------------- Socket ---------------- + +class Socket::ConnectOperBase: public Service::IoOper { +public: + ConnectOperBase(std::size_t size, Socket& sock) noexcept: + IoOper{size}, + m_socket{&sock} + { + } + Want initiate(const Endpoint& ep) + { + REALM_ASSERT(this == m_socket->m_write_oper.get()); + if (m_socket->initiate_async_connect(ep, m_error_code)) { // Throws + set_is_complete(true); // Failure, or immediate completion + return Want::nothing; + } + return Want::write; + } + Want advance() noexcept override final + { + REALM_ASSERT(!is_complete()); + REALM_ASSERT(!is_canceled()); + REALM_ASSERT(!m_error_code); + m_socket->finalize_async_connect(m_error_code); + set_is_complete(true); + return Want::nothing; + } + void recycle() noexcept override final + { + bool orphaned = !m_socket; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_socket = nullptr; + } + Service::Descriptor& descriptor() noexcept override final + { + return m_socket->m_desc; + } +protected: + Socket* m_socket; + std::error_code m_error_code; +}; + +template class Socket::ConnectOper: public ConnectOperBase { +public: + ConnectOper(std::size_t size, Socket& sock, H handler): + ConnectOperBase{size, sock}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code)); + bool orphaned = !m_socket; + std::error_code ec = m_error_code; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec); // Throws + } +private: + H m_handler; +}; + +inline Socket::Socket(Service& service): + SocketBase{service} +{ +} + +inline Socket::Socket(Service& service, const StreamProtocol& prot, + native_handle_type native_socket): + SocketBase{service} +{ + assign(prot, native_socket); // Throws +} + +inline Socket::~Socket() noexcept +{ +} + +inline void Socket::connect(const Endpoint& ep) +{ + std::error_code ec; + if (connect(ep, ec)) // Throws + throw std::system_error(ec); +} + +inline std::size_t Socket::read(char* buffer, std::size_t size) +{ + std::error_code ec; + read(buffer, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Socket::read(char* buffer, std::size_t size, std::error_code& ec) +{ + return StreamOps::read(*this, buffer, size, ec); // Throws +} + +inline std::size_t Socket::read(char* buffer, std::size_t size, ReadAheadBuffer& rab) +{ + std::error_code ec; + read(buffer, size, rab, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Socket::read(char* buffer, std::size_t size, ReadAheadBuffer& rab, + std::error_code& ec) +{ + int delim = std::char_traits::eof(); + return StreamOps::buffered_read(*this, buffer, size, delim, rab, ec); // Throws +} + +inline std::size_t Socket::read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab) +{ + std::error_code ec; + std::size_t n = read_until(buffer, size, delim, rab, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Socket::read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab, std::error_code& ec) +{ + int delim_2 = std::char_traits::to_int_type(delim); + return StreamOps::buffered_read(*this, buffer, size, delim_2, rab, ec); // Throws +} + +inline std::size_t Socket::write(const char* data, std::size_t size) +{ + std::error_code ec; + write(data, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Socket::write(const char* data, std::size_t size, std::error_code& ec) +{ + return StreamOps::write(*this, data, size, ec); // Throws +} + +inline std::size_t Socket::read_some(char* buffer, std::size_t size) +{ + std::error_code ec; + std::size_t n = read_some(buffer, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Socket::read_some(char* buffer, std::size_t size, std::error_code& ec) +{ + return StreamOps::read_some(*this, buffer, size, ec); // Throws +} + +inline std::size_t Socket::write_some(const char* data, std::size_t size) +{ + std::error_code ec; + std::size_t n = write_some(data, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Socket::write_some(const char* data, std::size_t size, std::error_code& ec) +{ + return StreamOps::write_some(*this, data, size, ec); // Throws +} + +template inline void Socket::async_connect(const Endpoint& ep, H handler) +{ + LendersConnectOperPtr op = + Service::alloc>(m_write_oper, *this, std::move(handler)); // Throws + m_desc.initiate_oper(std::move(op), ep); // Throws +} + +template inline void Socket::async_read(char* buffer, std::size_t size, H handler) +{ + bool is_read_some = false; + StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws +} + +template +inline void Socket::async_read(char* buffer, std::size_t size, ReadAheadBuffer& rab, H handler) +{ + int delim = std::char_traits::eof(); + StreamOps::async_buffered_read(*this, buffer, size, delim, rab, std::move(handler)); // Throws +} + +template +inline void Socket::async_read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab, H handler) +{ + int delim_2 = std::char_traits::to_int_type(delim); + StreamOps::async_buffered_read(*this, buffer, size, delim_2, rab, std::move(handler)); // Throws +} + +template inline void Socket::async_write(const char* data, std::size_t size, H handler) +{ + bool is_write_some = false; + StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws +} + +template inline void Socket::async_read_some(char* buffer, std::size_t size, H handler) +{ + bool is_read_some = true; + StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws +} + +template +inline void Socket::async_write_some(const char* data, std::size_t size, H handler) +{ + bool is_write_some = true; + StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws +} + +inline void Socket::shutdown(shutdown_type what) +{ + std::error_code ec; + if (shutdown(what, ec)) // Throws + throw std::system_error(ec); +} + +inline void Socket::assign(const StreamProtocol& prot, native_handle_type native_socket) +{ + std::error_code ec; + if (assign(prot, native_socket, ec)) // Throws + throw std::system_error(ec); +} + +inline std::error_code Socket::assign(const StreamProtocol& prot, + native_handle_type native_socket, std::error_code& ec) +{ + return do_assign(prot, native_socket, ec); // Throws +} + +inline Socket& Socket::lowest_layer() noexcept +{ + return *this; +} + +inline void Socket::do_init_read_async(std::error_code&, Want& want) noexcept +{ + want = Want::read; // Wait for read readiness before proceeding +} + +inline void Socket::do_init_write_async(std::error_code&, Want& want) noexcept +{ + want = Want::write; // Wait for write readiness before proceeding +} + +inline std::size_t Socket::do_read_some_sync(char* buffer, std::size_t size, + std::error_code& ec) noexcept +{ + return m_desc.read_some(buffer, size, ec); +} + +inline std::size_t Socket::do_write_some_sync(const char* data, std::size_t size, + std::error_code& ec) noexcept +{ + return m_desc.write_some(data, size, ec); +} + +inline std::size_t Socket::do_read_some_async(char* buffer, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + std::error_code ec_2; + std::size_t n = m_desc.read_some(buffer, size, ec_2); + bool success = (!ec_2 || ec_2 == error::resource_unavailable_try_again); + if (REALM_UNLIKELY(!success)) { + ec = ec_2; + want = Want::nothing; // Failure + return 0; + } + ec = std::error_code(); + want = Want::read; // Success + return n; +} + +inline std::size_t Socket::do_write_some_async(const char* data, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + std::error_code ec_2; + std::size_t n = m_desc.write_some(data, size, ec_2); + bool success = (!ec_2 || ec_2 == error::resource_unavailable_try_again); + if (REALM_UNLIKELY(!success)) { + ec = ec_2; + want = Want::nothing; // Failure + return 0; + } + ec = std::error_code(); + want = Want::write; // Success + return n; +} + +// ---------------- Acceptor ---------------- + +class Acceptor::AcceptOperBase: public Service::IoOper { +public: + AcceptOperBase(std::size_t size, Acceptor& a, Socket& s, Endpoint* e): + IoOper{size}, + m_acceptor{&a}, + m_socket{s}, + m_endpoint{e} + { + } + Want initiate() + { + REALM_ASSERT(this == m_acceptor->m_read_oper.get()); + REALM_ASSERT(!is_complete()); + m_acceptor->m_desc.ensure_nonblocking_mode(); // Throws + return Want::read; + } + Want advance() noexcept override final + { + REALM_ASSERT(!is_complete()); + REALM_ASSERT(!is_canceled()); + REALM_ASSERT(!m_error_code); + REALM_ASSERT(!m_socket.is_open()); + Want want = m_acceptor->do_accept_async(m_socket, m_endpoint, m_error_code); + if (want == Want::nothing) + set_is_complete(true); // Success or failure + return want; + } + void recycle() noexcept override final + { + bool orphaned = !m_acceptor; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_acceptor = nullptr; + } + Service::Descriptor& descriptor() noexcept override final + { + return m_acceptor->m_desc; + } +protected: + Acceptor* m_acceptor; + Socket& m_socket; // May be dangling after cancellation + Endpoint* const m_endpoint; // May be dangling after cancellation + std::error_code m_error_code; +}; + +template class Acceptor::AcceptOper: public AcceptOperBase { +public: + AcceptOper(std::size_t size, Acceptor& a, Socket& s, Endpoint* e, H handler): + AcceptOperBase{size, a, s, e}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code)); + REALM_ASSERT(is_canceled() || m_error_code || m_socket.is_open()); + bool orphaned = !m_acceptor; + std::error_code ec = m_error_code; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec); // Throws + } +private: + H m_handler; +}; + +inline Acceptor::Acceptor(Service& service): + SocketBase{service} +{ +} + +inline Acceptor::~Acceptor() noexcept +{ +} + +inline void Acceptor::listen(int backlog) +{ + std::error_code ec; + if (listen(backlog, ec)) // Throws + throw std::system_error(ec); +} + +inline void Acceptor::accept(Socket& sock) +{ + std::error_code ec; + if (accept(sock, ec)) // Throws + throw std::system_error(ec); +} + +inline void Acceptor::accept(Socket& sock, Endpoint& ep) +{ + std::error_code ec; + if (accept(sock, ep, ec)) // Throws + throw std::system_error(ec); +} + +inline std::error_code Acceptor::accept(Socket& sock, std::error_code& ec) +{ + Endpoint* ep = nullptr; + return accept(sock, ep, ec); // Throws +} + +inline std::error_code Acceptor::accept(Socket& sock, Endpoint& ep, std::error_code& ec) +{ + return accept(sock, &ep, ec); // Throws +} + +template inline void Acceptor::async_accept(Socket& sock, H handler) +{ + Endpoint* ep = nullptr; + async_accept(sock, ep, std::move(handler)); // Throws +} + +template inline void Acceptor::async_accept(Socket& sock, Endpoint& ep, H handler) +{ + async_accept(sock, &ep, std::move(handler)); // Throws +} + +inline std::error_code Acceptor::accept(Socket& socket, Endpoint* ep, std::error_code& ec) +{ + REALM_ASSERT(!m_read_oper || !m_read_oper->in_use()); + if (REALM_UNLIKELY(socket.is_open())) + throw std::runtime_error("Socket is already open"); + m_desc.ensure_blocking_mode(); // Throws + m_desc.accept(socket.m_desc, m_protocol, ep, ec); + return ec; +} + +inline Acceptor::Want Acceptor::do_accept_async(Socket& socket, Endpoint* ep, + std::error_code& ec) noexcept +{ + std::error_code ec_2; + m_desc.accept(socket.m_desc, m_protocol, ep, ec_2); + if (ec_2 == error::resource_unavailable_try_again) + return Want::read; + ec = ec_2; + return Want::nothing; +} + +template inline void Acceptor::async_accept(Socket& sock, Endpoint* ep, H handler) +{ + if (REALM_UNLIKELY(sock.is_open())) + throw std::runtime_error("Socket is already open"); + LendersAcceptOperPtr op = Service::alloc>(m_read_oper, *this, sock, ep, + std::move(handler)); // Throws + m_desc.initiate_oper(std::move(op)); // Throws +} + +// ---------------- DeadlineTimer ---------------- + +template +class DeadlineTimer::WaitOper: public Service::WaitOperBase { +public: + WaitOper(std::size_t size, DeadlineTimer& timer, clock::time_point expiration_time, H handler): + Service::WaitOperBase{size, timer, expiration_time}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + bool orphaned = !m_timer; + std::error_code ec; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec); // Throws + } +private: + H m_handler; +}; + +inline DeadlineTimer::DeadlineTimer(Service& service): + m_service_impl{*service.m_impl} +{ +} + +inline DeadlineTimer::~DeadlineTimer() noexcept +{ + cancel(); +} + +template +inline void DeadlineTimer::async_wait(std::chrono::duration delay, H handler) +{ + clock::time_point now = clock::now(); + // FIXME: This method of detecting overflow does not work. Comparison + // between distinct duration types is not overflow safe. Overflow easily + // happens in the implied conversion of arguments to the common duration + // type (std::common_type<>). + auto max_add = clock::time_point::max() - now; + if (delay > max_add) + throw std::runtime_error("Expiration time overflow"); + clock::time_point expiration_time = now + delay; + Service::LendersWaitOperPtr op = + Service::alloc>(m_wait_oper, *this, expiration_time, + std::move(handler)); // Throws + add_oper(std::move(op)); // Throws +} + +// ---------------- ReadAheadBuffer ---------------- + +inline ReadAheadBuffer::ReadAheadBuffer(): + m_buffer{new char[s_size]} // Throws +{ +} + +inline void ReadAheadBuffer::clear() noexcept +{ + m_begin = nullptr; + m_end = nullptr; +} + +inline bool ReadAheadBuffer::empty() const noexcept +{ + return (m_begin == m_end); +} + +template inline void ReadAheadBuffer::refill_sync(S& stream, std::error_code& ec) noexcept +{ + char* buffer = m_buffer.get(); + std::size_t size = s_size; + static_assert(noexcept(stream.do_read_some_sync(buffer, size, ec)), ""); + std::size_t n = stream.do_read_some_sync(buffer, size, ec); + if (REALM_UNLIKELY(n == 0)) + return; + REALM_ASSERT(!ec); + REALM_ASSERT(n <= size); + m_begin = m_buffer.get(); + m_end = m_begin + n; +} + +template +inline bool ReadAheadBuffer::refill_async(S& stream, std::error_code& ec, Want& want) noexcept +{ + char* buffer = m_buffer.get(); + std::size_t size = s_size; + static_assert(noexcept(stream.do_read_some_async(buffer, size, ec, want)), ""); + std::size_t n = stream.do_read_some_async(buffer, size, ec, want); + if (n == 0) + return false; + REALM_ASSERT(!ec); + REALM_ASSERT(n <= size); + m_begin = m_buffer.get(); + m_end = m_begin + n; + return true; +} + +} // namespace network +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_NETWORK_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp new file mode 100644 index 0000000..ddced77 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp @@ -0,0 +1,1173 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_UTIL_NETWORK_SSL_HPP +#define REALM_UTIL_NETWORK_SSL_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if REALM_HAVE_OPENSSL +# include +# include +#elif REALM_HAVE_SECURE_TRANSPORT +# include +# include +# include + +#define REALM_HAVE_KEYCHAIN_APIS (TARGET_OS_MAC && !TARGET_OS_IPHONE) + +#endif + +// FIXME: Add necessary support for customizing the SSL server and client +// configurations. + +// FIXME: Currently, the synchronous SSL operations (handshake, read, write, +// shutdown) do not automatically retry if the underlying SSL function returns +// with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. This normally never +// happens, but it can happen according to the man pages, but in the case of +// SSL_write(), only when a renegotiation has to take place. It is likely that +// the solution is to to wrap the SSL calls inside a loop, such that they keep +// retrying until they succeed, however, such a simple scheme will fail if the +// synchronous operations were to be used with an underlying TCP socket in +// nonblocking mode. Currently, the underlying TCP socket is always in blocking +// mode when performing synchronous operations, but that may continue to be the +// case in teh future. + + +namespace realm { +namespace util { +namespace network { +namespace ssl { + +class ProtocolNotSupported; + + +/// `VerifyMode::none` corresponds to OpenSSL's `SSL_VERIFY_NONE`, and +/// `VerifyMode::peer` to `SSL_VERIFY_PEER`. +enum class VerifyMode { none, peer }; + + +class Context { +public: + Context(); + ~Context() noexcept; + + /// File must be in PEM format. Corresponds to OpenSSL's + /// `SSL_CTX_use_certificate_chain_file()`. + void use_certificate_chain_file(const std::string& path); + + /// File must be in PEM format. Corresponds to OpenSSL's + /// `SSL_CTX_use_PrivateKey_file()`. + void use_private_key_file(const std::string& path); + + /// Calling use_default_verify() will make a client use the + /// device default certificates for server verification. + /// For OpenSSL, use_default_verify() corresponds to + /// SSL_CTX_set_default_verify_paths(SSL_CTX*); + void use_default_verify(); + + /// The verify file is a PEM file containing trust + /// certificates that the client will use to + /// verify the server crtificate. If use_verify_file() + /// is not called, the default device trust store will + /// be used. + /// Corresponds roughly to OpenSSL's + /// SSL_CTX_load_verify_locations(). + void use_verify_file(const std::string& path); + +private: + void ssl_init(); + void ssl_destroy() noexcept; + void ssl_use_certificate_chain_file(const std::string& path, std::error_code&); + void ssl_use_private_key_file(const std::string& path, std::error_code&); + void ssl_use_default_verify(std::error_code&); + void ssl_use_verify_file(const std::string& path, std::error_code&); + +#if REALM_HAVE_OPENSSL + class OpensslErrorCategory: public std::error_category { + public: + const char* name() const noexcept override final; + std::string message(int value) const override final; + }; + static OpensslErrorCategory s_openssl_error_category; + + SSL_CTX* m_ssl_ctx = nullptr; +#elif REALM_HAVE_SECURE_TRANSPORT + class SecureTransportErrorCategory: public std::error_category { + public: + const char* name() const noexcept override final; + std::string message(int value) const override final; + }; + static SecureTransportErrorCategory s_secure_transport_error_category; + +#if REALM_HAVE_KEYCHAIN_APIS + static util::CFPtr load_pem_file(const std::string& path, SecKeychainRef, std::error_code&); + + std::error_code open_temporary_keychain_if_needed(); + std::error_code update_identity_if_needed(); + + util::CFPtr m_keychain; + std::string m_keychain_path; + + util::CFPtr m_certificate; + util::CFPtr m_private_key; + util::CFPtr m_identity; + + util::CFPtr m_certificate_chain; + + util::CFPtr m_trust_anchors; +#endif // REALM_HAVE_KEYCHAIN_APIS +#endif + + friend class Stream; +}; + + +/// Switching between synchronous and asynchronous operations is allowed, but +/// only in a nonoverlapping fashion. That is, a synchronous operation is not +/// allowed to run concurrently with an asynchronous one on the same +/// stream. Note that an asynchronous operation is considered to be running +/// until its completion handler starts executing. +class Stream { +public: + enum HandshakeType { client, server }; + + Stream(Socket&, Context&, HandshakeType); + ~Stream() noexcept; + + /// \brief Set the certificate verification mode for this SSL stream. + /// + /// Corresponds to OpenSSL's `SSL_set_verify()` with null passed as + /// `verify_callback`. + /// + /// Clients should always set it to `VerifyMode::peer`, such that the client + /// verifies the servers certificate. Servers should only set it to + /// `VerifyMode::peer` if they want to request a certificate from the + /// client. When testing with self-signed certificates, it is necessary to + /// set it to `VerifyMode::none` for clients too. + /// + /// It is an error if this function is called after the handshake operation + /// is initiated. + /// + /// The default verify mode is `VerifyMode::none`. + void set_verify_mode(VerifyMode); + + /// \brief Check the certificate against a host_name. + /// + /// set_check_host() includes a host name check in the + /// certificate verification. It is typically used by clients + /// to secure that the received certificate has a common name + /// or subject alternative name that matches \param host_name. + /// + /// set_check_host() is only useful if verify_mode is + /// set to VerifyMode::peer. + void set_check_host(std::string host_name); + + /// @{ + /// + /// Read and write operations behave the same way as they do on \ref + /// network::Socket, except that after cancellation of asynchronous + /// operations (`lowest_layer().cancel()`), the stream may be left in a bad + /// state (see below). + /// + /// The handshake operation must complete sucessfully before any read, + /// write, or shutdown operations are performed. + /// + /// The shutdown operation sends the shutdown alert to the peer, and + /// returns/completes as soon as the alert message has been written to the + /// underlying socket. It is an error if the shutdown operation is initiated + /// while there are read or write operations in progress. No read or write + /// operations are allowed to be initiated after the shutdown operation has + /// been initated. When the shutdown operation has completed, it is safe to + /// close the underlying socket (`lowest_layer().close()`). + /// + /// If a write operation is executing while, or is initiated after a close + /// notify alert is received from the remote peer, the write operation will + /// fail with error::broken_pipe. + /// + /// Callback functions for async read and write operations must take two + /// arguments, an std::error_code(), and an integer of a type std::size_t + /// indicating the number of transferred bytes (other types are allowed as + /// long as implicit conversion can take place). + /// + /// Callback functions for async handshake and shutdown operations must take + /// a single argument of type std::error_code() (other types are allowed as + /// long as implicit conversion can take place). + /// + /// Resumption of stream operation after cancellation of asynchronous + /// operations is not supported (does not work). Since the shutdown + /// operation involves network communication, that operation is also not + /// allowed after cancellation. The only thing that is allowed, is to + /// destroy the stream object. Other stream objects are not affected. + + void handshake(); + std::error_code handshake(std::error_code&); + + std::size_t read(char* buffer, std::size_t size); + std::size_t read(char* buffer, std::size_t size, std::error_code& ec); + std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&); + std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&, std::error_code& ec); + std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&); + std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&, + std::error_code& ec); + + std::size_t write(const char* data, std::size_t size); + std::size_t write(const char* data, std::size_t size, std::error_code& ec); + + std::size_t read_some(char* buffer, std::size_t size); + std::size_t read_some(char* buffer, std::size_t size, std::error_code&); + + std::size_t write_some(const char* data, std::size_t size); + std::size_t write_some(const char* data, std::size_t size, std::error_code&); + + void shutdown(); + std::error_code shutdown(std::error_code&); + + template void async_handshake(H handler); + + template void async_read(char* buffer, std::size_t size, H handler); + template void async_read(char* buffer, std::size_t size, ReadAheadBuffer&, H handler); + template void async_read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer&, H handler); + + template void async_write(const char* data, std::size_t size, H handler); + + template void async_read_some(char* buffer, std::size_t size, H handler); + + template void async_write_some(const char* data, std::size_t size, H handler); + + template void async_shutdown(H handler); + + /// @} + + /// Returns a reference to the underlying socket. + Socket& lowest_layer() noexcept; + +private: + using Want = Service::Want; + using StreamOps = Service::BasicStreamOps; + + class HandshakeOperBase; + template class HandshakeOper; + class ShutdownOperBase; + template class ShutdownOper; + + using LendersHandshakeOperPtr = std::unique_ptr; + using LendersShutdownOperPtr = std::unique_ptr; + + Socket& m_tcp_socket; + Context& m_ssl_context; + const HandshakeType m_handshake_type; + + // The host name that the certificate should be checked against. + std::string m_host_name; + + // See Service::BasicStreamOps for details on these these 6 functions. + void do_init_read_async(std::error_code&, Want&) noexcept; + void do_init_write_async(std::error_code&, Want&) noexcept; + std::size_t do_read_some_sync(char* buffer, std::size_t size, + std::error_code&) noexcept; + std::size_t do_write_some_sync(const char* data, std::size_t size, + std::error_code&) noexcept; + std::size_t do_read_some_async(char* buffer, std::size_t size, + std::error_code&, Want&) noexcept; + std::size_t do_write_some_async(const char* data, std::size_t size, + std::error_code&, Want&) noexcept; + + // The meaning of the arguments and return values of ssl_read() and + // ssl_write() are identical to do_read_some_async() and + // do_write_some_async() respectively, except that when the return value is + // nonzero, `want` is always `Want::nothing`, meaning that after bytes have + // been transferred, ssl_read() and ssl_write() must be called again to + // figure out whether it is necessary to wait for read or write readiness. + // + // The first invocation of ssl_shutdown() must send the shutdown alert to + // the peer. In blocking mode it must wait until the alert has been sent. In + // nonblocking mode, it must keep setting `want` to something other than + // `Want::nothing` until the alert has been sent. When the shutdown alert + // has been sent, it is safe to shut down the sending side of the underlying + // socket. On failure, ssl_shutdown() must set `ec` to something differet + // than `std::error_code()` and return false. On success, it must set `ec` + // to `std::error_code()`, and return true if a shutdown alert from the peer + // has already been received, otherwise it must return false. When it sets + // `want` to something other than `Want::nothing`, it must set `ec` to + // `std::error_code()` and return false. + // + // The second invocation of ssl_shutdown() (after the first invocation + // completed) must wait for reception on the peers shutdown alert. + // + // Note: The semantics around the second invocation of shutdown is currently + // unused by the higer level API, because of a requirement of compatibility + // with Apple's Secure Transport API. + void ssl_init(); + void ssl_destroy() noexcept; + void ssl_set_verify_mode(VerifyMode, std::error_code&); + void ssl_set_check_host(std::string, std::error_code&); + void ssl_handshake(std::error_code&, Want& want) noexcept; + bool ssl_shutdown(std::error_code& ec, Want& want) noexcept; + std::size_t ssl_read(char* buffer, std::size_t size, + std::error_code&, Want& want) noexcept; + std::size_t ssl_write(const char* data, std::size_t size, + std::error_code&, Want& want) noexcept; + +#if REALM_HAVE_OPENSSL + class BioMethod; + static BioMethod s_bio_method; + SSL* m_ssl = nullptr; + std::error_code m_bio_error_code; + + template + std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept; + + int do_ssl_accept() noexcept; + int do_ssl_connect() noexcept; + int do_ssl_shutdown() noexcept; + int do_ssl_read(char* buffer, std::size_t size) noexcept; + int do_ssl_write(const char* data, std::size_t size) noexcept; + + static int bio_write(BIO*, const char*, int) noexcept; + static int bio_read(BIO*, char*, int) noexcept; + static int bio_puts(BIO*, const char*) noexcept; + static long bio_ctrl(BIO*, int, long, void*) noexcept; + static int bio_create(BIO*) noexcept; + static int bio_destroy(BIO*) noexcept; + static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) noexcept; + +#elif REALM_HAVE_SECURE_TRANSPORT + util::CFPtr m_ssl; + VerifyMode m_verify_mode = VerifyMode::none; + + enum class BlockingOperation { + read, + write, + }; + util::Optional m_last_operation; + + // Details of the underlying I/O error that lead to errSecIO being returned + // from a SecureTransport function. + std::error_code m_last_error; + + // The number of bytes accepted by SSWrite() but not yet confirmed to be + // written to the underlying socket. + std::size_t m_num_partially_written_bytes = 0; + + template + std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept; + + std::pair do_ssl_handshake() noexcept; + std::pair do_ssl_shutdown() noexcept; + std::pair do_ssl_read(char* buffer, std::size_t size) noexcept; + std::pair do_ssl_write(const char* data, std::size_t size) noexcept; + + static OSStatus tcp_read(SSLConnectionRef, void*, std::size_t* length) noexcept; + static OSStatus tcp_write(SSLConnectionRef, const void*, std::size_t* length) noexcept; + + OSStatus tcp_read(void*, std::size_t* length) noexcept; + OSStatus tcp_write(const void*, std::size_t* length) noexcept; + + OSStatus verify_peer() noexcept; +#endif + + friend class Service::BasicStreamOps; + friend class network::ReadAheadBuffer; +}; + + + + +// Implementation + +class ProtocolNotSupported: public std::exception { +public: + const char* what() const noexcept override final; +}; + +inline Context::Context() +{ + ssl_init(); // Throws +} + +inline Context::~Context() noexcept +{ + ssl_destroy(); +} + +inline void Context::use_certificate_chain_file(const std::string& path) +{ + std::error_code ec; + ssl_use_certificate_chain_file(path, ec); // Throws + if (ec) + throw std::system_error(ec); +} + +inline void Context::use_private_key_file(const std::string& path) +{ + std::error_code ec; + ssl_use_private_key_file(path, ec); // Throws + if (ec) + throw std::system_error(ec); +} + +inline void Context::use_default_verify() +{ + std::error_code ec; + ssl_use_default_verify(ec); + if (ec) + throw std::system_error(ec); +} + +inline void Context::use_verify_file(const std::string& path) +{ + std::error_code ec; + ssl_use_verify_file(path, ec); + if (ec) + throw std::system_error(ec); +} + + +class Stream::HandshakeOperBase: public Service::IoOper { +public: + HandshakeOperBase(std::size_t size, Stream& stream): + IoOper{size}, + m_stream{&stream} + { + } + Want initiate() + { + REALM_ASSERT(this == m_stream->m_tcp_socket.m_read_oper.get()); + REALM_ASSERT(!is_complete()); + m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws + return advance(); + } + Want advance() noexcept override final + { + REALM_ASSERT(!is_complete()); + REALM_ASSERT(!is_canceled()); + REALM_ASSERT(!m_error_code); + Want want = Want::nothing; + m_stream->ssl_handshake(m_error_code, want); + set_is_complete(want == Want::nothing); + return want; + } + void recycle() noexcept override final + { + bool orphaned = !m_stream; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_stream = nullptr; + } + Service::Descriptor& descriptor() noexcept override final + { + return m_stream->lowest_layer().m_desc; + } +protected: + Stream* m_stream; + std::error_code m_error_code; +}; + +template class Stream::HandshakeOper: public HandshakeOperBase { +public: + HandshakeOper(std::size_t size, Stream& stream, H handler): + HandshakeOperBase{size, stream}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(is_complete() || is_canceled()); + bool orphaned = !m_stream; + std::error_code ec = m_error_code; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec); // Throws + } +private: + H m_handler; +}; + +class Stream::ShutdownOperBase: public Service::IoOper { +public: + ShutdownOperBase(std::size_t size, Stream& stream): + IoOper{size}, + m_stream{&stream} + { + } + Want initiate() + { + REALM_ASSERT(this == m_stream->m_tcp_socket.m_write_oper.get()); + REALM_ASSERT(!is_complete()); + m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws + return advance(); + } + Want advance() noexcept override final + { + REALM_ASSERT(!is_complete()); + REALM_ASSERT(!is_canceled()); + REALM_ASSERT(!m_error_code); + Want want = Want::nothing; + m_stream->ssl_shutdown(m_error_code, want); + if (want == Want::nothing) + set_is_complete(true); + return want; + } + void recycle() noexcept override final + { + bool orphaned = !m_stream; + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_stream = nullptr; + } + Service::Descriptor& descriptor() noexcept override final + { + return m_stream->lowest_layer().m_desc; + } +protected: + Stream* m_stream; + std::error_code m_error_code; +}; + +template class Stream::ShutdownOper: public ShutdownOperBase { +public: + ShutdownOper(std::size_t size, Stream& stream, H handler): + ShutdownOperBase{size, stream}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(is_complete() || is_canceled()); + bool orphaned = !m_stream; + std::error_code ec = m_error_code; + if (is_canceled()) + ec = error::operation_aborted; + // Note: do_recycle_and_execute() commits suicide. + do_recycle_and_execute(orphaned, m_handler, ec); // Throws + } +private: + H m_handler; +}; + +inline Stream::Stream(Socket& socket, Context& context, HandshakeType type): + m_tcp_socket{socket}, + m_ssl_context{context}, + m_handshake_type{type} +{ + ssl_init(); // Throws +} + +inline Stream::~Stream() noexcept +{ + m_tcp_socket.cancel(); + ssl_destroy(); +} + +inline void Stream::set_verify_mode(VerifyMode mode) +{ + std::error_code ec; + ssl_set_verify_mode(mode, ec); // Throws + if (ec) + throw std::system_error(ec); +} + +inline void Stream::set_check_host(std::string host_name) +{ + std::error_code ec; + ssl_set_check_host(host_name, ec); + if (ec) + throw std::system_error(ec); +} + +inline void Stream::handshake() +{ + std::error_code ec; + if (handshake(ec)) // Throws + throw std::system_error(ec); +} + +inline std::size_t Stream::read(char* buffer, std::size_t size) +{ + std::error_code ec; + read(buffer, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Stream::read(char* buffer, std::size_t size, std::error_code& ec) +{ + return StreamOps::read(*this, buffer, size, ec); // Throws +} + +inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab) +{ + std::error_code ec; + read(buffer, size, rab, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab, + std::error_code& ec) +{ + int delim = std::char_traits::eof(); + return StreamOps::buffered_read(*this, buffer, size, delim, rab, ec); // Throws +} + +inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab) +{ + std::error_code ec; + std::size_t n = read_until(buffer, size, delim, rab, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab, std::error_code& ec) +{ + int delim_2 = std::char_traits::to_int_type(delim); + return StreamOps::buffered_read(*this, buffer, size, delim_2, rab, ec); // Throws +} + +inline std::size_t Stream::write(const char* data, std::size_t size) +{ + std::error_code ec; + write(data, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return size; +} + +inline std::size_t Stream::write(const char* data, std::size_t size, std::error_code& ec) +{ + return StreamOps::write(*this, data, size, ec); // Throws +} + +inline std::size_t Stream::read_some(char* buffer, std::size_t size) +{ + std::error_code ec; + std::size_t n = read_some(buffer, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Stream::read_some(char* buffer, std::size_t size, std::error_code& ec) +{ + return StreamOps::read_some(*this, buffer, size, ec); // Throws +} + +inline std::size_t Stream::write_some(const char* data, std::size_t size) +{ + std::error_code ec; + std::size_t n = write_some(data, size, ec); // Throws + if (ec) + throw std::system_error(ec); + return n; +} + +inline std::size_t Stream::write_some(const char* data, std::size_t size, std::error_code& ec) +{ + return StreamOps::write_some(*this, data, size, ec); // Throws +} + +inline void Stream::shutdown() +{ + std::error_code ec; + if (shutdown(ec)) // Throws + throw std::system_error(ec); +} + +template inline void Stream::async_handshake(H handler) +{ + LendersHandshakeOperPtr op = + Service::alloc>(m_tcp_socket.m_read_oper, *this, + std::move(handler)); // Throws + m_tcp_socket.m_desc.initiate_oper(std::move(op)); // Throws +} + +template inline void Stream::async_read(char* buffer, std::size_t size, H handler) +{ + bool is_read_some = false; + StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws +} + +template +inline void Stream::async_read(char* buffer, std::size_t size, ReadAheadBuffer& rab, H handler) +{ + int delim = std::char_traits::eof(); + StreamOps::async_buffered_read(*this, buffer, size, delim, rab, std::move(handler)); // Throws +} + +template +inline void Stream::async_read_until(char* buffer, std::size_t size, char delim, + ReadAheadBuffer& rab, H handler) +{ + int delim_2 = std::char_traits::to_int_type(delim); + StreamOps::async_buffered_read(*this, buffer, size, delim_2, rab, std::move(handler)); // Throws +} + +template inline void Stream::async_write(const char* data, std::size_t size, H handler) +{ + bool is_write_some = false; + StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws +} + +template inline void Stream::async_read_some(char* buffer, std::size_t size, H handler) +{ + bool is_read_some = true; + StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws +} + +template inline void Stream::async_write_some(const char* data, std::size_t size, H handler) +{ + bool is_write_some = true; + StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws +} + +template inline void Stream::async_shutdown(H handler) +{ + LendersShutdownOperPtr op = + Service::alloc>(m_tcp_socket.m_write_oper, *this, + std::move(handler)); // Throws + m_tcp_socket.m_desc.initiate_oper(std::move(op)); // Throws +} + +inline void Stream::do_init_read_async(std::error_code&, Want& want) noexcept +{ + want = Want::nothing; // Proceed immediately unless there is an error +} + +inline void Stream::do_init_write_async(std::error_code&, Want& want) noexcept +{ + want = Want::nothing; // Proceed immediately unless there is an error +} + +inline std::size_t Stream::do_read_some_sync(char* buffer, std::size_t size, + std::error_code& ec) noexcept +{ + Want want = Want::nothing; + std::size_t n = do_read_some_async(buffer, size, ec, want); + if (n == 0 && want != Want::nothing) + ec = error::resource_unavailable_try_again; + return n; +} + +inline std::size_t Stream::do_write_some_sync(const char* data, std::size_t size, + std::error_code& ec) noexcept +{ + Want want = Want::nothing; + std::size_t n = do_write_some_async(data, size, ec, want); + if (n == 0 && want != Want::nothing) + ec = error::resource_unavailable_try_again; + return n; +} + +inline std::size_t Stream::do_read_some_async(char* buffer, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + return ssl_read(buffer, size, ec, want); +} + +inline std::size_t Stream::do_write_some_async(const char* data, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + return ssl_write(data, size, ec, want); +} + +inline Socket& Stream::lowest_layer() noexcept +{ + return m_tcp_socket; +} + + +#if REALM_HAVE_OPENSSL + +inline void Stream::ssl_handshake(std::error_code& ec, Want& want) noexcept +{ + auto perform = [this]() noexcept { + switch (m_handshake_type) { + case client: + return do_ssl_connect(); + case server: + return do_ssl_accept(); + } + REALM_ASSERT(false); + return 0; + }; + std::size_t n = ssl_perform(std::move(perform), ec, want); + REALM_ASSERT(n == 0 || n == 1); + if (want == Want::nothing && n == 0 && !ec) { + // End of input on TCP socket + ec = network::premature_end_of_input; + } +} + +inline std::size_t Stream::ssl_read(char* buffer, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + auto perform = [this, buffer, size]() noexcept { + return do_ssl_read(buffer, size); + }; + std::size_t n = ssl_perform(std::move(perform), ec, want); + if (want == Want::nothing && n == 0 && !ec) { + // End of input on TCP socket + if (SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) { + ec = network::end_of_input; + } + else { + ec = network::premature_end_of_input; + } + } + return n; +} + +inline std::size_t Stream::ssl_write(const char* data, std::size_t size, + std::error_code& ec, Want& want) noexcept +{ + // While OpenSSL is able to continue writing after we have received the + // close notify alert fro the remote peer, Apple's Secure Transport API is + // not, so to achieve common behaviour, we make sure that any such attempt + // will result in an `error::broken_pipe` error. + if ((SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) != 0) { + ec = error::broken_pipe; + want = Want::nothing; + return 0; + } + auto perform = [this, data, size]() noexcept { + return do_ssl_write(data, size); + }; + std::size_t n = ssl_perform(std::move(perform), ec, want); + if (want == Want::nothing && n == 0 && !ec) { + // End of input on TCP socket + ec = network::premature_end_of_input; + } + return n; +} + +inline bool Stream::ssl_shutdown(std::error_code& ec, Want& want) noexcept +{ + auto perform = [this]() noexcept { + return do_ssl_shutdown(); + }; + std::size_t n = ssl_perform(std::move(perform), ec, want); + REALM_ASSERT(n == 0 || n == 1); + if (want == Want::nothing && n == 0 && !ec) { + // The first invocation of SSL_shutdown() does not signal completion + // until the shutdown alert has been sent to the peer, or an error + // occurred (does not wait for acknowledgment). + // + // The second invocation (after a completed first invocation) does not + // signal completion until the peers shutdown alert has been received, + // or an error occurred. + // + // It is believed that: + // + // If this is the first time SSL_shutdown() is called, and + // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` evaluates to nonzero, then a + // zero return value means "partial success" (shutdown alert was sent, + // but the peers shutdown alert was not yet received), and 1 means "full + // success" (peers shutdown alert has already been received). + // + // If this is the first time SSL_shutdown() is called, and + // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` valuates to zero, then a + // zero return value means "premature end of input", and 1 is supposedly + // not a possibility. + // + // If this is the second time SSL_shutdown() is called (after the first + // call has returned zero), then a zero return value means "premature + // end of input", and 1 means "full success" (peers shutdown alert has + // now been received). + if ((SSL_get_shutdown(m_ssl) & SSL_SENT_SHUTDOWN) == 0) + ec = network::premature_end_of_input; + } + return (n > 0); +} + +// Provides a homogeneous, and mostly quirks-free interface across the OpenSSL +// operations (handshake, read, write, shutdown). +// +// First of all, if the operation remains incomplete (neither successfully +// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`, +// `want` to something other than `Want::nothing`, and return zero. Note that +// read and write operations are partial in the sense that they do not need to +// read or write everything before completing successfully. They only nead to +// read or write at least one byte to complete successfully. +// +// Such a situation will normally only happen when the underlying TCP socket is +// in nonblocking mode, and the read/write requirements of the operation could +// not be immediately accommodated. However, as is noted in the SSL_write() man +// page, it can also happen in blocking mode (at least while writing). +// +// If an error occurred, ssl_perform() will set `ec` to something other than +// `std::system_error()`, `want` to `Want::nothing`, and return 0. +// +// If no error occurred, and the operation completed (`!ec && want == +// Want::nothing`), then the return value indicates the outcome of the +// operation. +// +// In general, a nonzero value means "full" success, and a zero value means +// "partial" success, however, a zero result can also generally mean "premature +// end of input" / "unclean protocol termination". +// +// Assuming there is no premature end of input, then for reads and writes, the +// returned value is the number of transferred bytes. Zero for read on end of +// input. Never zero for write. For handshake it is always 1. For shutdown it is +// 1 if the peer shutdown alert was already received, otherwise it is zero. +// +// ssl_read() should use `SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN` to +// distinguish between the two possible meanings of zero. +// +// ssl_shutdown() should use `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` to +// distinguish between the two possible meanings of zero. +template +std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept +{ + ERR_clear_error(); + m_bio_error_code = std::error_code(); // Success + int ret = oper(); + int ssl_error = SSL_get_error(m_ssl, ret); + int sys_error = int(ERR_get_error()); + + // Guaranteed by the documentstion of SSL_get_error() + REALM_ASSERT((ret > 0) == (ssl_error == SSL_ERROR_NONE)); + + REALM_ASSERT(!m_bio_error_code || ssl_error == SSL_ERROR_SYSCALL); + + // Judging from various comments in the man pages, and from experience with + // the API, it seems that, + // + // ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0 + // + // is supposed to be an indicator of "premature end of input" / "unclean + // protocol termination", while + // + // ret=0, ssl_error=SSL_ERROR_ZERO_RETURN + // + // is supposed to be an indicator of the following success conditions: + // + // - Mature end of input / clean protocol termination. + // + // - Successful transmission of the shutdown alert, but no prior reception + // of shutdown alert from peer. + // + // Unfortunately, as is also remarked in various places in the man pages, + // those two success conditions may actually result in `ret=0, + // ssl_error=SSL_ERROR_SYSCALL, sys_error=0` too, and it seems that they + // almost always do. + // + // This means that we cannot properly discriminate between these conditions + // in ssl_perform(), and will have to defer to the caller to interpret the + // situation. Since thay cannot be properly told apart, we report all + // `ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0` and `ret=0, + // ssl_error=SSL_ERROR_ZERO_RETURN` cases as the latter. + switch (ssl_error) { + case SSL_ERROR_NONE: + ec = std::error_code(); // Success + want = Want::nothing; + return std::size_t(ret); // ret > 0 + case SSL_ERROR_ZERO_RETURN: + ec = std::error_code(); // Success + want = Want::nothing; + return 0; + case SSL_ERROR_WANT_READ: + ec = std::error_code(); // Success + want = Want::read; + return 0; + case SSL_ERROR_WANT_WRITE: + ec = std::error_code(); // Success + want = Want::write; + return 0; + case SSL_ERROR_SYSCALL: + if (REALM_UNLIKELY(sys_error != 0)) { + ec = make_basic_system_error_code(sys_error); + } + else if (REALM_UNLIKELY(m_bio_error_code)) { + ec = m_bio_error_code; + } + else if (ret == 0) { + // ret = 0, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0 + // + // See remarks above! + ec = std::error_code(); // Success + } + else { + // ret = -1, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0 + // + // This situation arises in OpenSSL version >= 1.1. + // It has been observed in the SSL_connect call if the + // other endpoint terminates the connection during + // SSL_connect. The OpenSSL documentation states + // that ret = -1 implies an underlying BIO error and + // that errno should be consulted. However, + // errno = 0(Undefined error) in the observed case. + // At the moment. we will report + // premature_end_of_input. + // If we see this error case occuring in other situations in + // the future, we will have to update this case. + ec = network::premature_end_of_input; + } + want = Want::nothing; + return 0; + case SSL_ERROR_SSL: + ec = std::error_code(sys_error, Context::s_openssl_error_category); + want = Want::nothing; + return 0; + default: + break; + } + // We are not supposed to ever get here + REALM_ASSERT(false); + return 0; +} + +inline int Stream::do_ssl_accept() noexcept +{ + int ret = SSL_accept(m_ssl); + return ret; +} + +inline int Stream::do_ssl_connect() noexcept +{ + int ret = SSL_connect(m_ssl); + return ret; +} + +inline int Stream::do_ssl_read(char* buffer, std::size_t size) noexcept +{ + int size_2 = int(size); + if (size > unsigned(std::numeric_limits::max())) + size_2 = std::size_t(std::numeric_limits::max()); + int ret = SSL_read(m_ssl, buffer, size_2); + return ret; +} + +inline int Stream::do_ssl_write(const char* data, std::size_t size) noexcept +{ + int size_2 = int(size); + if (size > unsigned(std::numeric_limits::max())) + size_2 = std::size_t(std::numeric_limits::max()); + int ret = SSL_write(m_ssl, data, size_2); + return ret; +} + +inline int Stream::do_ssl_shutdown() noexcept +{ + int ret = SSL_shutdown(m_ssl); + return ret; +} + +#elif REALM_HAVE_SECURE_TRANSPORT + +// Provides a homogeneous, and mostly quirks-free interface across the SecureTransport +// operations (handshake, read, write, shutdown). +// +// First of all, if the operation remains incomplete (neither successfully +// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`, +// `want` to something other than `Want::nothing`, and return zero. +// +// If an error occurred, ssl_perform() will set `ec` to something other than +// `std::system_error()`, `want` to `Want::nothing`, and return 0. +// +// If no error occured, and the operation completed (`!ec && want == +// Want::nothing`), then the return value indicates the outcome of the +// operation. +// +// In general, a nonzero value means "full" success, and a zero value means +// "partial" success, however, a zero result can also generally mean "premature +// end of input" / "unclean protocol termination". +// +// Assuming there is no premature end of input, then for reads and writes, the +// returned value is the number of transferred bytes. Zero for read on end of +// input. Never zero for write. For handshake it is always 1. For shutdown it is +// 1 if the peer shutdown alert was already received, otherwise it is zero. +template +std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept +{ + OSStatus result; + std::size_t n; + std::tie(result, n) = oper(); + + if (result == noErr) { + ec = std::error_code(); + want = Want::nothing; + return n; + } + + if (result == errSSLWouldBlock) { + REALM_ASSERT(m_last_operation); + ec = std::error_code(); + want = m_last_operation == BlockingOperation::read ? Want::read : Want::write; + m_last_operation = {}; + return n; + } + + if (result == errSSLClosedGraceful) { + ec = network::end_of_input; + want = Want::nothing; + return n; + } + + if (result == errSSLClosedAbort || result == errSSLClosedNoNotify) { + ec = network::premature_end_of_input; + want = Want::nothing; + return n; + } + + if (result == errSecIO) { + // A generic I/O error means something went wrong at a lower level. Use the error + // code we smuggled out of our lower-level functions to provide a more specific error. + REALM_ASSERT(m_last_error); + ec = m_last_error; + want = Want::nothing; + return n; + } + + ec = std::error_code(result, Context::s_secure_transport_error_category); + want = Want::nothing; + return 0; +} +#endif // REALM_HAVE_OPENSSL / REALM_HAVE_SECURE_TRANSPORT + +} // namespace ssl +} // namespace network +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_NETWORK_SSL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/optional.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/optional.hpp new file mode 100644 index 0000000..29a8be1 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/optional.hpp @@ -0,0 +1,728 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#pragma once +#ifndef REALM_UTIL_OPTIONAL_HPP +#define REALM_UTIL_OPTIONAL_HPP + +#include + +#include // std::logic_error +#include // std::less + +namespace realm { +namespace util { + +template +class Optional; + +// some() should be the equivalent of the proposed C++17 `make_optional`. +template +Optional some(Args&&...); +template +struct Some; + +// Note: Should conform with the future std::nullopt_t and std::in_place_t. +struct None { + constexpr explicit None(int) + { + } +}; +static constexpr None none{0}; +struct InPlace { + constexpr InPlace() + { + } +}; +static constexpr InPlace in_place; + +// Note: Should conform with the future std::bad_optional_access. +struct BadOptionalAccess : std::logic_error { + explicit BadOptionalAccess(const std::string& what_arg) + : std::logic_error(what_arg) + { + } + explicit BadOptionalAccess(const char* what_arg) + : std::logic_error(what_arg) + { + } +}; + +} // namespace util + +namespace _impl { + +template ::value> +struct OptionalStorage; + +template +struct TypeIsAssignableToOptional { + // Constraints from [optional.object.assign.18] + static const bool value = (std::is_same::type, T>::value && + std::is_constructible::value && std::is_assignable::value); +}; + +} // namespace _impl + +namespace util { + +// Note: Should conform with the future std::optional. +template +class Optional : private _impl::OptionalStorage { +public: + using value_type = T; + + constexpr Optional(); + constexpr Optional(None); + Optional(Optional&& other); + Optional(const Optional& other); + + constexpr Optional(T&& value); + constexpr Optional(const T& value); + + template + constexpr Optional(InPlace tag, Args&&...); + // FIXME: std::optional specifies an std::initializer_list constructor overload as well. + + Optional& operator=(None); + Optional& operator=(Optional&& other); + Optional& operator=(const Optional& other); + + template ::value>::type> + Optional& operator=(U&& value); + + explicit constexpr operator bool() const; + constexpr const T& value() const; // Throws + T& value(); // Throws, FIXME: Can be constexpr with C++14 + constexpr const T& operator*() const; // Throws + T& operator*(); // Throws, FIXME: Can be constexpr with C++14 + constexpr const T* operator->() const; // Throws + T* operator->(); // Throws, FIXME: Can be constexpr with C++14 + + template + constexpr T value_or(U&& value) const &; + + template + T value_or(U&& value) &&; + + void swap(Optional& other); // FIXME: Add noexcept() clause + + template + void emplace(Args&&...); + // FIXME: std::optional specifies an std::initializer_list overload for `emplace` as well. +private: + using Storage = _impl::OptionalStorage; + using Storage::m_engaged; + using Storage::m_value; + + constexpr bool is_engaged() const + { + return m_engaged; + } + void set_engaged(bool b) + { + m_engaged = b; + } + void clear(); +}; + +/// An Optional is functionally equivalent to a bool. +/// Note: C++17 does not (yet) specify this specialization, but it is convenient +/// as a "safer bool", especially in the presence of `fmap`. +/// Disabled for compliance with std::optional. +// template <> +// class Optional { +// public: +// Optional() {} +// Optional(None) {} +// Optional(Optional&&) = default; +// Optional(const Optional&) = default; +// explicit operator bool() const { return m_engaged; } +// private: +// bool m_engaged = false; +// friend struct Some; +// }; + +/// An Optional is a non-owning nullable pointer that throws on dereference. +// FIXME: Visual Studio 2015's constexpr support isn't sufficient to allow Optional to compile +// in constexpr contexts. +template +class Optional { +public: + using value_type = T&; + using target_type = typename std::decay::type; + + constexpr Optional() + { + } + constexpr Optional(None) + { + } // FIXME: Was a delegating constructor, but not fully supported in VS2015 + Optional(const Optional& other) = default; + template + Optional(const Optional& other) + : m_ptr(other.m_ptr) + { + } + template + Optional(std::reference_wrapper ref) + : m_ptr(&ref.get()) + { + } + + constexpr Optional(T& init_value) + : m_ptr(&init_value) + { + } + Optional(T&& value) = delete; // Catches accidental references to rvalue temporaries. + + Optional& operator=(None) + { + m_ptr = nullptr; + return *this; + } + Optional& operator=(const Optional& other) + { + m_ptr = other.m_ptr; + return *this; + } + + template + Optional& operator=(std::reference_wrapper ref) + { + m_ptr = &ref.get(); + return *this; + } + + explicit constexpr operator bool() const + { + return m_ptr; + } + constexpr const target_type& value() const; // Throws + target_type& value(); // Throws + constexpr const target_type& operator*() const + { + return value(); + } + target_type& operator*() + { + return value(); + } + constexpr const target_type* operator->() const + { + return &value(); + } + target_type* operator->() + { + return &value(); + } + + void swap(Optional other); // FIXME: Add noexcept() clause +private: + T* m_ptr = nullptr; + + template + friend class Optional; +}; + + +template +struct RemoveOptional { + using type = T; +}; +template +struct RemoveOptional> { + using type = typename RemoveOptional::type; // Remove recursively +}; + + +/// Implementation: + +template +struct Some { + template + static Optional some(Args&&... args) + { + return Optional{std::forward(args)...}; + } +}; + +/// Disabled for compliance with std::optional. +// template <> +// struct Some { +// static Optional some() +// { +// Optional opt; +// opt.m_engaged = true; +// return opt; +// } +// }; + +template +Optional some(Args&&... args) +{ + return Some::some(std::forward(args)...); +} + + +template +constexpr Optional::Optional() + : Storage(none) +{ +} + +template +constexpr Optional::Optional(None) + : Storage(none) +{ +} + +template +Optional::Optional(Optional&& other) + : Storage(none) +{ + if (other.m_engaged) { + new (&m_value) T(std::move(other.m_value)); + m_engaged = true; + } +} + +template +Optional::Optional(const Optional& other) + : Storage(none) +{ + if (other.m_engaged) { + new (&m_value) T(other.m_value); + m_engaged = true; + } +} + +template +constexpr Optional::Optional(T&& r_value) + : Storage(std::move(r_value)) +{ +} + +template +constexpr Optional::Optional(const T& l_value) + : Storage(l_value) +{ +} + +template +template +constexpr Optional::Optional(InPlace, Args&&... args) + : Storage(std::forward(args)...) +{ +} + +template +void Optional::clear() +{ + if (m_engaged) { + m_value.~T(); + m_engaged = false; + } +} + +template +Optional& Optional::operator=(None) +{ + clear(); + return *this; +} + +template +Optional& Optional::operator=(Optional&& other) +{ + if (m_engaged) { + if (other.m_engaged) { + m_value = std::move(other.m_value); + } + else { + clear(); + } + } + else { + if (other.m_engaged) { + new (&m_value) T(std::move(other.m_value)); + m_engaged = true; + } + } + return *this; +} + +template +Optional& Optional::operator=(const Optional& other) +{ + if (m_engaged) { + if (other.m_engaged) { + m_value = other.m_value; + } + else { + clear(); + } + } + else { + if (other.m_engaged) { + new (&m_value) T(other.m_value); + m_engaged = true; + } + } + return *this; +} + +template +template +Optional& Optional::operator=(U&& r_value) +{ + if (m_engaged) { + m_value = std::forward(r_value); + } + else { + new (&m_value) T(std::forward(r_value)); + m_engaged = true; + } + return *this; +} + +template +constexpr Optional::operator bool() const +{ + return m_engaged; +} + +template +constexpr const T& Optional::value() const +{ + return m_engaged ? m_value : (throw BadOptionalAccess{"bad optional access"}, m_value); +} + +template +T& Optional::value() +{ + if (!m_engaged) { + throw BadOptionalAccess{"bad optional access"}; + } + return m_value; +} + +template +constexpr const typename Optional::target_type& Optional::value() const +{ + return m_ptr ? *m_ptr : (throw BadOptionalAccess{"bad optional access"}, *m_ptr); +} + +template +typename Optional::target_type& Optional::value() +{ + if (!m_ptr) { + throw BadOptionalAccess{"bad optional access"}; + } + return *m_ptr; +} + +template +constexpr const T& Optional::operator*() const +{ + // Note: This differs from std::optional, which doesn't throw. + return value(); +} + +template +T& Optional::operator*() +{ + // Note: This differs from std::optional, which doesn't throw. + return value(); +} + +template +constexpr const T* Optional::operator->() const +{ + // Note: This differs from std::optional, which doesn't throw. + return &value(); +} + +template +T* Optional::operator->() +{ + // Note: This differs from std::optional, which doesn't throw. + return &value(); +} + +template +template +constexpr T Optional::value_or(U&& otherwise) const & +{ + return m_engaged ? T{m_value} : T{std::forward(otherwise)}; +} + +template +template +T Optional::value_or(U&& otherwise) && +{ + if (is_engaged()) { + return T(std::move(m_value)); + } + else { + return T(std::forward(otherwise)); + } +} + +template +void Optional::swap(Optional& other) +{ + // FIXME: This might be optimizable. + Optional tmp = std::move(other); + other = std::move(*this); + *this = std::move(tmp); +} + +template +template +void Optional::emplace(Args&&... args) +{ + clear(); + new (&m_value) T(std::forward(args)...); + m_engaged = true; +} + + +template +constexpr Optional::type> make_optional(T&& value) +{ + using Type = typename std::decay::type; + return some(std::forward(value)); +} + +template +bool operator==(const Optional& lhs, const Optional& rhs) +{ + if (!lhs && !rhs) { + return true; + } + if (lhs && rhs) { + return *lhs == *rhs; + } + return false; +} + +template +bool operator!=(const Optional& lhs, const Optional& rhs) +{ + return !(lhs == rhs); +} + +template +bool operator<(const Optional& lhs, const Optional& rhs) +{ + if (!rhs) { + return false; + } + if (!lhs) { + return true; + } + return std::less{}(*lhs, *rhs); +} + +template +bool operator>(const util::Optional& lhs, const util::Optional& rhs) +{ + if (!lhs) { + return false; + } + if (!rhs) { + return true; + } + return std::greater{}(*lhs, *rhs); +} + +template +bool operator==(const Optional& lhs, None) +{ + return !bool(lhs); +} + +template +bool operator!=(const Optional& lhs, None) +{ + return bool(lhs); +} + +template +bool operator<(const Optional& lhs, None) +{ + static_cast(lhs); + return false; +} + +template +bool operator==(None, const Optional& rhs) +{ + return !bool(rhs); +} + +template +bool operator!=(None, const Optional& rhs) +{ + return bool(rhs); +} + +template +bool operator<(None, const Optional& rhs) +{ + return bool(rhs); +} + +template +bool operator==(const Optional& lhs, const U& rhs) +{ + return lhs ? *lhs == rhs : false; +} + +template +bool operator<(const Optional& lhs, const T& rhs) +{ + return lhs ? std::less{}(*lhs, rhs) : true; +} + +template +bool operator==(const T& lhs, const Optional& rhs) +{ + return rhs ? lhs == *rhs : false; +} + +template +bool operator<(const T& lhs, const Optional& rhs) +{ + return rhs ? std::less{}(lhs, *rhs) : false; +} + +template +auto operator>>(Optional lhs, F&& rhs) -> decltype(fmap(lhs, std::forward(rhs))) +{ + return fmap(lhs, std::forward(rhs)); +} + +template +OS& operator<<(OS& os, const Optional& rhs) +{ + if (rhs) { + os << "some(" << *rhs << ")"; + } + else { + os << "none"; + } + return os; +} + +template +T unwrap(T&& value) +{ + return value; +} + +template +T unwrap(util::Optional&& value) +{ + return *value; +} + +template +T unwrap(const util::Optional& value) +{ + return *value; +} + +template +T unwrap(util::Optional& value) +{ + return *value; +} + +} // namespace util + +namespace _impl { + +// T is trivially destructible. +template +struct OptionalStorage { + union { + T m_value; + char m_null_state; + }; + bool m_engaged = false; + + constexpr OptionalStorage(realm::util::None) + : m_null_state() + { + } + constexpr OptionalStorage(T&& value) + : m_value(std::move(value)) + , m_engaged(true) + { + } + + template + constexpr OptionalStorage(Args&&... args) + : m_value(args...) + , m_engaged(true) + { + } +}; + +// T is not trivially destructible. +template +struct OptionalStorage { + union { + T m_value; + char m_null_state; + }; + bool m_engaged = false; + + constexpr OptionalStorage(realm::util::None) + : m_null_state() + { + } + constexpr OptionalStorage(T&& value) + : m_value(std::move(value)) + , m_engaged(true) + { + } + + template + constexpr OptionalStorage(Args&&... args) + : m_value(args...) + , m_engaged(true) + { + } + + ~OptionalStorage() + { + if (m_engaged) + m_value.~T(); + } +}; + +} // namespace _impl + +using util::none; + +} // namespace realm + +#endif // REALM_UTIL_OPTIONAL_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/overload.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/overload.hpp new file mode 100644 index 0000000..49188ae --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/overload.hpp @@ -0,0 +1,70 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_OVERLOAD_HPP +#define REALM_UTIL_OVERLOAD_HPP + +#include + +namespace realm { + +namespace _impl { + +template +struct Overloaded; + +} // namespace _impl + + +namespace util { + +// Declare an overload set using lambdas or other function objects. +// A minimal version of C++ Library Evolution Working Group proposal P0051R2. + +template +_impl::Overloaded overload(Fns&&... f) +{ + return _impl::Overloaded(std::forward(f)...); +} + +} // namespace util + + +namespace _impl { + +template +struct Overloaded : Fn, Overloaded { + template + Overloaded(U&& fn, Rest&&... rest) : Fn(std::forward(fn)), Overloaded(std::forward(rest)...) { } + + using Fn::operator(); + using Overloaded::operator(); +}; + +template +struct Overloaded : Fn { + template + Overloaded(U&& fn) : Fn(std::forward(fn)) { } + + using Fn::operator(); +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_UTIL_OVERLOAD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp new file mode 100644 index 0000000..a2a28c8 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp @@ -0,0 +1,304 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + + +#pragma once +#ifndef REALM_UTIL_PRIORITY_QUEUE_HPP +#define REALM_UTIL_PRIORITY_QUEUE_HPP + +#include +#include +#include + +namespace realm { +namespace util { + + +/// PriorityQueue corresponds exactly to `std::priority_queue`, but has the extra feature +/// of allowing iteration and erasure of elements in the queue. +/// +/// PriorityQueue only allows const access to its elements, because non-const access +/// would open up the risk of changing the ordering of the elements. +/// +/// Note: As opposed to `std::priority_queue`, this does not store elements in a heap +/// internally. Instead, elements are stored in sorted order. Users of this class are +/// allowed to operate on this assumption. +template , class Compare = std::less> +class PriorityQueue : private Compare { +public: + using container_type = Container; + using value_type = typename Container::value_type; + using size_type = typename Container::size_type; + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using const_reverse_iterator = typename Container::const_reverse_iterator; + using const_iterator = typename Container::const_iterator; + + //@{ + /// Construct a PriorityQueue, optionally providing a comparator object. + PriorityQueue(const Compare& comparator, const Container& cont); + + explicit PriorityQueue(const Compare& comparator = Compare{}, Container&& cont = Container{}); + + template + PriorityQueue(InputIt first, InputIt last, const Compare& comparator, const Container& cont); + + template + PriorityQueue(InputIt first, InputIt last, const Compare& comparator = Compare{}, Container&& cont = Container{}); + //@} + // Skipping Allocator-specific template constructors. + + PriorityQueue(const PriorityQueue&) = default; + PriorityQueue(PriorityQueue&&) = default; + PriorityQueue& operator=(const PriorityQueue&) = default; + PriorityQueue& operator=(PriorityQueue&&) = default; + + bool empty() const; + size_type size() const; + + //@{ + /// Push an element to the priority queue. + /// + /// If insertion to the underlying `Container` invalidates + /// iterators and references, any iterators and references into this + /// priority queue are also invalidated. By default, this is the case. + void push(const T& value); + void push(T&& value); + //@} + + /// Pop the largest element from the priority queue. + /// + /// If `pop_back` on the underlying `Container` invalidates + /// iterators and references, any iterators and reference into this + /// priority queue are also invalidated. By default, this is *NOT* the case. + /// + /// Calling `pop()` on an empty priority queue is undefined. + void pop(); + + /// Return a reference to the largest element of the priority queue. + /// + /// Calling `top()` on an empty priority queue is undefined. + const_reference top() const; + + /// Pop the top of the queue and return it by moving it out of the queue. + /// + /// Note: This method does not exist in `std::priority_queue`. + /// + /// Calling `pop_top()` on an empty priorty queue is undefined. + value_type pop_top(); + + // FIXME: emplace() deliberately omitted for simplicity. + + /// Swap the contents of this priority queue with the contents of \a other. + void swap(PriorityQueue& other); + + // Not in std::priority_queue: + + /// Return an iterator to the beginning of the queue (smallest element first). + const_iterator begin() const; + + /// Return an iterator to the end of the queue (largest element last); + const_iterator end() const; + + /// Return a reverse iterator into the priority queue (largest element first). + const_reverse_iterator rbegin() const; + + /// Return a reverse iterator representing the end of the priority queue (smallest element last). + const_reverse_iterator rend() const; + + /// Erase element pointed to by \a it. + /// + /// Note: This function differs from `std::priority_queue` by returning the erased + /// element using move semantics. + /// + /// Calling `erase()` with a beyond-end iterator (such as what is returned by `end()`) + /// is undefined. + value_type erase(const_iterator it); + + /// Remove all elements from the priority queue. + void clear(); + + /// Calls `reserve()` on the underlying `Container`. + void reserve(size_type); + +private: + Container m_queue; + + const Compare& compare() const; + Compare& compare(); +}; + + +/// Implementation + +template +PriorityQueue::PriorityQueue(const Compare& comparator, const Container& cont) + : Compare(comparator) + , m_queue(cont) +{ +} + +template +PriorityQueue::PriorityQueue(const Compare& comparator, Container&& cont) + : Compare(comparator) + , m_queue(std::move(cont)) +{ +} + +template +template +PriorityQueue::PriorityQueue(InputIt first, InputIt last, const Compare& comparator, + const Container& cont) + : Compare(comparator) + , m_queue(cont) +{ + for (auto it = first; it != last; ++it) { + push(*it); + } +} + +template +template +PriorityQueue::PriorityQueue(InputIt first, InputIt last, const Compare& comparator, + Container&& cont) + : Compare(comparator) + , m_queue(std::move(cont)) +{ + for (auto it = first; it != last; ++it) { + push(*it); + } +} + +template +typename PriorityQueue::size_type PriorityQueue::size() const +{ + return m_queue.size(); +} + +template +bool PriorityQueue::empty() const +{ + return m_queue.empty(); +} + +template +void PriorityQueue::push(const T& element) +{ + auto it = std::lower_bound(m_queue.begin(), m_queue.end(), element, compare()); + m_queue.insert(it, element); +} + +template +void PriorityQueue::push(T&& element) +{ + auto it = std::lower_bound(m_queue.begin(), m_queue.end(), element, compare()); + m_queue.insert(it, std::move(element)); +} + +template +void PriorityQueue::pop() +{ + m_queue.pop_back(); +} + +template +typename PriorityQueue::const_reference PriorityQueue::top() const +{ + return m_queue.back(); +} + +template +typename PriorityQueue::value_type PriorityQueue::pop_top() +{ + value_type value = std::move(m_queue.back()); + m_queue.pop_back(); + return value; +} + +template +Compare& PriorityQueue::compare() +{ + return *this; +} + +template +const Compare& PriorityQueue::compare() const +{ + return *this; +} + +template +typename PriorityQueue::const_iterator PriorityQueue::begin() const +{ + return m_queue.begin(); +} + +template +typename PriorityQueue::const_iterator PriorityQueue::end() const +{ + return m_queue.end(); +} + +template +typename PriorityQueue::const_reverse_iterator +PriorityQueue::rbegin() const +{ + return m_queue.rbegin(); +} + +template +typename PriorityQueue::const_reverse_iterator +PriorityQueue::rend() const +{ + return m_queue.rend(); +} + +template +typename PriorityQueue::value_type +PriorityQueue::erase(const_iterator it) +{ + // Convert to non-const iterator: + auto non_const_iterator = m_queue.begin() + (it - m_queue.begin()); + value_type value = std::move(*non_const_iterator); + m_queue.erase(non_const_iterator); + return value; +} + +template +void PriorityQueue::clear() +{ + m_queue.clear(); +} + +template +void PriorityQueue::reserve(size_type sz) +{ + m_queue.reserve(sz); +} + +template +void PriorityQueue::swap(PriorityQueue& other) +{ + using std::swap; + swap(m_queue, other.m_queue); + swap(compare(), other.compare()); +} +} +} + +#endif // REALM_UTIL_PRIORITY_QUEUE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/random.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/random.hpp new file mode 100644 index 0000000..f862345 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/random.hpp @@ -0,0 +1,122 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_UTIL_RANDOM_HPP +#define REALM_UTIL_RANDOM_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace util { + +/// Perform a nondeterministc seeding of the specified pseudo random number +/// generator. +/// +/// \tparam Engine A type that satisfies UniformRandomBitGenerator as defined by +/// the C++ standard. +/// +/// \tparam state_size The number of words of type Engine::result_type that make +/// up the engine state. +/// +/// Thread-safe. +/// +/// FIXME: Move this to core repo, as it is generally useful. +template +void seed_prng_nondeterministically(Engine&); + + + + +// Implementation + +} // namespace util + +namespace _impl { + +void get_extra_seed_entropy(unsigned int& extra_entropy_1, unsigned int& extra_entropy_2, + unsigned int& extra_entropy_3); + +} // namespace _impl + +namespace util { + +template void seed_prng_nondeterministically(Engine& engine) +{ + // This implementation was informed and inspired by + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html. + // + // The number of bits of entropy needed is `state_size * + // std::numeric_limits::digits` (assuming that + // the engine uses all available bits in each word). + // + // Each invocation of `std::random_device::operator()` gives us + // `std::numeric_limits::digits` bits (assuming maximum + // entropy). Note that `std::random_device::result_type` must be `unsigned + // int`, `std::random_device::min()` must return zero, and + // `std::random_device::max()` must return `std::numeric_limits::max()`. + // + // Ideally, we could have used `std::random_device::entropy()` as the actual + // number of bits of entropy produced per invocation of + // `std::random_device::operator()`, however, it is incorrectly implemented + // on many platform. Also, it is supposed to return zero when + // `std::random_device` is just a PRNG, but that would leave us with no way + // to continue. + // + // When the actual entropy from `std::random_device` is less than maximum, + // the seeding will be less than optimal. For example, if the actual entropy + // is only half of the maximum, then the seeding will only produce half the + // entrpy that it ought to, but that will generally still be a good seeding. + // + // For the (assumed) rare cases where `std::random_device` is a PRGN that is + // not nondeterministically seeded, we include a bit of extra entropy taken + // from such places as the current time and the ID of the executing process + // (when available). + + constexpr long seed_bits_needed = state_size * + long(std::numeric_limits::digits); + constexpr int seed_bits_per_device_invocation = + std::numeric_limits::digits; + constexpr size_t seed_words_needed = + size_t((seed_bits_needed + (seed_bits_per_device_invocation - 1)) / + seed_bits_per_device_invocation); // Rounding up + constexpr int num_extra = 3; + std::array seed_values; + std::random_device rnddev; + std::generate(seed_values.begin(), seed_values.end()-num_extra, std::ref(rnddev)); + + unsigned int extra_entropy[3]; + _impl::get_extra_seed_entropy(extra_entropy[0], extra_entropy[1], extra_entropy[2]); + static_assert(num_extra == sizeof extra_entropy / sizeof extra_entropy[0], "Mismatch"); + std::copy(extra_entropy, extra_entropy+num_extra, seed_values.end()-num_extra); + + std::seed_seq seed_seq(seed_values.begin(), seed_values.end()); + engine.seed(seed_seq); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_RANDOM_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp new file mode 100644 index 0000000..696f74a --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp @@ -0,0 +1,621 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_SAFE_INT_OPS_HPP +#define REALM_UTIL_SAFE_INT_OPS_HPP + +#ifdef _WIN32 +#undef max // collides with numeric_limits::max called later in this header file +#undef min // collides with numeric_limits::min called later in this header file +#endif + +#include + +#include +#include +#include + +namespace realm { +namespace util { + + +/// Perform integral or floating-point promotion on the argument. This +/// is useful for example when printing a number of arbitrary numeric +/// type to 'stdout', since it will convert values of character-like +/// types to regular integer types, which will then be printed as +/// numbers rather characters. +template +typename Promote::type promote(T value) noexcept; + + +/// This function allows you to test for a negative value in any +/// numeric type, even when the type is unsigned. Normally, when the +/// type is unsigned, such a test will produce a compiler warning. +template +bool is_negative(T value) noexcept; + + +/// Cast the specified value to the specified unsigned type reducing +/// the value (or in case of negative values, the two's complement +/// representation) modulo `2**N` where `N` is the number of value +/// bits (or digits) in the unsigned target type. This is usefull in +/// cases where the target type may be `bool`, but need not be `bool`. +template +To cast_to_unsigned(From) noexcept; + + +//@{ + +/// Compare two integers of the same, or of different type, and +/// produce the expected result according to the natural +/// interpretation of the operation. +/// +/// Note that in general a standard comparison between a signed and an +/// unsigned integer type is unsafe, and it often generates a compiler +/// warning. An example is a 'less than' comparison between a negative +/// value of type 'int' and a small positive value of type +/// 'unsigned'. In this case the negative value will be converted to +/// 'unsigned' producing a large positive value which, in turn, will +/// lead to the counter intuitive result of 'false'. +/// +/// Please note that these operation incur absolutely no overhead when +/// the two types have the same signedness. +/// +/// These functions check at compile time that both types have valid +/// specializations of std::numeric_limits<> and that both are indeed +/// integers. +/// +/// These functions make absolutely no assumptions about the platform +/// except that it complies with at least C++03. + +template +inline bool int_equal_to(A, B) noexcept; +template +inline bool int_not_equal_to(A, B) noexcept; +template +inline bool int_less_than(A, B) noexcept; +template +inline bool int_less_than_or_equal(A, B) noexcept; +template +inline bool int_greater_than(A, B) noexcept; +template +inline bool int_greater_than_or_equal(A, B) noexcept; + +//@} + + +//@{ + +/// Check for overflow in integer variable `lval` while adding integer +/// `rval` to it, or while subtracting integer `rval` from it. Returns +/// true on positive or negative overflow. +/// +/// Both `lval` and `rval` must be of an integer type for which a +/// specialization of std::numeric_limits<> exists. The two types need +/// not be the same, in particular, one can be signed and the other +/// one can be unsigned. +/// +/// These functions are especially well suited for cases where \a rval +/// is a compile-time constant. +/// +/// These functions check at compile time that both types have valid +/// specializations of std::numeric_limits<> and that both are indeed +/// integers. +/// +/// These functions make absolutely no assumptions about the platform +/// except that it complies with at least C++03. + +template +inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept; + +template +inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept; + +//@} + + +/// Check for positive overflow when multiplying two positive integers +/// of the same, or of different type. Returns true on overflow. +/// +/// \param lval Must not be negative. Both signed and unsigned types +/// can be used. +/// +/// \param rval Must be stricly greater than zero. Both signed and +/// unsigned types can be used. +/// +/// This function is especially well suited for cases where \a rval is +/// a compile-time constant. +/// +/// This function checks at compile time that both types have valid +/// specializations of std::numeric_limits<> and that both are indeed +/// integers. +/// +/// This function makes absolutely no assumptions about the platform +/// except that it complies with at least C++03. +template +inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept; + + +/// Checks for positive overflow when performing a bitwise shift to +/// the left on a non-negative value of arbitrary integer +/// type. Returns true on overflow. +/// +/// \param lval Must not be negative. Both signed and unsigned types +/// can be used. +/// +/// \param i Must be non-negative and such that L(1)>>i has a +/// value that is defined by the C++03 standard. +/// +/// This function makes absolutely no assumptions about the platform +/// except that it complies with at least C++03. +template +inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept; + + +//@{ + +/// Check for overflow when casting an integer value from one type to +/// another. While the first function is a mere check, the second one +/// also carries out the cast, but only when there is no +/// overflow. Both return true on overflow. +/// +/// These functions check at compile time that both types have valid +/// specializations of std::numeric_limits<> and that both are indeed +/// integers. +/// +/// These functions make absolutely no assumptions about the platform +/// except that it complies with at least C++03. + +template +bool int_cast_has_overflow(From from) noexcept; + +template +bool int_cast_with_overflow_detect(From from, To& to) noexcept; + +//@} + + +/// Convert negative values from two's complement representation to the +/// platforms native representation. +/// +/// If `To` is an unsigned type, this function does nothing beyond casting the +/// specified value to `To`. Otherwise, `To` is a signed type, and negative +/// values will be converted from two's complement representation in unsigned +/// `From` to the platforms native representation in `To`. +/// +/// For signed `To` the result is well-defined if, and only if the value with +/// the specified two's complement representation is representable in the +/// specified signed type. While this is generally the case when using +/// corresponding signed/unsigned type pairs, it is not guaranteed by the +/// standard. However, if you know that the signed type has at least as many +/// value bits as the unsigned type, then the result is always +/// well-defined. Note that a 'value bit' in this context is the same as a +/// 'digit' from the point of view of `std::numeric_limits`. +/// +/// On platforms that use two's complement representation of negative values, +/// this function is expected to be completely optimized away. This has been +/// observed to be true with both GCC 4.8 and Clang 3.2. +/// +/// Note that the **opposite** direction (from the platforms native +/// representation to two's complement) is trivially handled by casting the +/// signed value to a value of a sufficiently wide unsigned integer type. An +/// unsigned type will be sufficiently wide if it has at least one more value +/// bit than the signed type. +/// +/// Interestingly, the C++ language offers no direct way of doing what this +/// function does, yet, this function is implemented in a way that makes no +/// assumption about the underlying platform except what is guaranteed by C++11. +/// +/// \tparam From The unsigned type used to store the two's complement +/// representation. +/// +/// \tparam To A signed or unsigned integer type. +template +To from_twos_compl(From twos_compl) noexcept; + + +// Implementation: + +template +inline typename Promote::type promote(T value) noexcept +{ + typedef typename Promote::type promoted_type; + promoted_type value_2 = promoted_type(value); + return value_2; +} + +} // namespace util + +namespace _impl { + +template +struct IsNegative { + static bool test(T value) noexcept + { + return value < 0; + } +}; +template +struct IsNegative { + static bool test(T) noexcept + { + return false; + } +}; + +template +struct CastToUnsigned { + template + static To cast(From value) noexcept + { + return To(value); + } +}; +template <> +struct CastToUnsigned { + template + static bool cast(From value) noexcept + { + return bool(unsigned(value) & 1); + } +}; + +template +struct SafeIntBinopsImpl { +}; + +// (unsigned, unsigned) (all size combinations) +// +// This implementation utilizes the fact that overflow in unsigned +// arithmetic is guaranteed to be handled by reduction modulo 2**N +// where N is the number of bits in the unsigned type. The purpose of +// the bitwise 'and' with lim_l::max() is to make a cast to bool +// behave the same way as casts to other unsigned integer types. +// Finally, this implementation uses the fact that if modular addition +// overflows, then the result must be a value that is less than both +// operands. Also, if modular subtraction overflows, then the result +// must be a value that is greater than the first operand. +template +struct SafeIntBinopsImpl { + typedef std::numeric_limits lim_l; + typedef std::numeric_limits lim_r; + static const int needed_bits_l = lim_l::digits; + static const int needed_bits_r = lim_r::digits; + static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r; + typedef typename util::FastestUnsigned::type common_unsigned; + static bool equal(L l, R r) noexcept + { + return common_unsigned(l) == common_unsigned(r); + } + static bool less(L l, R r) noexcept + { + return common_unsigned(l) < common_unsigned(r); + } + static bool add(L& lval, R rval) noexcept + { + L lval_2 = util::cast_to_unsigned(lval + rval); + bool overflow = common_unsigned(lval_2) < common_unsigned(rval); + if (REALM_UNLIKELY(overflow)) + return true; + lval = lval_2; + return false; + } + static bool sub(L& lval, R rval) noexcept + { + common_unsigned lval_2 = common_unsigned(lval) - common_unsigned(rval); + bool overflow = lval_2 > common_unsigned(lval); + if (REALM_UNLIKELY(overflow)) + return true; + lval = util::cast_to_unsigned(lval_2); + return false; + } +}; + +// (unsigned, signed) (all size combinations) +template +struct SafeIntBinopsImpl { + typedef std::numeric_limits lim_l; + typedef std::numeric_limits lim_r; + static const int needed_bits_l = lim_l::digits; + static const int needed_bits_r = lim_r::digits + 1; + static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r; + typedef typename util::FastestUnsigned::type common_unsigned; + typedef std::numeric_limits lim_cu; + static bool equal(L l, R r) noexcept + { + return (lim_l::digits > lim_r::digits) ? r >= 0 && l == util::cast_to_unsigned(r) : R(l) == r; + } + static bool less(L l, R r) noexcept + { + return (lim_l::digits > lim_r::digits) ? r >= 0 && l < util::cast_to_unsigned(r) : R(l) < r; + } + static bool add(L& lval, R rval) noexcept + { + common_unsigned lval_2 = lval + common_unsigned(rval); + bool overflow; + if (lim_l::digits < lim_cu::digits) { + overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max()); + } + else { + overflow = (lval_2 < common_unsigned(lval)) == (rval >= 0); + } + if (REALM_UNLIKELY(overflow)) + return true; + lval = util::cast_to_unsigned(lval_2); + return false; + } + static bool sub(L& lval, R rval) noexcept + { + common_unsigned lval_2 = lval - common_unsigned(rval); + bool overflow; + if (lim_l::digits < lim_cu::digits) { + overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max()); + } + else { + overflow = (common_unsigned(lval_2) > common_unsigned(lval)) == (rval >= 0); + } + if (REALM_UNLIKELY(overflow)) + return true; + lval = util::cast_to_unsigned(lval_2); + return false; + } +}; + +// (signed, unsigned) (all size combinations) +template +struct SafeIntBinopsImpl { + typedef std::numeric_limits lim_l; + typedef std::numeric_limits lim_r; + static const int needed_bits_l = lim_l::digits + 1; + static const int needed_bits_r = lim_r::digits; + static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r; + typedef typename util::FastestUnsigned::type common_unsigned; + static bool equal(L l, R r) noexcept + { + return (lim_l::digits < lim_r::digits) ? l >= 0 && util::cast_to_unsigned(l) == r : l == L(r); + } + static bool less(L l, R r) noexcept + { + return (lim_l::digits < lim_r::digits) ? l < 0 || util::cast_to_unsigned(l) < r : l < L(r); + } + static bool add(L& lval, R rval) noexcept + { + common_unsigned max_add = common_unsigned(lim_l::max()) - common_unsigned(lval); + bool overflow = common_unsigned(rval) > max_add; + if (REALM_UNLIKELY(overflow)) + return true; + lval = util::from_twos_compl(common_unsigned(lval) + rval); + return false; + } + static bool sub(L& lval, R rval) noexcept + { + common_unsigned max_sub = common_unsigned(lval) - common_unsigned(lim_l::min()); + bool overflow = common_unsigned(rval) > max_sub; + if (REALM_UNLIKELY(overflow)) + return true; + lval = util::from_twos_compl(common_unsigned(lval) - rval); + return false; + } +}; + +// (signed, signed) (all size combinations) +template +struct SafeIntBinopsImpl { + typedef std::numeric_limits lim_l; + static bool equal(L l, R r) noexcept + { + return l == r; + } + static bool less(L l, R r) noexcept + { + return l < r; + } + static bool add(L& lval, R rval) noexcept + { + // Note that both subtractions below occur in a signed type + // that is at least as wide as both of the two types. Note + // also that any signed type guarantees that there is no + // overflow when subtracting two negative values or two + // non-negative value. See C99 (adopted as subset of C++11) + // section 6.2.6.2 "Integer types" paragraph 2. + if (rval < 0) { + if (REALM_UNLIKELY(lval < lim_l::min() - rval)) + return true; + } + else { + if (REALM_UNLIKELY(lval > lim_l::max() - rval)) + return true; + } + // The following statement has exactly the same effect as + // `lval += rval`. + lval = L(lval + rval); + return false; + } + static bool sub(L& lval, R rval) noexcept + { + // Note that both subtractions below occur in a signed type + // that is at least as wide as both of the two types. Note + // also that there can be no overflow when adding a negative + // value to a non-negative value, or when adding a + // non-negative value to a negative one. + if (rval < 0) { + if (REALM_UNLIKELY(lval > lim_l::max() + rval)) + return true; + } + else { + if (REALM_UNLIKELY(lval < lim_l::min() + rval)) + return true; + } + // The following statement has exactly the same effect as + // `lval += rval`. + lval = L(lval - rval); + return false; + } +}; + +template +struct SafeIntBinops : SafeIntBinopsImpl::is_signed, std::numeric_limits::is_signed> { + typedef std::numeric_limits lim_l; + typedef std::numeric_limits lim_r; + static_assert(lim_l::is_specialized && lim_r::is_specialized, + "std::numeric_limits<> must be specialized for both types"); + static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers"); +}; + +} // namespace _impl + +namespace util { + +template +inline bool is_negative(T value) noexcept +{ + return _impl::IsNegative::is_signed>::test(value); +} + +template +inline To cast_to_unsigned(From value) noexcept +{ + return _impl::CastToUnsigned::cast(value); +} + +template +inline bool int_equal_to(A a, B b) noexcept +{ + return _impl::SafeIntBinops::equal(a, b); +} + +template +inline bool int_not_equal_to(A a, B b) noexcept +{ + return !_impl::SafeIntBinops::equal(a, b); +} + +template +inline bool int_less_than(A a, B b) noexcept +{ + return _impl::SafeIntBinops::less(a, b); +} + +template +inline bool int_less_than_or_equal(A a, B b) noexcept +{ + return !_impl::SafeIntBinops::less(b, a); // Not greater than +} + +template +inline bool int_greater_than(A a, B b) noexcept +{ + return _impl::SafeIntBinops::less(b, a); +} + +template +inline bool int_greater_than_or_equal(A a, B b) noexcept +{ + return !_impl::SafeIntBinops::less(a, b); // Not less than +} + +template +inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept +{ + return _impl::SafeIntBinops::add(lval, rval); +} + +template +inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept +{ + return _impl::SafeIntBinops::sub(lval, rval); +} + +template +inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept +{ + // FIXME: Check if the following optimizes better (if it works at all): + // L lval_2 = L(lval * rval); + // bool overflow = rval != 0 && (lval_2 / rval) != lval; + typedef std::numeric_limits lim_l; + typedef std::numeric_limits lim_r; + static_assert(lim_l::is_specialized && lim_r::is_specialized, + "std::numeric_limits<> must be specialized for both types"); + static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers"); + REALM_ASSERT(int_greater_than_or_equal(lval, 0)); + REALM_ASSERT(int_greater_than(rval, 0)); + if (int_less_than(lim_r::max() / rval, lval)) + return true; + lval = L(lval * rval); + return false; +} + +template +inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept +{ + typedef std::numeric_limits lim; + static_assert(lim::is_specialized, "std::numeric_limits<> must be specialized for T"); + static_assert(lim::is_integer, "T must be an integer type"); + REALM_ASSERT(int_greater_than_or_equal(lval, 0)); + if ((lim::max() >> i) < lval) + return true; + lval <<= i; + return false; +} + +template +inline bool int_cast_has_overflow(From from) noexcept +{ + typedef std::numeric_limits lim_to; + return int_less_than(from, lim_to::min()) || int_less_than(lim_to::max(), from); +} + +template +inline bool int_cast_with_overflow_detect(From from, To& to) noexcept +{ + if (REALM_LIKELY(!int_cast_has_overflow(from))) { + to = To(from); + return false; + } + return true; +} + +template +inline To from_twos_compl(From twos_compl) noexcept +{ + typedef std::numeric_limits lim_f; + typedef std::numeric_limits lim_t; + static_assert(lim_f::is_specialized && lim_t::is_specialized, + "std::numeric_limits<> must be specialized for both types"); + static_assert(lim_f::is_integer && lim_t::is_integer, "Both types must be integers"); + static_assert(!lim_f::is_signed, "`From` must be unsigned"); + To native; + int sign_bit_pos = lim_f::digits - 1; + From sign_bit = From(1) << sign_bit_pos; + bool non_negative = !lim_t::is_signed || (twos_compl & sign_bit) == 0; + if (non_negative) { + // Non-negative value + native = To(twos_compl); + } + else { + // Negative value + native = To(-1 - To(From(-1) - twos_compl)); + } + return native; +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SAFE_INT_OPS_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp new file mode 100644 index 0000000..5410d19 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp @@ -0,0 +1,72 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_SCOPE_EXIT_HPP +#define REALM_UTIL_SCOPE_EXIT_HPP + +#include +#include + +#include + +namespace realm { +namespace util { + +template +class ScopeExit { +public: + explicit ScopeExit(const H& handler) noexcept(std::is_nothrow_copy_constructible::value) + : m_handler(handler) + { + } + + explicit ScopeExit(H&& handler) noexcept(std::is_nothrow_move_constructible::value) + : m_handler(std::move(handler)) + { + } + + ScopeExit(ScopeExit&& se) noexcept(std::is_nothrow_move_constructible::value) + : m_handler(std::move(se.m_handler)) + { + se.m_handler = none; + } + + ~ScopeExit() noexcept + { + if (m_handler) + (*m_handler)(); + } + + static_assert(noexcept(std::declval()()), "Handler must be nothrow executable"); + static_assert(std::is_nothrow_destructible::value, "Handler must be nothrow destructible"); + +private: + util::Optional m_handler; +}; + +template +ScopeExit::type> make_scope_exit(H&& handler) noexcept( + noexcept(ScopeExit::type>(std::forward(handler)))) +{ + return ScopeExit::type>(std::forward(handler)); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SCOPE_EXIT_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp new file mode 100644 index 0000000..f89b24f --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp @@ -0,0 +1,130 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_SHARED_PTR_HPP +#define REALM_SHARED_PTR_HPP + +#include // size_t + +namespace realm { +namespace util { + +template +class SharedPtr { +public: + SharedPtr(T* p) + { + init(p); + } + + SharedPtr() + { + init(0); + } + + ~SharedPtr() + { + decref(); + } + + SharedPtr(const SharedPtr& o) + : m_ptr(o.m_ptr) + , m_count(o.m_count) + { + incref(); + } + + SharedPtr& operator=(const SharedPtr& o) + { + if (m_ptr == o.m_ptr) + return *this; + decref(); + m_ptr = o.m_ptr; + m_count = o.m_count; + incref(); + return *this; + } + + T* operator->() const + { + return m_ptr; + } + + T& operator*() const + { + return *m_ptr; + } + + T* get() const + { + return m_ptr; + } + + bool operator==(const SharedPtr& o) const + { + return m_ptr == o.m_ptr; + } + + bool operator!=(const SharedPtr& o) const + { + return m_ptr != o.m_ptr; + } + + bool operator<(const SharedPtr& o) const + { + return m_ptr < o.m_ptr; + } + + size_t ref_count() const + { + return *m_count; + } + +private: + void init(T* p) + { + m_ptr = p; + try { + m_count = new size_t(1); + } + catch (...) { + delete p; + throw; + } + } + + void decref() + { + if (--(*m_count) == 0) { + delete m_ptr; + delete m_count; + } + } + + void incref() + { + ++(*m_count); + } + + T* m_ptr; + size_t* m_count; +}; +} +} + +#endif diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp new file mode 100644 index 0000000..9d85043 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp @@ -0,0 +1,169 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_STRING_BUFFER_HPP +#define REALM_UTIL_STRING_BUFFER_HPP + +#include +#include +#include + +#include +#include + +namespace realm { +namespace util { + + +// FIXME: Check whether this class provides anything that a C++03 +// std::string does not already provide. In particular, can a C++03 +// std::string be used as a contiguous mutable buffer? +class StringBuffer { +public: + StringBuffer() noexcept; + + std::string str() const; + + /// Returns the current size of the string in this buffer. This + /// size does not include the terminating zero. + size_t size() const noexcept; + + /// Gives read and write access to the bytes of this buffer. The + /// caller may read and write from *c_str() up to, but not + /// including, *(c_str()+size()). + char* data() noexcept; + + /// Gives read access to the bytes of this buffer. The caller may + /// read from *c_str() up to, but not including, + /// *(c_str()+size()). + const char* data() const noexcept; + + /// Guarantees that the returned string is zero terminated, that + /// is, *(c_str()+size()) is zero. The caller may read from + /// *c_str() up to and including *(c_str()+size()). + const char* c_str() const noexcept; + + void append(const std::string&); + + void append(const char* append_data, size_t append_size); + + /// Append a zero-terminated string to this buffer. + void append_c_str(const char* c_string); + + /// The specified size is understood as not including the + /// terminating zero. If the specified size is less than the + /// current size, then the string is truncated accordingly. If the + /// specified size is greater than the current size, then the + /// extra characters will have undefined values, however, there + /// will be a terminating zero at *(c_str()+size()), and the + /// original terminating zero will also be left in place such that + /// from the point of view of c_str(), the size of the string is + /// unchanged. + void resize(size_t new_size); + + /// The specified minimum capacity is understood as not including + /// the terminating zero. This operation does not change the size + /// of the string in the buffer as returned by size(). If the + /// specified capacity is less than the current capacity, this + /// operation has no effect. + void reserve(size_t min_capacity); + + /// Set size to zero. The capacity remains unchanged. + void clear() noexcept; + +private: + util::Buffer m_buffer; + size_t m_size; // Excluding the terminating zero + void reallocate(size_t min_capacity); +}; + + +// Implementation: + +inline StringBuffer::StringBuffer() noexcept + : m_size(0) +{ +} + +inline std::string StringBuffer::str() const +{ + return std::string(m_buffer.data(), m_size); +} + +inline size_t StringBuffer::size() const noexcept +{ + return m_size; +} + +inline char* StringBuffer::data() noexcept +{ + return m_buffer.data(); +} + +inline const char* StringBuffer::data() const noexcept +{ + return m_buffer.data(); +} + +inline const char* StringBuffer::c_str() const noexcept +{ + static const char zero = 0; + const char* d = data(); + return d ? d : &zero; +} + +inline void StringBuffer::append(const std::string& s) +{ + return append(s.data(), s.size()); +} + +inline void StringBuffer::append_c_str(const char* c_string) +{ + append(c_string, std::strlen(c_string)); +} + +inline void StringBuffer::reserve(size_t min_capacity) +{ + size_t capacity = m_buffer.size(); + if (capacity == 0 || capacity - 1 < min_capacity) + reallocate(min_capacity); +} + +inline void StringBuffer::resize(size_t new_size) +{ + reserve(new_size); + // Note that even reserve(0) will attempt to allocate a + // buffer, so we can safely write the truncating zero at this + // time. + m_size = new_size; + m_buffer[new_size] = 0; +} + +inline void StringBuffer::clear() noexcept +{ + if (m_buffer.size() == 0) + return; + m_size = 0; + m_buffer[0] = 0; +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_STRING_BUFFER_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/terminate.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/terminate.hpp new file mode 100644 index 0000000..4e6034e --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/terminate.hpp @@ -0,0 +1,59 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_TERMINATE_HPP +#define REALM_UTIL_TERMINATE_HPP + +#include + +#include +#include +#include + +#define REALM_TERMINATE(msg) realm::util::terminate((msg), __FILE__, __LINE__) + +namespace realm { +namespace util { + +REALM_NORETURN void terminate(const char* message, const char* file, long line, + std::initializer_list&& = {}) noexcept; +REALM_NORETURN void terminate_with_info(const char* message, const char* file, long line, + const char* interesting_names, + std::initializer_list&& = {}) noexcept; + +// LCOV_EXCL_START +template +REALM_NORETURN void terminate(const char* message, const char* file, long line, Ts... infos) noexcept +{ + static_assert(sizeof...(infos) == 2 || sizeof...(infos) == 4 || sizeof...(infos) == 6, + "Called realm::util::terminate() with wrong number of arguments"); + terminate(message, file, line, {Printable(infos)...}); +} + +template +REALM_NORETURN void terminate_with_info(const char* assert_message, int line, const char* file, + const char* interesting_names, Args&&... interesting_values) noexcept +{ + terminate_with_info(assert_message, file, line, interesting_names, {Printable(interesting_values)...}); +} +// LCOV_EXCL_STOP + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TERMINATE_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/thread.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/thread.hpp new file mode 100644 index 0000000..9c032e7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/thread.hpp @@ -0,0 +1,680 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_THREAD_HPP +#define REALM_UTIL_THREAD_HPP + +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +// Use below line to enable a thread bug detection tool. Note: Will make program execution slower. +// #include <../test/pthread_test.hpp> + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace realm { +namespace util { + + +/// A separate thread of execution. +/// +/// This class is a C++03 compatible reproduction of a subset of std::thread +/// from C++11 (when discounting Thread::start(), Thread::set_name(), and +/// Thread::get_name()). +class Thread { +public: + Thread(); + ~Thread() noexcept; + + template + explicit Thread(F func); + + // Disable copying. It is an error to copy this Thread class. + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + + Thread(Thread&&); + + /// This method is an extension of the API provided by + /// std::thread. This method exists because proper move semantics + /// is unavailable in C++03. If move semantics had been available, + /// calling `start(func)` would have been equivalent to `*this = + /// Thread(func)`. Please see std::thread::operator=() for + /// details. + template + void start(F func); + + bool joinable() noexcept; + + void join(); + + // If supported by the platform, set the name of the calling thread (mainly + // for debugging purposes). The name will be silently clamped to whatever + // limit the platform places on these names. Linux places a limit of 15 + // characters for these names. + static void set_name(const std::string&); + + // If supported by the platform, this function assigns the name of the + // calling thread to \a name, and returns true, otherwise it does nothing + // and returns false. + static bool get_name(std::string& name); + +private: + pthread_t m_id; + bool m_joinable; + + typedef void* (*entry_func_type)(void*); + + void start(entry_func_type, void* arg); + + template + static void* entry_point(void*) noexcept; + + REALM_NORETURN static void create_failed(int); + REALM_NORETURN static void join_failed(int); +}; + + +/// Low-level mutual exclusion device. +class Mutex { +public: + Mutex(); + ~Mutex() noexcept; + + struct process_shared_tag { + }; + /// Initialize this mutex for use across multiple processes. When + /// constructed this way, the instance may be placed in memory + /// shared by multiple processes, as well as in a memory mapped + /// file. Such a mutex remains valid even after the constructing + /// process terminates. Deleting the instance (freeing the memory + /// or deleting the file) without first calling the destructor is + /// legal and will not cause any system resources to be leaked. + Mutex(process_shared_tag); + + // Disable copying. + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + friend class LockGuard; + friend class UniqueLock; + + void lock() noexcept; + bool try_lock() noexcept; + void unlock() noexcept; + +protected: + pthread_mutex_t m_impl = PTHREAD_MUTEX_INITIALIZER; + + struct no_init_tag { + }; + Mutex(no_init_tag) + { + } + + void init_as_regular(); + void init_as_process_shared(bool robust_if_available); + + REALM_NORETURN static void init_failed(int); + REALM_NORETURN static void attr_init_failed(int); + REALM_NORETURN static void destroy_failed(int) noexcept; + REALM_NORETURN static void lock_failed(int) noexcept; + + friend class CondVar; +}; + + +/// A simple mutex ownership wrapper. +class LockGuard { +public: + LockGuard(Mutex&) noexcept; + ~LockGuard() noexcept; + +private: + Mutex& m_mutex; + friend class CondVar; +}; + + +/// See UniqueLock. +struct defer_lock_tag { +}; + +/// A general-purpose mutex ownership wrapper supporting deferred +/// locking as well as repeated unlocking and relocking. +class UniqueLock { +public: + UniqueLock(Mutex&) noexcept; + UniqueLock(Mutex&, defer_lock_tag) noexcept; + ~UniqueLock() noexcept; + + void lock() noexcept; + void unlock() noexcept; + bool holds_lock() noexcept; + +private: + Mutex* m_mutex; + bool m_is_locked; +}; + + +/// A robust version of a process-shared mutex. +/// +/// A robust mutex is one that detects whether a thread (or process) +/// has died while holding a lock on the mutex. +/// +/// When the present platform does not offer support for robust +/// mutexes, this mutex class behaves as a regular process-shared +/// mutex, which means that if a thread dies while holding a lock, any +/// future attempt at locking will block indefinitely. +class RobustMutex : private Mutex { +public: + RobustMutex(); + ~RobustMutex() noexcept; + + static bool is_robust_on_this_platform() noexcept; + + class NotRecoverable; + + /// \param recover_func If the present platform does not support + /// robust mutexes, this function is never called. Otherwise it is + /// called if, and only if a thread has died while holding a + /// lock. The purpose of the function is to reestablish a + /// consistent shared state. If it fails to do this by throwing an + /// exception, the mutex enters the 'unrecoverable' state where + /// any future attempt at locking it will fail and cause + /// NotRecoverable to be thrown. This function is advised to throw + /// NotRecoverable when it fails, but it may throw any exception. + /// + /// \throw NotRecoverable If thrown by the specified recover + /// function, or if the mutex has entered the 'unrecoverable' + /// state due to a different thread throwing from its recover + /// function. + template + void lock(Func recover_func); + + template + bool try_lock(Func recover_func); + + void unlock() noexcept; + + /// Low-level locking of robust mutex. + /// + /// If the present platform does not support robust mutexes, this + /// function always returns true. Otherwise it returns false if, + /// and only if a thread has died while holding a lock. + /// + /// \note Most application should never call this function + /// directly. It is called automatically when using the ordinary + /// lock() function. + /// + /// \throw NotRecoverable If this mutex has entered the "not + /// recoverable" state. It enters this state if + /// mark_as_consistent() is not called between a call to + /// robust_lock() that returns false and the corresponding call to + /// unlock(). + bool low_level_lock(); + + /// Low-level try-lock of robust mutex + /// + /// If the present platform does not support robust mutexes, this + /// function always returns 0 or 1. Otherwise it returns -1 if, + /// and only if a thread has died while holding a lock. + /// + /// Returns 1 if the lock is succesfully obtained. + /// Returns 0 if the lock is held by somebody else (not obtained) + /// Returns -1 if a thread has died while holding a lock. + /// + /// \note Most application should never call this function + /// directly. It is called automatically when using the ordinary + /// lock() function. + /// + /// \throw NotRecoverable If this mutex has entered the "not + /// recoverable" state. It enters this state if + /// mark_as_consistent() is not called between a call to + /// robust_lock() that returns false and the corresponding call to + /// unlock(). + int try_low_level_lock(); + + /// Pull this mutex out of the 'inconsistent' state. + /// + /// Must be called only after low_level_lock() has returned false. + /// + /// \note Most application should never call this function + /// directly. It is called automatically when using the ordinary + /// lock() function. + void mark_as_consistent() noexcept; + + /// Attempt to check if this mutex is a valid object. + /// + /// This attempts to trylock() the mutex, and if that fails returns false if + /// the return value indicates that the low-level mutex is invalid (which is + /// distinct from 'inconsistent'). Although pthread_mutex_trylock() may + /// return EINVAL if the argument is not an initialized mutex object, merely + /// attempting to check if an arbitrary blob of memory is a mutex object may + /// involve undefined behavior, so it is only safe to assume that this + /// function will run correctly when it is known that the mutex object is + /// valid. + bool is_valid() noexcept; + + friend class CondVar; +}; + +class RobustMutex::NotRecoverable : public std::exception { +public: + const char* what() const noexcept override + { + return "Failed to recover consistent state of shared memory"; + } +}; + + +/// A simple robust mutex ownership wrapper. +class RobustLockGuard { +public: + /// \param m the mutex to guard + /// \param func See RobustMutex::lock(). + template + RobustLockGuard(RobustMutex& m, TFunc func); + ~RobustLockGuard() noexcept; + +private: + RobustMutex& m_mutex; + friend class CondVar; +}; + + +/// Condition variable for use in synchronization monitors. +class CondVar { +public: + CondVar(); + ~CondVar() noexcept; + + struct process_shared_tag { + }; + + /// Initialize this condition variable for use across multiple + /// processes. When constructed this way, the instance may be + /// placed in memory shared by multimple processes, as well as in + /// a memory mapped file. Such a condition variable remains valid + /// even after the constructing process terminates. Deleting the + /// instance (freeing the memory or deleting the file) without + /// first calling the destructor is legal and will not cause any + /// system resources to be leaked. + CondVar(process_shared_tag); + + /// Wait for another thread to call notify() or notify_all(). + void wait(LockGuard& l) noexcept; + template + void wait(RobustMutex& m, Func recover_func, const struct timespec* tp = nullptr); + + /// If any threads are wating for this condition, wake up at least + /// one. + void notify() noexcept; + + /// Wake up every thread that is currently wating on this + /// condition. + void notify_all() noexcept; + +private: + pthread_cond_t m_impl; + + REALM_NORETURN static void init_failed(int); + REALM_NORETURN static void attr_init_failed(int); + REALM_NORETURN static void destroy_failed(int) noexcept; + void handle_wait_error(int error); +}; + + +// Implementation: + +inline Thread::Thread() + : m_joinable(false) +{ +} + +template +inline Thread::Thread(F func) + : m_joinable(true) +{ + std::unique_ptr func2(new F(func)); // Throws + start(&Thread::entry_point, func2.get()); // Throws + func2.release(); +} + +inline Thread::Thread(Thread&& thread) +{ + m_id = thread.m_id; + m_joinable = thread.m_joinable; + thread.m_joinable = false; +} + +template +inline void Thread::start(F func) +{ + if (m_joinable) + std::terminate(); + std::unique_ptr func2(new F(func)); // Throws + start(&Thread::entry_point, func2.get()); // Throws + func2.release(); + m_joinable = true; +} + +inline Thread::~Thread() noexcept +{ + if (m_joinable) + REALM_TERMINATE("Destruction of joinable thread"); +} + +inline bool Thread::joinable() noexcept +{ + return m_joinable; +} + +inline void Thread::start(entry_func_type entry_func, void* arg) +{ + const pthread_attr_t* attr = nullptr; // Use default thread attributes + int r = pthread_create(&m_id, attr, entry_func, arg); + if (REALM_UNLIKELY(r != 0)) + create_failed(r); // Throws +} + +template +inline void* Thread::entry_point(void* cookie) noexcept +{ + std::unique_ptr func(static_cast(cookie)); + try { + (*func)(); + } + catch (...) { + std::terminate(); + } + return 0; +} + + +inline Mutex::Mutex() +{ + init_as_regular(); +} + +inline Mutex::Mutex(process_shared_tag) +{ + bool robust_if_available = false; + init_as_process_shared(robust_if_available); +} + +inline Mutex::~Mutex() noexcept +{ + int r = pthread_mutex_destroy(&m_impl); + if (REALM_UNLIKELY(r != 0)) + destroy_failed(r); +} + +inline void Mutex::init_as_regular() +{ + int r = pthread_mutex_init(&m_impl, 0); + if (REALM_UNLIKELY(r != 0)) + init_failed(r); +} + +inline void Mutex::lock() noexcept +{ + int r = pthread_mutex_lock(&m_impl); + if (REALM_LIKELY(r == 0)) + return; + lock_failed(r); +} + +inline bool Mutex::try_lock() noexcept +{ + int r = pthread_mutex_trylock(&m_impl); + if (r == EBUSY) { + return false; + } + else if (r == 0) { + return true; + } + lock_failed(r); +} + +inline void Mutex::unlock() noexcept +{ + int r = pthread_mutex_unlock(&m_impl); + REALM_ASSERT(r == 0); +} + + +inline LockGuard::LockGuard(Mutex& m) noexcept + : m_mutex(m) +{ + m_mutex.lock(); +} + +inline LockGuard::~LockGuard() noexcept +{ + m_mutex.unlock(); +} + + +inline UniqueLock::UniqueLock(Mutex& m) noexcept + : m_mutex(&m) +{ + m_mutex->lock(); + m_is_locked = true; +} + +inline UniqueLock::UniqueLock(Mutex& m, defer_lock_tag) noexcept + : m_mutex(&m) +{ + m_is_locked = false; +} + +inline UniqueLock::~UniqueLock() noexcept +{ + if (m_is_locked) + m_mutex->unlock(); +} + +inline bool UniqueLock::holds_lock() noexcept +{ + return m_is_locked; +} + +inline void UniqueLock::lock() noexcept +{ + m_mutex->lock(); + m_is_locked = true; +} + +inline void UniqueLock::unlock() noexcept +{ + m_mutex->unlock(); + m_is_locked = false; +} + +template +inline RobustLockGuard::RobustLockGuard(RobustMutex& m, TFunc func) + : m_mutex(m) +{ + m_mutex.lock(func); +} + +inline RobustLockGuard::~RobustLockGuard() noexcept +{ + m_mutex.unlock(); +} + + +inline RobustMutex::RobustMutex() + : Mutex(no_init_tag()) +{ + bool robust_if_available = true; + init_as_process_shared(robust_if_available); +} + +inline RobustMutex::~RobustMutex() noexcept +{ +} + +template +inline void RobustMutex::lock(Func recover_func) +{ + bool no_thread_has_died = low_level_lock(); // Throws + if (REALM_LIKELY(no_thread_has_died)) + return; + try { + recover_func(); // Throws + mark_as_consistent(); + // If we get this far, the protected memory has been + // brought back into a consistent state, and the mutex has + // been notified about this. This means that we can safely + // enter the applications critical section. + } + catch (...) { + // Unlocking without first calling mark_as_consistent() + // means that the mutex enters the "not recoverable" + // state, which will cause all future attempts at locking + // to fail. + unlock(); + throw; + } +} + +template +inline bool RobustMutex::try_lock(Func recover_func) +{ + int lock_result = try_low_level_lock(); // Throws + if (lock_result == 0) return false; + bool no_thread_has_died = lock_result == 1; + if (REALM_LIKELY(no_thread_has_died)) + return true; + try { + recover_func(); // Throws + mark_as_consistent(); + // If we get this far, the protected memory has been + // brought back into a consistent state, and the mutex has + // been notified aboit this. This means that we can safely + // enter the applications critical section. + } + catch (...) { + // Unlocking without first calling mark_as_consistent() + // means that the mutex enters the "not recoverable" + // state, which will cause all future attempts at locking + // to fail. + unlock(); + throw; + } + return true; +} + +inline void RobustMutex::unlock() noexcept +{ + Mutex::unlock(); +} + + +inline CondVar::CondVar() +{ + int r = pthread_cond_init(&m_impl, 0); + if (REALM_UNLIKELY(r != 0)) + init_failed(r); +} + +inline CondVar::~CondVar() noexcept +{ + int r = pthread_cond_destroy(&m_impl); + if (REALM_UNLIKELY(r != 0)) + destroy_failed(r); +} + +inline void CondVar::wait(LockGuard& l) noexcept +{ + int r = pthread_cond_wait(&m_impl, &l.m_mutex.m_impl); + if (REALM_UNLIKELY(r != 0)) + REALM_TERMINATE("pthread_cond_wait() failed"); +} + +template +inline void CondVar::wait(RobustMutex& m, Func recover_func, const struct timespec* tp) +{ + int r; + + if (!tp) { + r = pthread_cond_wait(&m_impl, &m.m_impl); + } + else { + r = pthread_cond_timedwait(&m_impl, &m.m_impl, tp); + if (r == ETIMEDOUT) + return; + } + + if (REALM_LIKELY(r == 0)) + return; + + handle_wait_error(r); + + try { + recover_func(); // Throws + m.mark_as_consistent(); + // If we get this far, the protected memory has been + // brought back into a consistent state, and the mutex has + // been notified aboit this. This means that we can safely + // enter the applications critical section. + } + catch (...) { + // Unlocking without first calling mark_as_consistent() + // means that the mutex enters the "not recoverable" + // state, which will cause all future attempts at locking + // to fail. + m.unlock(); + throw; + } +} + +inline void CondVar::notify() noexcept +{ + int r = pthread_cond_signal(&m_impl); + REALM_ASSERT(r == 0); +} + +inline void CondVar::notify_all() noexcept +{ + int r = pthread_cond_broadcast(&m_impl); + REALM_ASSERT(r == 0); +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_THREAD_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/to_string.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/to_string.hpp new file mode 100644 index 0000000..3b6efa4 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/to_string.hpp @@ -0,0 +1,111 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_TO_STRING_HPP +#define REALM_UTIL_TO_STRING_HPP + +#include +#include + +namespace realm { +namespace util { + +class Printable { +public: + Printable(bool value) + : m_type(Type::Bool) + , m_uint(value) + { + } + Printable(unsigned char value) + : m_type(Type::Uint) + , m_uint(value) + { + } + Printable(unsigned int value) + : m_type(Type::Uint) + , m_uint(value) + { + } + Printable(unsigned long value) + : m_type(Type::Uint) + , m_uint(value) + { + } + Printable(unsigned long long value) + : m_type(Type::Uint) + , m_uint(value) + { + } + Printable(char value) + : m_type(Type::Int) + , m_int(value) + { + } + Printable(int value) + : m_type(Type::Int) + , m_int(value) + { + } + Printable(long value) + : m_type(Type::Int) + , m_int(value) + { + } + Printable(long long value) + : m_type(Type::Int) + , m_int(value) + { + } + Printable(const char* value) + : m_type(Type::String) + , m_string(value) + { + } + + void print(std::ostream& out, bool quote) const; + std::string str() const; + + static void print_all(std::ostream& out, const std::initializer_list& values, bool quote); + +private: + enum class Type { + Bool, + Int, + Uint, + String, + } m_type; + + union { + uintmax_t m_uint; + intmax_t m_int; + const char* m_string; + }; +}; + + +template +std::string to_string(const T& v) +{ + return Printable(v).str(); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TO_STRING_HPP diff --git a/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/type_list.hpp b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/type_list.hpp new file mode 100644 index 0000000..da847c7 --- /dev/null +++ b/Desafio Mobile iOS/Pods/Realm/include/core/realm/util/type_list.hpp @@ -0,0 +1,244 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * 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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_TYPE_LIST_HPP +#define REALM_UTIL_TYPE_LIST_HPP + +namespace realm { +namespace util { + + +/// The 'cons' operator for building lists of types. +/// +/// \tparam H The head of the list, that is, the first type in the +/// list. +/// +/// \tparam T The tail of the list, that is, the list of types +/// following the head. It is 'void' if nothing follows the head, +/// otherwise it matches TypeCons. +/// +/// Note that 'void' is interpreted as a zero-length list. +template +struct TypeCons { + typedef H head; + typedef T tail; +}; + + +/// Append a type the the end of a type list. The resulting type list +/// is available as TypeAppend::type. +/// +/// \tparam List A list of types constructed using TypeCons<>. Note +/// that 'void' is interpreted as a zero-length list. +/// +/// \tparam T The new type to be appended. +template +struct TypeAppend { + typedef TypeCons::type> type; +}; +/// Base case for empty type list. +template +struct TypeAppend { + typedef TypeCons type; +}; + + +/// Get an element from the specified list of types. The result is +/// available as TypeAt::type. +/// +/// \tparam List A list of types constructed using TypeCons<>. Note +/// that 'void' is interpreted as a zero-length list. +/// +/// \tparam i The index of the list element to get. +template +struct TypeAt { + typedef typename TypeAt::type type; +}; +/// Base case for empty type list. +template +struct TypeAt { + typedef typename List::head type; +}; + + +/// Count the number of elements in the specified list of types. The +/// result is available as TypeCount::value. +/// +/// \tparam List The list of types, constructed using TypeCons<>. Note +/// that 'void' is interpreted as a zero-length list. +template +struct TypeCount { + static const int value = 1 + TypeCount::value; +}; +/// Base case for empty type list. +template <> +struct TypeCount { + static const int value = 0; +}; + + +/// Find the first type in the specified list that satisfies the +/// specified predicate. +/// +/// \tparam List The list of types, constructed using TypeCons<>. Note +/// that 'void' is interpreted as a zero-length list. +/// +/// \tparam Pred Must be such that `Pred::%value` is true if, and +/// only if the predicate is satisfied for `T`. +template class Pred> +struct FindType { +private: + typedef typename List::head type_1; + typedef typename FindType::type type_2; + +public: + typedef typename std::conditional::value, type_1, type_2>::type type; +}; +/// Base case for empty type list. +template